test(snapshot): derive compaction replay from logs

This commit is contained in:
Tianyi Cui
2026-08-08 01:32:27 +08:00
parent a29a0a4773
commit e46b082fee
16 changed files with 317 additions and 26 deletions
@@ -29,6 +29,10 @@ const goalScenarioDir = join(snapshotsDir, 'goal-tools')
const goalConfigPath = fileURLToPath(new URL('../goal.cordis.snapshot.yml', import.meta.url))
const retryScenarioDir = join(snapshotsDir, 'provider-retry')
const retryConfigPath = fileURLToPath(new URL('../retry.cordis.snapshot.yml', import.meta.url))
const compactionScenarioDir = join(snapshotsDir, 'compaction-recovery')
const compactionSessionFixture = join(compactionScenarioDir, 'session.jsonl')
const compactionStreamExpected = join(compactionScenarioDir, 'stream-json.expected.jsonl')
const compactionConfigPath = fileURLToPath(new URL('../compaction.cordis.snapshot.yml', import.meta.url))
const credentialsScenarioDir = join(snapshotsDir, 'missing-credential')
const credentialsConfigPath = fileURLToPath(new URL('../credentials.cordis.snapshot.yml', import.meta.url))
// Same keyless composition as the missing-credential scenario: the endpoint is
@@ -227,6 +231,75 @@ describe('headless stream-json snapshots', () => {
expect(normalized).toBe(await readFile(streamExpected, 'utf8'))
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('recovers from context overflow through an assembled compaction', async () => {
const prompt = await scenarioPrompt(compactionScenarioDir, 'compaction-recovery')
let expectedSession = await readFile(compactionSessionFixture, 'utf8')
let runCwd = ''
const result = await runLoaderSmoke({
label: 'compaction recovery headless stream-json snapshot',
tempDirPrefix: 'headless-snapshot-compaction-recovery-',
binScript,
configPath: compactionConfigPath,
binArgs: ['--config', compactionConfigPath, '--output-format', 'stream-json', prompt],
tsconfigPath,
env: {
DSH_SNAPSHOT: 'replay',
DSH_SNAPSHOT_FILE: compactionSessionFixture,
NODE_OPTIONS: [process.env.NODE_OPTIONS, '--disable-warning=ExperimentalWarning'].filter(Boolean).join(' '),
},
prepare: (cwd) => { runCwd = cwd },
inspect: async (cwd) => {
const logs = await persistedLogs(cwd)
expect(logs).toHaveLength(1)
const actual = logs[0]
if (actual === undefined) throw new Error('compaction snapshot did not persist its session')
const records = parseJsonl(actual.content)
const types = records.map(record => record.type)
expect(types.filter(type => type === 'compact/start')).toHaveLength(1)
expect(types.filter(type => type === 'compact/summary')).toHaveLength(1)
expect(types.filter(type => type === 'compact/end')).toHaveLength(1)
const start = types.indexOf('compact/start')
const summary = types.indexOf('compact/summary')
const replacement = records.findIndex((record) => {
if (record.type !== 'user/message') return false
const surfaceOp = record.surfaceOp as JsonObject | undefined
return surfaceOp?.op === 'replace'
})
const end = types.indexOf('compact/end')
expect(start).toBeLessThan(summary)
expect(summary).toBeLessThan(replacement)
expect(replacement).toBeLessThan(end)
const summaryRecord = records[summary]
const summaryData = summaryRecord?.data as JsonObject | undefined
expect(summaryData?.shadowedSeqs).toEqual(expect.arrayContaining([expect.any(Number)]))
const final = [...records].reverse().find(record => record.type === 'assistant/message')
expect(JSON.stringify(final)).toContain('COMPACTION RECOVERED')
const actualContext = contextFromLogs([actual.content])
if (refreshing) {
const harvested: HarvestedLog = {
id: String(actual.header.id),
createdAt: Number(actual.header.createdAt),
content: actual.content,
}
const replacements = refreshFixtureReplacements([harvested], [expectedSession])
expectedSession = tokenizeSessionFixtureCwd(
stabilizeRefreshLog(actual.content, expectedSession, replacements, actualContext),
)
await writeFile(compactionSessionFixture, expectedSession)
}
const expectedContext = contextFromLogs([expectedSession])
expect(scrubRequestHeaders(normalizeSessionLog(actual.content, actualContext)))
.toBe(scrubRequestHeaders(normalizeSessionLog(expectedSession, expectedContext)))
},
})
expect(result.stderr).toBe('')
const normalized = normalizeHeadlessStream(result.stdout, runCwd)
if (refreshing) await writeFile(compactionStreamExpected, normalized)
expect(normalized).toBe(await readFile(compactionStreamExpected, 'utf8'))
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('logs actionable missing-credential guidance through the one-shot app', async () => {
const streamExpected = join(credentialsScenarioDir, 'stream-json.expected.jsonl')
let runCwd = ''