Skip to content

Commit a09dbd0

Browse files
author
Tehan
committed
fix(pi): exclude external m[1] delta from pressure refold (cache parity)
1 parent d87a2b3 commit a09dbd0

3 files changed

Lines changed: 165 additions & 20 deletions

File tree

packages/pi-plugin/src/inject-compartments-pi.test.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,3 +1491,90 @@ describe("mustMaterializePi — SOFT/HARD taxonomy (parity with OpenCode)", () =
14911491
}
14921492
});
14931493
});
1494+
1495+
describe("Pi external m[1] delta pressure-refold exclusion (cache parity)", () => {
1496+
// RED-GREEN regression for Finding #1: a large external-recall delta in m[1]
1497+
// must NEVER trigger the pressure-refold backstop. The fix subtracts
1498+
// externalDeltaTokens + wrapper overhead from the pressure comparison so
1499+
// late recall "must NEVER cause a fold" (parity with OpenCode injectM0M1).
1500+
//
1501+
// Setup: materialize m[0] with a small compartment (m[0] tokens ≈ small).
1502+
// Then seed a large external recall snapshot whose token count exceeds
1503+
// 15% of m[0] tokens. On a cache-busting pass the pressure math must
1504+
// exclude the external delta and NOT call materializeM0PiWithRetry.
1505+
//
1506+
// RED (without fix): m1PressureTokens = m1Tokens (no subtraction) →
1507+
// large delta crosses the 15% ratio → materializeM0PiWithRetry called →
1508+
// m[0] bytes change → test FAILS.
1509+
// GREEN (with fix): m1PressureTokens = m1Tokens - externalDeltaTokens -
1510+
// wrapper → ratio not crossed → no refold → m[0] bytes unchanged → PASSES.
1511+
1512+
it("large external m[1] delta does NOT trigger pressure refold (m[0] bytes stable)", () => {
1513+
const db = createTestDb();
1514+
const cwd = mkdtempSync(join(tmpdir(), "pi-ext-pressure-"));
1515+
try {
1516+
const state = piState("ses-pi-ext-pressure", cwd);
1517+
1518+
// Materialize m[0] with a compartment large enough to clear the
1519+
// M0_DRIFT_RATIO_FLOOR_TOKENS=500 gate (so the ratio test can fire).
1520+
// ~600 tokens of body content ensures m[0] > 500 tokens.
1521+
const m0Body = "word ".repeat(600); // ~600 tokens
1522+
appendCompartments(db, state.sessionId, [
1523+
{
1524+
sequence: 0,
1525+
startMessage: 1,
1526+
endMessage: 1,
1527+
startMessageId: "entry-0",
1528+
endMessageId: "entry-0",
1529+
title: "Large",
1530+
content: `U: large turn\n${m0Body}`,
1531+
p1: `U: large turn\n${m0Body}`,
1532+
},
1533+
]);
1534+
const firstPass = [userMessage("hello", 10)];
1535+
const r0 = injectM0M1Pi(state, db, firstPass as never, ["entry-0"], true);
1536+
expect(r0.m0Materialized).toBe(true);
1537+
const baselineM0 = textOf(firstPass[0] as never);
1538+
const baselineM0Bytes = baselineM0.length;
1539+
1540+
// Seed a large external recall snapshot AFTER m[0] was materialized.
1541+
// The snapshot hash differs from the m[0] baseline hash (which is "")
1542+
// so the delta will be rendered into m[1]. Make it large enough to
1543+
// exceed 15% of m[0] tokens (m[0] is small, so even a moderate delta
1544+
// crosses the ratio without the fix).
1545+
// ~200 tokens of content — well above 15% of m[0] (~600 tokens).
1546+
// Without the fix, this delta alone would cross the ratio and trigger
1547+
// a refold. With the fix, it is subtracted from m1PressureTokens.
1548+
const largeContent = "word ".repeat(200); // ~200 tokens
1549+
db.prepare(
1550+
"UPDATE session_meta SET external_recall_state = ?, external_recall_json = ?, external_recall_at = ? WHERE session_id = ?",
1551+
).run(
1552+
"done",
1553+
JSON.stringify({
1554+
project: [{ content: largeContent }],
1555+
profile: [],
1556+
global: [],
1557+
}),
1558+
Date.now(),
1559+
state.sessionId,
1560+
);
1561+
1562+
// Cache-busting pass: recomputeM1ThisPass=true. The external delta
1563+
// will be rendered into m[1]. The pressure backstop must NOT fire.
1564+
const secondPass = [userMessage("hello", 11)];
1565+
const r1 = injectM0M1Pi(state, db, secondPass as never, ["entry-0"], true);
1566+
1567+
// (a) m[0] NOT re-materialized — external delta must not trigger refold.
1568+
expect(r1.m0Materialized).toBe(false);
1569+
// (b) m[0] bytes byte-identical to the baseline (cache-stable).
1570+
const m0After = textOf(secondPass[0] as never);
1571+
expect(m0After.length).toBe(baselineM0Bytes);
1572+
expect(m0After).toBe(baselineM0);
1573+
// (c) m[1] contains the external delta.
1574+
expect(textOf(secondPass[1] as never)).toContain(largeContent);
1575+
} finally {
1576+
rmSync(cwd, { recursive: true, force: true });
1577+
closeQuietly(db);
1578+
}
1579+
});
1580+
});

