Clarify compaction test harness (#26777)

This commit is contained in:
Kit Langton
2026-05-10 20:41:21 -04:00
committed by GitHub
parent 426d92e352
commit fed716ada5

View File

@@ -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<LLM.Service>
plugin?: Layer.Layer<Plugin.Service>
provider?: ReturnType<typeof ProviderTest.fake>
config?: Layer.Layer<Config.Service>
}) {
}
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 }) })),
)
})