From fed716ada5659409fa97567ccc33de3d88978002 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sun, 10 May 2026 20:41:21 -0400 Subject: [PATCH] Clarify compaction test harness (#26777) --- .../opencode/test/session/compaction.test.ts | 86 ++++++++----------- 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/packages/opencode/test/session/compaction.test.ts b/packages/opencode/test/session/compaction.test.ts index 982e2d8040..b48e68b10f 100644 --- a/packages/opencode/test/session/compaction.test.ts +++ b/packages/opencode/test/session/compaction.test.ts @@ -233,16 +233,22 @@ const env = Layer.mergeAll( const it = testEffect(env) -const processEnv = Layer.mergeAll(SessionNs.defaultLayer, CrossSpawnSpawner.defaultLayer) -const itProcess = testEffect(processEnv) +const compactionEnv = Layer.mergeAll(SessionNs.defaultLayer, CrossSpawnSpawner.defaultLayer) +const itCompaction = testEffect(compactionEnv) -function compactionProcessLayer(options?: { +type CompactionProcessOptions = { result?: "continue" | "compact" llm?: Layer.Layer plugin?: Layer.Layer provider?: ReturnType config?: Layer.Layer -}) { +} + +function withCompaction(options?: CompactionProcessOptions) { + return Effect.provide(compactionProcessLayer(options)) +} + +function compactionProcessLayer(options?: CompactionProcessOptions) { const bus = Bus.layer const status = SessionStatus.layer.pipe(Layer.provide(bus)) const processor = options?.llm @@ -857,7 +863,7 @@ describe("session.compaction.process", () => { }), ) - itProcess.instance( + itCompaction.instance( "marks summary message as errored on compact result", Effect.gen(function* () { const ssn = yield* SessionNs.Service @@ -882,7 +888,7 @@ describe("session.compaction.process", () => { expect(summary.info.finish).toBe("error") expect(JSON.stringify(summary.info.error)).toContain("Session too large to compact") } - }).pipe(Effect.provide(compactionProcessLayer({ result: "compact" }))), + }).pipe(withCompaction({ result: "compact" })), ) it.instance( @@ -916,7 +922,7 @@ describe("session.compaction.process", () => { }), ) - itProcess.instance( + itCompaction.instance( "persists tail_start_id for retained recent turns", Effect.gen(function* () { const ssn = yield* SessionNs.Service @@ -939,10 +945,10 @@ describe("session.compaction.process", () => { const part = yield* readCompactionPart(session.id) expect(part?.type).toBe("compaction") expect(part?.tail_start_id).toBe(keep.id) - }).pipe(Effect.provide(compactionProcessLayer({ config: cfg({ tail_turns: 2, preserve_recent_tokens: 10_000 }) }))), + }).pipe(withCompaction({ config: cfg({ tail_turns: 2, preserve_recent_tokens: 10_000 }) })), ) - itProcess.instance( + itCompaction.instance( "shrinks retained tail to fit preserve token budget", Effect.gen(function* () { const ssn = yield* SessionNs.Service @@ -965,10 +971,10 @@ describe("session.compaction.process", () => { const part = yield* readCompactionPart(session.id) expect(part?.type).toBe("compaction") expect(part?.tail_start_id).toBe(keep.id) - }).pipe(Effect.provide(compactionProcessLayer({ config: cfg({ tail_turns: 2, preserve_recent_tokens: 100 }) }))), + }).pipe(withCompaction({ config: cfg({ tail_turns: 2, preserve_recent_tokens: 100 }) })), ) - itProcess.instance( + itCompaction.instance( "falls back to full summary when even one recent turn exceeds preserve token budget", () => { const stub = llm() @@ -990,16 +996,12 @@ describe("session.compaction.process", () => { expect(part?.type).toBe("compaction") expect(part?.tail_start_id).toBeUndefined() expect(captured).toContain("yyyy") - }).pipe( - Effect.provide( - compactionProcessLayer({ llm: stub.layer, config: cfg({ tail_turns: 1, preserve_recent_tokens: 20 }) }), - ), - ) + }).pipe(withCompaction({ llm: stub.layer, config: cfg({ tail_turns: 1, preserve_recent_tokens: 20 }) })) }, { git: true }, ) - itProcess.instance( + itCompaction.instance( "falls back to full summary when retained tail media exceeds preserve token budget", () => { const stub = llm() @@ -1031,16 +1033,12 @@ describe("session.compaction.process", () => { expect(part?.tail_start_id).toBeUndefined() expect(captured).toContain("recent image turn") expect(captured).toContain("Attached image/png: big.png") - }).pipe( - Effect.provide( - compactionProcessLayer({ llm: stub.layer, config: cfg({ tail_turns: 1, preserve_recent_tokens: 100 }) }), - ), - ) + }).pipe(withCompaction({ llm: stub.layer, config: cfg({ tail_turns: 1, preserve_recent_tokens: 100 }) })) }, { git: true }, ) - itProcess.instance( + itCompaction.instance( "retains a split turn suffix when a later message fits the preserve token budget", () => { const stub = llm() @@ -1086,16 +1084,12 @@ describe("session.compaction.process", () => { expect(filtered[1]?.info.role).toBe("assistant") expect(filtered[1]?.info.role === "assistant" ? filtered[1].info.summary : false).toBe(true) expect(filtered.map((msg) => msg.info.id)).not.toContain(large.id) - }).pipe( - Effect.provide( - compactionProcessLayer({ llm: stub.layer, config: cfg({ tail_turns: 1, preserve_recent_tokens: 100 }) }), - ), - ) + }).pipe(withCompaction({ llm: stub.layer, config: cfg({ tail_turns: 1, preserve_recent_tokens: 100 }) })) }, { git: true }, ) - itProcess.instance( + itCompaction.instance( "allows plugins to disable synthetic continue prompt", Effect.gen(function* () { const ssn = yield* SessionNs.Service @@ -1124,7 +1118,7 @@ describe("session.compaction.process", () => { ), ), ).toBe(false) - }).pipe(Effect.provide(compactionProcessLayer({ plugin: autocontinue(false) }))), + }).pipe(withCompaction({ plugin: autocontinue(false) })), ) it.instance( @@ -1192,7 +1186,7 @@ describe("session.compaction.process", () => { }), ) - itProcess.instance( + itCompaction.instance( "stops quickly when aborted during retry backoff", () => { const stub = llm() @@ -1249,12 +1243,12 @@ describe("session.compaction.process", () => { expect(Cause.hasInterrupts(exit.cause)).toBe(true) expect(Date.now() - start).toBeLessThan(250) } - }).pipe(Effect.provide(compactionProcessLayer({ llm: stub.layer }))) + }).pipe(withCompaction({ llm: stub.layer })) }, { git: true }, ) - itProcess.instance( + itCompaction.instance( "does not leave a summary assistant when aborted before processor setup", () => Effect.gen(function* () { @@ -1281,12 +1275,12 @@ describe("session.compaction.process", () => { expect(Exit.isFailure(exit)).toBe(true) if (Exit.isFailure(exit)) expect(Cause.hasInterrupts(exit.cause)).toBe(true) expect(all.some((msg) => msg.info.role === "assistant" && msg.info.summary)).toBe(false) - }).pipe(Effect.provide(compactionProcessLayer({ plugin: plugin(ready) }))) + }).pipe(withCompaction({ plugin: plugin(ready) })) }), { git: true }, ) - itProcess.instance( + itCompaction.instance( "does not allow tool calls while generating the summary", () => { const stub = llm() @@ -1350,12 +1344,12 @@ describe("session.compaction.process", () => { expect(summary?.info.role).toBe("assistant") expect(summary?.parts.some((part) => part.type === "tool")).toBe(false) - }).pipe(Effect.provide(compactionProcessLayer({ llm: stub.layer }))) + }).pipe(withCompaction({ llm: stub.layer })) }, { git: true }, ) - itProcess.instance( + itCompaction.instance( "summarizes only the head while keeping recent tail out of summary input", () => { const stub = llm() @@ -1387,12 +1381,12 @@ describe("session.compaction.process", () => { expect(captured).not.toContain("keep this turn") expect(captured).not.toContain("and this one too") expect(captured).not.toContain("What did we do so far?") - }).pipe(Effect.provide(compactionProcessLayer({ llm: stub.layer }))) + }).pipe(withCompaction({ llm: stub.layer })) }, { git: true }, ) - itProcess.instance( + itCompaction.instance( "anchors repeated compactions with the previous summary", () => { const stub = llm() @@ -1429,12 +1423,12 @@ describe("session.compaction.process", () => { expect(captured.match(/summary one/g)?.length).toBe(1) expect(captured).toContain("## Constraints & Preferences") expect(captured).toContain("## Progress") - }).pipe(Effect.provide(compactionProcessLayer({ llm: stub.layer }))) + }).pipe(withCompaction({ llm: stub.layer })) }, { git: true }, ) - itProcess.instance("keeps recent pre-compaction turns across repeated compactions", () => { + itCompaction.instance("keeps recent pre-compaction turns across repeated compactions", () => { const stub = llm() stub.push(reply("summary one")) stub.push(reply("summary two")) @@ -1471,14 +1465,10 @@ describe("session.compaction.process", () => { expect( filtered.some((msg) => msg.info.role === "user" && msg.parts.some((part) => part.type === "compaction")), ).toBe(true) - }).pipe( - Effect.provide( - compactionProcessLayer({ llm: stub.layer, config: cfg({ tail_turns: 2, preserve_recent_tokens: 10_000 }) }), - ), - ) + }).pipe(withCompaction({ llm: stub.layer, config: cfg({ tail_turns: 2, preserve_recent_tokens: 10_000 }) })) }) - itProcess.instance( + itCompaction.instance( "ignores previous summaries when sizing the retained tail", Effect.gen(function* () { const ssn = yield* SessionNs.Service @@ -1519,7 +1509,7 @@ describe("session.compaction.process", () => { const part = yield* readCompactionPart(session.id) expect(part?.type).toBe("compaction") expect(part?.tail_start_id).toBe(keep.id) - }).pipe(Effect.provide(compactionProcessLayer({ config: cfg({ tail_turns: 2, preserve_recent_tokens: 500 }) }))), + }).pipe(withCompaction({ config: cfg({ tail_turns: 2, preserve_recent_tokens: 500 }) })), ) })