fix: harden resume after compaction failures (#195)

* fix: harden resume after compaction failures

* test: cover resume compaction safeguards

* fix: address resume safeguard review findings
This commit is contained in:
sooth
2026-04-03 10:31:06 -04:00
committed by GitHub
parent 6987a54a71
commit b0d796e5c3
8 changed files with 499 additions and 27 deletions

View File

@@ -0,0 +1,79 @@
import { afterEach, expect, test } from 'bun:test'
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import {
loadConversationForResume,
ResumeTranscriptTooLargeError,
} from './conversationRecovery.ts'
const tempDirs: string[] = []
const originalSimple = process.env.CLAUDE_CODE_SIMPLE
const sessionId = '00000000-0000-4000-8000-000000001999'
const ts = '2026-04-02T00:00:00.000Z'
function id(n: number): string {
return `00000000-0000-4000-8000-${String(n).padStart(12, '0')}`
}
function user(uuid: string, content: string) {
return {
type: 'user',
uuid,
parentUuid: null,
timestamp: ts,
cwd: '/tmp',
userType: 'external',
sessionId,
version: 'test',
isSidechain: false,
isMeta: false,
message: {
role: 'user',
content,
},
}
}
async function writeJsonl(entry: unknown): Promise<string> {
const dir = await mkdtemp(join(tmpdir(), 'openclaude-conversation-recovery-'))
tempDirs.push(dir)
const filePath = join(dir, 'resume.jsonl')
await writeFile(filePath, `${JSON.stringify(entry)}\n`)
return filePath
}
afterEach(async () => {
process.env.CLAUDE_CODE_SIMPLE = originalSimple
await Promise.all(tempDirs.splice(0).map(dir => rm(dir, { recursive: true, force: true })))
})
test('loadConversationForResume accepts a small transcript from jsonl path', async () => {
process.env.CLAUDE_CODE_SIMPLE = '1'
const path = await writeJsonl(user(id(1), 'hello'))
const result = await loadConversationForResume('fixture', path)
expect(result).not.toBeNull()
expect(result?.sessionId).toBe(sessionId)
expect(result?.messages.length).toBeGreaterThan(0)
})
test('loadConversationForResume rejects oversized reconstructed transcripts', async () => {
process.env.CLAUDE_CODE_SIMPLE = '1'
const hugeContent = 'x'.repeat(8 * 1024 * 1024 + 32 * 1024)
const path = await writeJsonl(user(id(2), hugeContent))
let caught: unknown
try {
await loadConversationForResume('fixture', path)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(ResumeTranscriptTooLargeError)
expect((caught as Error).message).toContain(
'Reconstructed transcript is too large to resume safely',
)
})