packages/pi-plugin/src/inject-compartments-pi.ts

Lines changed: 77 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ import {
7878
type PreparedCompartmentInjection,
7979
prepareCompartmentInjection,
8080
renderExternalMemoryBlock,
81+
renderExternalMemoryDelta,
8182
renderMemoryBlockV2,
8283
trimMemoriesToBudgetV2,
8384
trimUserMemoriesToBudget,
@@ -1645,6 +1646,14 @@ function renderMemoryUpdatesBlockPi(args: {
16451646
interface RenderM1PiResult {
16461647
text: string;
16471648
memoryUpdateCount: number;
1649+
/** The <external-memory> delta block (late-arrival snapshot) when present,
1650+
* "" otherwise. Excluded from the injectM0M1Pi pressure-refold token math
1651+
* so a large recall can NEVER cause an m[0] refold (parity with OpenCode). */
1652+
externalDeltaText: string;
1653+
/** True when freshly rendered from current DB state. False when replayed
1654+
* from a sibling-adoption row. The pressure-refold backstop must only fire
1655+
* on recomputed bytes (parity with OpenCode RenderM1Result.recomputed). */
1656+
recomputed: boolean;
16481657
}
16491658

16501659
function renderM1PiWithMetadata(
@@ -1767,28 +1776,42 @@ function renderM1PiWithMetadata(
17671776

17681777
// External memory delta: when the live recall hash differs from the m[0]
17691778
// baseline hash, surface the current recall snapshot as a delta. Mirrors
1770-
// OpenCode renderM1's renderExternalMemoryDelta path. The delta carries ALL
1771-
// slices (profile lines reconcile into <user-profile> at the next HARD fold).
1772-
const liveExternalRecallHash = readExternalRecallHash(db, state.sessionId);
1773-
if (liveExternalRecallHash !== markers.externalRecallHash) {
1774-
const recallRead = readExternalRecallSnapshot(db, state.sessionId);
1775-
if (recallRead.state === "done" && recallRead.snapshot) {
1776-
const externalBlock = renderExternalMemoryBlock(recallRead.snapshot);
1777-
if (externalBlock) sections.push(externalBlock);
1779+
// OpenCode renderM1WithMetadata's renderExternalMemoryDelta path. The delta
1780+
// carries ALL slices including profile (profile lines reconcile into
1781+
// <user-profile> at the next HARD fold). Captured separately so the caller
1782+
// can subtract its tokens from the pressure-refold math — recall is NOT a
1783+
// bust trigger (parity with OpenCode RenderM1Result.externalDeltaText).
1784+
let externalDeltaText = "";
1785+
const recallRead = readExternalRecallSnapshot(db, state.sessionId);
1786+
if (recallRead.state === "done" && recallRead.snapshot) {
1787+
const currentRecallHash = computeRecallSnapshotHash(recallRead.snapshot);
1788+
if (
1789+
currentRecallHash !== "" &&
1790+
currentRecallHash !== markers.externalRecallHash
1791+
) {
1792+
const delta = renderExternalMemoryDelta(recallRead.snapshot);
1793+
if (delta) {
1794+
externalDeltaText = delta;
1795+
sections.push(delta);
1796+
}
17781797
}
17791798
}
17801799

17811800
if (sections.length === 0) {
17821801
return {
17831802
text: PI_M1_PLACEHOLDER,
17841803
memoryUpdateCount: memoryUpdates.count,
1804+
externalDeltaText: "",
1805+
recomputed: true,
17851806
};
17861807
}
17871808
// Join with "\n" (single newline) to match OpenCode renderM1 exactly — the
17881809
// m[1] delta bytes must be identical across harnesses.
17891810
return {
17901811
text: `<session-history-since>\n${sections.join("\n")}\n</session-history-since>`,
17911812
memoryUpdateCount: memoryUpdates.count,
1813+
externalDeltaText,
1814+
recomputed: true,
17921815
};
17931816
}
17941817

@@ -2015,6 +2038,7 @@ function softRefreshCachedM1Pi(args: {
20152038
markers: PiM0SnapshotMarkers;
20162039
memoryUpdateCount: number;
20172040
recomputed: boolean;
2041+
externalDeltaText: string;
20182042
} {
20192043
const preRenderedKeyFilesBlock = preRenderKeyFilesBlockPi(
20202044
args.state,
@@ -2043,15 +2067,20 @@ function softRefreshCachedM1Pi(args: {
20432067
args.db,
20442068
args.state.sessionId,
20452069
);
2046-
return {
2047-
...applyCachedPiRow({
2048-
row: sibling,
2049-
state: args.state,
2050-
compartmentsForNormalization: siblingCompartments,
2051-
}),
2052-
memoryUpdateCount: 0,
2053-
recomputed: false,
2054-
};
2070+
return {
2071+
...applyCachedPiRow({
2072+
row: sibling,
2073+
state: args.state,
2074+
compartmentsForNormalization: siblingCompartments,
2075+
}),
2076+
memoryUpdateCount: 0,
2077+
recomputed: false,
2078+
// Sibling-adoption replay: the bytes are persisted, not freshly
2079+
// rendered. The external delta is unknown from the persisted row;
2080+
// use "" so the pressure backstop (which only fires on recomputed
2081+
// bytes) is never triggered by a replayed sibling m[1].
2082+
externalDeltaText: "",
2083+
};
20552084
}
20562085

20572086
const markers = markersFromCachedPiRow(
@@ -2103,6 +2132,7 @@ function softRefreshCachedM1Pi(args: {
21032132
markers: { ...markers, lastBaselineEndMessageId: advancedBoundary },
21042133
memoryUpdateCount: rendered.memoryUpdateCount,
21052134
recomputed: true,
2135+
externalDeltaText: rendered.externalDeltaText,
21062136
};
21072137
} catch (error) {
21082138
try {
@@ -2164,6 +2194,10 @@ export function injectM0M1Pi(
21642194
let memoryUpdateCount = 0;
21652195
let m1Recomputed = false;
21662196
let freshFallbackRenderedMemoryIds: number[] | null = null;
2197+
// Tracks the external-recall delta text from the freshly rendered m[1] so
2198+
// the pressure backstop can subtract its tokens — recall must NEVER cause a
2199+
// fold (parity with OpenCode injectM0M1 externalDeltaText subtraction).
2200+
let m1ExternalDeltaText = "";
21672201

21682202
if (decision.value) {
21692203
// On contention exhaustion, reuse the cached m[0]/m[1] pair rather than
@@ -2259,6 +2293,7 @@ export function injectM0M1Pi(
22592293
m1 = freshM1.text;
22602294
memoryUpdateCount = freshM1.memoryUpdateCount;
22612295
m1Recomputed = true;
2296+
m1ExternalDeltaText = freshM1.externalDeltaText;
22622297
} else if (contentionExhausted) {
22632298
// m[1] was replayed with the cached m[0] pair above.
22642299
} else if (recomputeM1ThisPass) {
@@ -2274,6 +2309,7 @@ export function injectM0M1Pi(
22742309
markers = refreshed.markers;
22752310
memoryUpdateCount = refreshed.memoryUpdateCount;
22762311
m1Recomputed = refreshed.recomputed;
2312+
m1ExternalDeltaText = refreshed.externalDeltaText;
22772313
} else {
22782314
const replayed = replayCachedM1Pi(db, state, currentCompartments);
22792315
m0 = replayed.m0;
@@ -2294,6 +2330,15 @@ export function injectM0M1Pi(
22942330
// Token counts (NOT char lengths) on both sides of the ratio — parity with
22952331
// OpenCode. The documented intent is "m[1] exceeds ~15% of m[0] tokens";
22962332
// char length diverges from token count on XML-heavy / non-Latin content.
2333+
//
2334+
// External recall content must NEVER CAUSE a fold (spec: not a bust trigger);
2335+
// it rides along when a fold fires for other reasons. Two layers of
2336+
// subtraction from m1Tokens: the delta itself (late recall) AND a small
2337+
// wrapper overhead (every m[1] carries the wrapper, empty or not — not a
2338+
// drift signal). The wrapper tokens are also subtracted from the absolute
2339+
// cap budget for symmetry, so a tiny m[0] baseline (where the wrapper
2340+
// alone would exceed the cap) does not falsely fire a refold when the
2341+
// only m[1] content is the recall delta. (Parity with OpenCode injectM0M1.)
22972342
const M0_DRIFT_RATIO_FLOOR_TOKENS = 500;
22982343
const M1_DRIFT_RATIO = 0.15;
22992344
const M1_ABSOLUTE_CAP_RATIO = 0.2;
@@ -2302,8 +2347,21 @@ export function injectM0M1Pi(
23022347
M1_ABSOLUTE_CAP_RATIO;
23032348
const m1HasContent = m1 !== PI_M1_PLACEHOLDER;
23042349
const m1Tokens = m1HasContent ? estimateTokens(m1) : 0;
2350+
const M1_PRESSURE_WRAPPER_TOKENS = 20;
2351+
const externalDeltaTokens = m1ExternalDeltaText
2352+
? estimateTokens(m1ExternalDeltaText)
2353+
: 0;
2354+
const m1PressureTokens = Math.max(
2355+
0,
2356+
m1Tokens - externalDeltaTokens - M1_PRESSURE_WRAPPER_TOKENS,
2357+
);
2358+
const m1AbsoluteContentBudget = Math.max(
2359+
0,
2360+
m1AbsoluteBudget - M1_PRESSURE_WRAPPER_TOKENS,
2361+
);
23052362
const m0Tokens = estimateTokens(m0);
2306-
const m1OverAbsoluteCap = m1HasContent && m1Tokens > m1AbsoluteBudget;
2363+
const m1OverAbsoluteCap =
2364+
m1HasContent && m1PressureTokens > m1AbsoluteContentBudget;
23072365
if (
23082366
!materialized &&
23092367
!contentionExhausted &&
@@ -2313,7 +2371,7 @@ export function injectM0M1Pi(
23132371
m1OverAbsoluteCap ||
23142372
(m1HasContent &&
23152373
m0Tokens >= M0_DRIFT_RATIO_FLOOR_TOKENS &&
2316-
m1Tokens > m0Tokens * M1_DRIFT_RATIO))
2374+
m1PressureTokens > m0Tokens * M1_DRIFT_RATIO))
23172375
) {
23182376
decision = { value: true, reason: "drift" };
23192377
try {

packages/plugin/src/hooks/magic-context/inject-compartments.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1460,7 +1460,7 @@ export function renderExternalMemoryBlock(snapshot: ExternalRecallSnapshot): str
14601460

14611461
/** m[1] delta when recall settles after the last m[0] fold — carries ALL
14621462
* slices (profile lines reconcile into <user-profile> at the next HARD fold). */
1463-
function renderExternalMemoryDelta(snapshot: ExternalRecallSnapshot): string {
1463+
export function renderExternalMemoryDelta(snapshot: ExternalRecallSnapshot): string {
14641464
const body = renderExternalDeltaLines(snapshot);
14651465
if (body.length === 0) return "";
14661466
return [

0 commit comments

Comments
 (0)