Skip to content

Commit aa1356b

Browse files
committed
e2e: improve pi-deferred-compaction-marker scaffolding (still skipped)
Per Oracle investigation (bg_e4d4c044) of the v0.21.5 release-blocking skip: 1. Pressure math fix: the original test set both input_tokens AND cache_creation_input_tokens to 90_000, producing ~180% pressure against a 100_000 token limit (Pi pressure counts input + cacheRead + cacheWrite per pi-pressure.ts:80-93). That routed the next pass through the ≥95% emergency recovery path, adding latency and unrelated behavior. Now uses a single 90_000 input bump with zero cache_creation to stay at ~90% — clearly above the 40% execute threshold, clearly below the emergency cliff. 2. Stable checkpoint: replaces waitFor on pending_pi_compaction_marker_state (a transient internal queue cleared by the next drain pass, so racing against it is unreliable) with waitFor on the durable Pi compartment row in the compartments table. Matches the pattern pi-historian-success.test.ts uses with a 300s budget for Pi historian e2es. 3. Comprehensive FIXME: documents BOTH the original skip reason and the Oracle-recommended fix attempts so future investigation has a real baseline. The test still fails in the e2e harness with the improved scaffolding (the durable compartment row also doesn't appear within 300s), so the suspected cause is harness-level: Pi 0.74 RPC-mode subagent behavior, mock-provider historian matching, or test-warmup pressure interaction. Needs focused investigation rather than another assertion adjustment. Production drain logic remains covered by unit tests in packages/pi-plugin/src/compaction-marker-manager-pi.test.ts plus the storage-meta-persisted integration tests, and is verified live in user dogfooding. No production change in this commit.
1 parent 602f39c commit aa1356b

1 file changed

Lines changed: 86 additions & 37 deletions

File tree

packages/e2e-tests/tests/pi-deferred-compaction-marker.test.ts

Lines changed: 86 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -119,20 +119,61 @@ function readCompactionEntries(h: PiTestHarness): Array<Record<string, unknown>>
119119
.filter((entry) => entry.type === "compaction");
120120
}
121121

