fix(scripts): validate historical fixture layout with source codecs

This commit is contained in:
Tianyi Cui
2026-09-06 21:18:49 +08:00
parent 2890cf3a18
commit a4dcff874e
2 changed files with 49 additions and 57 deletions
+30
View File
@@ -106,6 +106,36 @@ describe('canonicalSessionFixture', () => {
expect(canonicalSessionFixture(projected)).toBe(projected)
})
it.each([0, 1, 2])('preserves physically valid v%i bytes without requiring migration to current', (version) => {
const header = { type: 'session', version, id: 'historical', createdAt: 1, delegationDepth: 0, ...(version === 2 ? { isSeeded: false } : {}) }
const content = [
JSON.stringify(header),
JSON.stringify({ type: 'user/message', data: { role: 'user', id: 'historical-user', source: { kind: 'user' }, content: [] }, surfaceOp: 'append' }),
'',
].join('\n')
expect(canonicalSessionFixture(content)).toBe(content)
})
it.each([0, 1, 2])('rejects v%i sequence gaps and invalid provenance ranges with source line diagnostics', (version) => {
const header = JSON.stringify({ type: 'session', version, id: 'historical', createdAt: 1, delegationDepth: 0, ...(version === 2 ? { isSeeded: false } : {}) })
expect(() => canonicalSessionFixture(`${header}\n{"type":"feedback/record","seq":3,"data":{"text":"gap"}}\n`, 'gap.jsonl'))
.toThrow(/gap\.jsonl: session snapshot line 2:.*seq/)
expect(() => canonicalSessionFixture(`${header}\n{"type":"feedback/record","data":{},"sourceEventSeqs":[[2,0]]}\n`, 'range.jsonl'))
.toThrow(/range\.jsonl: session snapshot line 2:/)
})
it.each([0, 1, 2])('finalizes the v%i source inherited cut', (version) => {
const header = { type: 'session', version, id: 'historical', createdAt: 1, delegationDepth: 0, ...(version === 2 ? { isSeeded: true } : { seedLength: 1 }) }
expect(() => canonicalSessionFixture(`${JSON.stringify(header)}\n`, 'cut.jsonl'))
.toThrow(/cut\.jsonl: session snapshot line 1:.*(?:inherited|seed)/)
})
it('refuses unsupported generation headers', () => {
const header = { type: 'session', version: 99, id: 'future', createdAt: 1, isSeeded: false, delegationDepth: 0 }
expect(() => canonicalSessionFixture(`${JSON.stringify(header)}\n`, 'future.jsonl'))
.toThrow(/future\.jsonl: session snapshot line 1:.*99/)
})
it('fails loud on malformed records after a session header', () => {
expect(() => canonicalSessionFixture(`${HEADER}\n{not-json}\n`, 'broken.jsonl'))
.toThrow(/broken\.jsonl: session snapshot line 2 contains invalid JSON/)
+19 -57
View File
@@ -11,6 +11,13 @@ import {
} from '@deepseek-ai/dsh-session'
import type { SessionLogOffset as SessionLogOffsetType } from '@deepseek-ai/dsh-session'
import { sessionFormatCatalog } from '@deepseek-ai/dsh-session-format-catalog'
import { SessionFormatEventCollector, type SessionFormatArtifactDecoder, type SessionFormatCodec } from '@deepseek-ai/dsh-session-format'
import { releasedV0SessionFormatCodec, releasedV1SessionFormatCodec } from '@deepseek-ai/dsh-session-format-v0-to-v1'
import { releasedV2SessionFormatCodec } from '@deepseek-ai/dsh-session-format-v1-to-v2'
const historicalCodecs: readonly SessionFormatCodec[] = [
releasedV0SessionFormatCodec, releasedV1SessionFormatCodec, releasedV2SessionFormatCodec,
]
/** Physical persistence artifacts validated by the WebWorker runtime fixture spec. */
const WEBWORKER_PHYSICAL_SESSION_FIXTURE_ROOT =
@@ -58,51 +65,6 @@ function validationHeader(value: unknown): unknown {
return header
}
function validationRow(source: Readonly<Record<string, unknown>>): Record<string, unknown> {
if (source.type !== 'request/header') return { ...source }
const data = source.data
if (data === null || typeof data !== 'object' || Array.isArray(data)) return { ...source }
const header = (data as Record<string, unknown>).header
if (header === null || typeof header !== 'object' || Array.isArray(header)) return { ...source }
if ((header as Record<string, unknown>).tools !== '{{tools}}') return { ...source }
return {
...source,
data: {
...data,
header: { ...header, tools: [] },
},
}
}
function restoreRequestHeaderTokens(
events: readonly SessionEvent[],
rows: readonly Readonly<Record<string, unknown>>[],
): SessionEvent[] {
const sources = rows.filter(row => row.type === 'request/header')
let sourceIndex = 0
return events.map((event) => {
if (event.type !== 'request/header') return event
const source = sources[sourceIndex]
sourceIndex += 1
const sourceData = source?.data
const sourceHeader = sourceData !== null && typeof sourceData === 'object' && !Array.isArray(sourceData)
? (sourceData as Record<string, unknown>).header
: undefined
if (sourceHeader === null || typeof sourceHeader !== 'object' || Array.isArray(sourceHeader)
|| (sourceHeader as Record<string, unknown>).tools !== '{{tools}}') return event
return {
...event,
data: {
...event.data,
header: {
...(event.data as unknown as { header: Record<string, unknown> }).header,
tools: '{{tools}}',
},
},
} as SessionEvent
})
}
function renderFixture(headerLine: string, events: readonly SessionEvent[]): string {
return [
headerLine,
@@ -191,29 +153,28 @@ function parseFixtureRows(content: string, headerValue: unknown): SessionEvent[]
}
})
}
let restore: ReturnType<typeof sessionFormatCatalog.createRestore>
const collector = new SessionFormatEventCollector()
let decoder: SessionFormatArtifactDecoder
try {
restore = sessionFormatCatalog.createRestore(validationHeader(headerValue), {
recovery: 'strict',
validation: 'current',
})
const version = (headerValue as Record<string, unknown>).version
const codec = historicalCodecs.find(candidate => candidate.version === version)
if (codec === undefined) throw new Error(`unsupported session fixture version ${String(version)}`)
decoder = codec.createDecoder(validationHeader(headerValue), 'strict')
} catch (error) {
const detail = error instanceof Error ? error.message : String(error)
throw new Error(`session snapshot line 1: ${detail}`, { cause: error })
}
for (const [index, row] of rows.entries()) {
try {
restore.decodeRow(validationRow(row))
decoder.decodeRow(row, collector)
} catch (error) {
const detail = error instanceof Error ? error.message : String(error)
throw new Error(`session snapshot line ${rowLines[index] ?? 1}: ${detail}`, { cause: error })
}
}
try {
return restoreRequestHeaderTokens(
[...restore.finish().events] as unknown as SessionEvent[],
rows,
)
decoder.finish(collector)
return [...collector.values] as unknown as SessionEvent[]
} catch (error) {
const detail = error instanceof Error ? error.message : String(error)
const line = fixtureDiagnosticLine(error, rowLines, eventLines)
@@ -246,8 +207,9 @@ function withoutEnvelope(events: readonly SessionEvent[]): Array<Omit<SessionEve
/**
* Canonicalize one JSONL document when its first record is a session header.
* The header line remains byte-identical; body records decode to logical events,
* re-encode one event per row, and omit storage sequence/time envelopes.
* Historical generations receive strict source-codec validation and remain byte-identical;
* conversion to the current format is not required. Current body records re-encode
* one event per row and omit storage sequence/time envelopes; their header is preserved.
* Non-session JSONL returns undefined.
*
* @param content - JSONL source text.