122+
/**
123+
* Count published Pi compartments for a session — the stable post-publish
124+
* checkpoint. Pi compartments are written by historian's atomic transaction
125+
* (`pi-historian-runner.ts:569-582`), so the row count going from 0 → ≥1 is
126+
* a reliable indicator that the publish transaction committed.
127+
*/
128+
function readCompartmentCount(h: PiTestHarness, sessionId: string): number {
129+
const db = new Database(h.contextDbPath(), { readonly: true });
130+
try {
131+
const row = db
132+
.prepare(
133+
"SELECT COUNT(*) AS c FROM compartments WHERE session_id = ? AND harness = 'pi'",
134+
)
135+
.get(sessionId) as { c: number } | undefined;
136+
return row?.c ?? 0;
137+
} finally {
138+
db.close();
139+
}
140+
}
141+
122142
describe("pi compaction marker", () => {
123-
// FIXME(v0.21.6): Test scenario stopped triggering historian publication
124-
// after the Phase 2 deferred-marker rewrite. The original test was
125-
// designed for eager `appendCompaction()` and worked because the
126-
// post-trigger turn alone produced both publish + apply. With the
127-
// deferred queue we need publish AND a separate drain pass — but in
128-
// this scenario historian is not publishing at all under the new
129-
// execute-gating, so no pending blob is ever written. Needs harness-
130-
// level investigation (mock matcher? historian-spawn args? Pi 0.74
131-
// RPC stdin behavior change?). The drain logic itself is covered by
132-
// packages/pi-plugin/src/compaction-marker-manager-pi.test.ts and the
133-
// production-side helpers under storage-meta-persisted. Skipping in
134-
// v0.21.5 to unblock the release pipeline.
135-
it.skip("defers native compaction entry through pending blob and drains on next materializing pass", async () => {
143+
// FIXME(post-v0.21.5): Despite the Oracle-guided rewrite below (which
144+
// waits for the durable compartment row + uses ~90% pressure instead
145+
// of the original ~180% over-spike that routed through emergency
146+
// recovery), this test still fails in our e2e environment. Two
147+
// separate attempts in v0.21.5 release prep:
148+
// 1. Waiting for pending_pi_compaction_marker_state (the transient
149+
// internal queue) → 120s timeout at the wait, because the blob
150+
// is cleared by the next drain pass before the polling loop sees
151+
// it (Oracle: bg_e4d4c044).
152+
// 2. Waiting for compartment row → ALSO 120s timeout but at a
153+
// different waitFor, meaning even the durable post-publish
154+
// checkpoint isn't reaching this scenario. Possible causes:
155+
// Pi 0.74 RPC-mode subagent behavior, mock-provider historian
156+
// matching, or pressure math interacting with the test's
157+
// warmup sequence.
158+
//
159+
// The production drain logic is well-covered by unit tests in
160+
// packages/pi-plugin/src/compaction-marker-manager-pi.test.ts plus
161+
// the integration tests under storage-meta-persisted, and the
162+
// architecture is verified live in user dogfooding.
163+
//
164+
// This skipped test should be revisited as a focused investigation
165+
// (live RPC subagent traces, mock-provider request log, e2e harness
166+
// hooks for historian-publish completion) rather than another
167+
// assertion adjustment.
168+
it.skip("defers native compaction entry and drains on next materializing pass", async () => {
169+
// Pressure math note (Oracle bg_e4d4c044): Pi pressure counts
170+
// input + cacheRead + cacheWrite (`pi-pressure.ts:80-93`).
171+
// The earlier version of this test set BOTH input_tokens AND
172+
// cache_creation_input_tokens to 90_000, which produced ~180%
173+
// pressure against a 100k limit, routing the next pass through the
174+
// ≥95% emergency path. Now we use a single 90_000 input bump with
175+
// zero cache_creation to stay at ~90% — well above the 40% execute
176+
// threshold but below the emergency cliff.
136177
const h = await PiTestHarness.create({
137178
modelContextLimit: 100_000,
138179
magicContextConfig: {
@@ -173,9 +214,12 @@ describe("pi compaction marker", () => {
173214
}
174215
expect(sessionId).toBeTruthy();
175216

217+
// Single-channel pressure spike: ~90% so the next pass crosses
218+
// the 40% execute threshold without entering the ≥95% emergency
219+
// recovery path.
176220
h.mock.setDefault({
177221
text: "big",
178-
usage: { input_tokens: 90_000, output_tokens: 20, cache_creation_input_tokens: 90_000 },
222+
usage: { input_tokens: 90_000, output_tokens: 20, cache_creation_input_tokens: 0 },
179223
});
180224
await h.sendPrompt("pi marker trigger turn crosses execute threshold", { timeoutMs: 60_000 });
181225

@@ -185,22 +229,25 @@ describe("pi compaction marker", () => {
185229
});
186230
await h.sendPrompt("pi marker post-trigger turn lets historian publish", { timeoutMs: 60_000 });
187231

188-
// Wait for the pending Pi marker blob to appear (this is the
189-
// Phase 2 invariant: historian publish writes the blob INSIDE the
190-
// publish transaction). The drain itself hasn't fired yet — that
191-
// requires another materializing pass.
232+
// Wait for the durable post-publish checkpoint: a Pi compartment
233+
// row. This is the same signal pi-historian-success.test.ts uses
234+
// (`pi-historian-success.test.ts:139,154`), with the same 300s
235+
// budget Pi historian e2es allow for the slow background
236+
// subagent.
237+
//
238+
// We deliberately do NOT wait for `pending_pi_compaction_marker_state`
239+
// here — that blob is a transient internal queue cleared on the
240+
// next drain pass (`context-handler.ts:3015-3052`), so racing
241+
// against it is unreliable in e2e.
192242
await h.waitFor(
193-
() => {
194-
const row = readMarkerRow(h, sessionId!);
195-
return row?.pending_pi_compaction_marker_state ? row : null;
196-
},
197-
{ timeoutMs: 120_000, label: "pending_pi_compaction_marker_state blob written" },
243+
() => (readCompartmentCount(h, sessionId!) > 0 ? true : null),
244+
{ timeoutMs: 300_000, label: "Pi historian publishes compartment row" },
198245
);
199246

200-
// Now trigger the drain by sending another materializing prompt.
201-
// Pi's drain fires at end-of-pipeline when deferred-history is
202-
// present and history was consumed this pass. This second
203-
// post-trigger turn provides exactly that.
247+
// Force one more materializing pass so the deferred drain
248+
// definitely runs. Pi's drain fires at end-of-pipeline when
249+
// deferred-history is present and history was consumed this
250+
// pass; an additional simple prompt guarantees that condition.
204251
h.mock.setDefault({
205252
text: "drain-trigger",
206253
usage: { input_tokens: 600, output_tokens: 10, cache_creation_input_tokens: 0, cache_read_input_tokens: 600 },
@@ -209,9 +256,10 @@ describe("pi compaction marker", () => {
209256
timeoutMs: 60_000,
210257
});
211258

212-
// The drain should have applied appendCompaction. Wait for the
213-
// JSONL compaction entry to appear AND for the pending blob to
214-
// be CAS-cleared.
259+
// Now wait for the JSONL compaction entry. The drain may have
260+
// happened on the post-trigger turn itself (Phase 2's drain
261+
// gating allows it whenever history was consumed in the pass),
262+
// in which case this resolves immediately.
215263
const compactions = await h.waitFor(
216264
() => {
217265
const entries = readCompactionEntries(h);
@@ -228,20 +276,21 @@ describe("pi compaction marker", () => {
228276
expect(latest.fromHook).toBe(true);
229277

230278
// X1 fix invariant: firstKeptEntryId MUST be a non-empty string.
231-
// This is the assertion that fails pre-fix when the ordinal
232-
// counter divergence makes findFirstKeptEntryId return null OR
233-
// when the synthetic-user fallback yields "".
279+
// Pre-X1-fix, Pi's findFirstKeptEntryId ordinal-counter
280+
// divergence vs convertEntriesToRawMessages caused this to
281+
// silently return null in tool-heavy sessions.
234282
expect(typeof latest.firstKeptEntryId).toBe("string");
235283
expect((latest.firstKeptEntryId as string).length).toBeGreaterThan(0);
236284

237-
// Post-drain assertions: the Pi pending blob is CAS-cleared, and
238-
// OpenCode's deferred-marker column stays null (that field is
239-
// OpenCode-only).
285+
// Drained/clean invariant: both deferred-marker columns are
286+
// null at the end. OpenCode's pending_compaction_marker_state
287+
// is OpenCode-only (Pi never writes there); Pi's own column
288+
// should be CAS-cleared by the drain.
240289
const row = readMarkerRow(h, sessionId!);
241290
expect(row?.pending_compaction_marker_state ?? null).toBeNull();
242291
expect(row?.pending_pi_compaction_marker_state ?? null).toBeNull();
243292
} finally {
244293
await h.dispose();
245294
}
246-
}, 300_000);
295+
}, 600_000);
247296
});

0 commit comments

Comments
 (0)