diff --git a/apps/web/tests/approval-composer.e2e.ts b/apps/web/tests/approval-composer.e2e.ts index 268549f58f..d1bc84a488 100644 --- a/apps/web/tests/approval-composer.e2e.ts +++ b/apps/web/tests/approval-composer.e2e.ts @@ -17,7 +17,7 @@ import { import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/approval-composer', import.meta.url)) -const FIXTURE = join(SNAPSHOT_DIR, 'session.v2.jsonl') +const FIXTURE = join(SNAPSHOT_DIR, 'session.v3.jsonl') // The golden covers the stable waiting panel; direct assertions cover its answer. const UI_EXPECTED = join(SNAPSHOT_DIR, 'ui.expected.md') const MODE = webSnapshotMode() @@ -142,6 +142,6 @@ describe('web e2e: approval takeover keeps its actions reachable', () => { }, 300_000) it.skipIf(MODE === 'record')('keeps the fixture inventory closed', async () => { - await assertFixtureInventory(SNAPSHOT_DIR, ['session.v2.jsonl', 'ui.expected.md', 'workspace.expected']) + await assertFixtureInventory(SNAPSHOT_DIR, ['session.v3.jsonl', 'ui.expected.md', 'workspace.expected']) }) }) diff --git a/apps/web/tests/background-job-list.e2e.ts b/apps/web/tests/background-job-list.e2e.ts index cb7025435f..f4db98483a 100644 --- a/apps/web/tests/background-job-list.e2e.ts +++ b/apps/web/tests/background-job-list.e2e.ts @@ -16,7 +16,7 @@ import { } from './scaffold.ts' import { newEnglishPage, saveFailureShot } from './support.ts' -const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/fresh-round-trip/session.v2.jsonl', import.meta.url)) +const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/fresh-round-trip/session.v3.jsonl', import.meta.url)) const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/background-job-list', import.meta.url)) const RUNNING_EXPECTED = join(SNAPSHOT_DIR, 'running.expected.md') const SETTLED_EXPECTED = join(SNAPSHOT_DIR, 'settled.expected.md') diff --git a/apps/web/tests/bash-abort-row.e2e.ts b/apps/web/tests/bash-abort-row.e2e.ts index 50fa548e63..ce42436859 100644 --- a/apps/web/tests/bash-abort-row.e2e.ts +++ b/apps/web/tests/bash-abort-row.e2e.ts @@ -13,7 +13,7 @@ import { } from './scaffold.ts' import { newEnglishPage, saveFailureShot } from './support.ts' -const FIXTURE = fileURLToPath(new URL('../../../snapshots/acp/cancel-tool-calls/session.v2.jsonl', import.meta.url)) +const FIXTURE = fileURLToPath(new URL('../../../snapshots/acp/cancel-tool-calls/session.v3.jsonl', import.meta.url)) const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/bash-abort-row', import.meta.url)) const UI_EXPECTED = join(SNAPSHOT_DIR, 'ui.expected.md') const MODE = webSnapshotMode() diff --git a/apps/web/tests/cordis-tool-round.e2e.ts b/apps/web/tests/cordis-tool-round.e2e.ts index 2b7c75b547..1000bc0aaf 100644 --- a/apps/web/tests/cordis-tool-round.e2e.ts +++ b/apps/web/tests/cordis-tool-round.e2e.ts @@ -20,7 +20,7 @@ import { } from './scaffold.ts' import { connectFreshWorkspace, expandOwningTurnProcess, newEnglishPage, saveFailureShot } from './support.ts' -const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/cordis-tool-round/session.v2.jsonl', import.meta.url)) +const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/cordis-tool-round/session.v3.jsonl', import.meta.url)) const UI_EXPECTED = fileURLToPath(new URL('../../../snapshots/web/cordis-tool-round/ui.expected.md', import.meta.url)) const MODE = webSnapshotMode() const CORDIS_TOOLS = ['cordis_inspect_self', 'cordis_define', 'cordis_run', 'cordis_stop'] as const diff --git a/apps/web/tests/details-session-lifecycle.e2e.ts b/apps/web/tests/details-session-lifecycle.e2e.ts index b903c0c102..722a31b303 100644 --- a/apps/web/tests/details-session-lifecycle.e2e.ts +++ b/apps/web/tests/details-session-lifecycle.e2e.ts @@ -16,8 +16,8 @@ import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './suppor const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/details-session-lifecycle', import.meta.url)) const HANDLES_EXPECTED = join(SNAPSHOT_DIR, 'handles.expected.md') -const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/lifecycle-chrome/session.v2.jsonl', import.meta.url)) -const SEED_FIXTURE = fileURLToPath(new URL('../../../snapshots/web/seeded-history/session.v2.jsonl', import.meta.url)) +const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/lifecycle-chrome/session.v3.jsonl', import.meta.url)) +const SEED_FIXTURE = fileURLToPath(new URL('../../../snapshots/web/seeded-history/session.v3.jsonl', import.meta.url)) const PROMPT = 'Reply with the single word LIGHTHOUSE and stop.' const MODE = webSnapshotMode() diff --git a/apps/web/tests/feedback-command.e2e.ts b/apps/web/tests/feedback-command.e2e.ts index 9e4a7be60e..35d037fb1d 100644 --- a/apps/web/tests/feedback-command.e2e.ts +++ b/apps/web/tests/feedback-command.e2e.ts @@ -18,7 +18,7 @@ import { import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/feedback-command', import.meta.url)) -const FIXTURE = join(SNAPSHOT_DIR, 'session.v2.jsonl') +const FIXTURE = join(SNAPSHOT_DIR, 'session.v3.jsonl') const ACK_EXPECTED = join(SNAPSHOT_DIR, 'ack.expected.md') const ACK_EXPANDED_EXPECTED = join(SNAPSHOT_DIR, 'ack-expanded.expected.md') const MODE = webSnapshotMode() @@ -96,7 +96,7 @@ describe('web e2e: /feedback command acknowledgement', () => { it.skipIf(MODE === 'record')('keeps the fixture inventory closed', async () => { await assertFixtureInventory(SNAPSHOT_DIR, [ - 'session.v2.jsonl', 'ack.expected.md', 'ack-expanded.expected.md', + 'session.v3.jsonl', 'ack.expected.md', 'ack-expanded.expected.md', ]) }) }) diff --git a/apps/web/tests/feedback-release.e2e.ts b/apps/web/tests/feedback-release.e2e.ts index e4659aa622..f71605dde9 100644 --- a/apps/web/tests/feedback-release.e2e.ts +++ b/apps/web/tests/feedback-release.e2e.ts @@ -19,7 +19,7 @@ import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './suppor const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/feedback-release', import.meta.url)) // Both routes borrow the same settled turn; this manifest references its owner. -const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/feedback-command/session.v2.jsonl', import.meta.url)) +const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/feedback-command/session.v3.jsonl', import.meta.url)) const ACK_EXPECTED = join(SNAPSHOT_DIR, 'ack.expected.md') const ACK_EXPANDED_EXPECTED = join(SNAPSHOT_DIR, 'ack-expanded.expected.md') const RELEASE_EXPECTED = join(SNAPSHOT_DIR, 'feedback-release.expected.json') @@ -110,7 +110,7 @@ describe.each(MODE === 'record' ? ['deepseek-official'] : ['deepseek-official', { id: 'feedback-mock', name: 'Feedback mock', contextWindow: 128_000 }, ] }, ], - // The replayed session.v2.jsonl belongs to the feedback-command scenario; + // The replayed session.v3.jsonl belongs to the feedback-command scenario; // comparing (or refreshing) the persisted session here would rewrite // that shared source with this lane's feedback events. Persistence and // collector assertions belong to this lane. diff --git a/apps/web/tests/file-upload-round.e2e.ts b/apps/web/tests/file-upload-round.e2e.ts index 409c46ca6c..d39babbf16 100644 --- a/apps/web/tests/file-upload-round.e2e.ts +++ b/apps/web/tests/file-upload-round.e2e.ts @@ -6,7 +6,7 @@ // content-addressed store makes the saved path identical across record and // replay once the workspace cwd is tokenized, so the recorded read arguments // replay verbatim against a freshly re-uploaded object. -// Record: DSH_SNAPSHOT=record rewrites session.v2.jsonl, then a keyless +// Record: DSH_SNAPSHOT=record rewrites session.v3.jsonl, then a keyless // DSH_SNAPSHOT=refresh regenerates ui.expected.md. import { readFile } from 'node:fs/promises' import { fileURLToPath } from 'node:url' @@ -21,7 +21,7 @@ import { import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/file-upload-round', import.meta.url)) -const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/file-upload-round/session.v2.jsonl', import.meta.url)) +const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/file-upload-round/session.v3.jsonl', import.meta.url)) const UI_EXPECTED = fileURLToPath(new URL('../../../snapshots/web/file-upload-round/ui.expected.md', import.meta.url)) const TRAJECTORY_EXPECTED = fileURLToPath(new URL('../../../snapshots/web/file-upload-round/trajectory.expected.md', import.meta.url)) const OVERRIDE = fileURLToPath(new URL('../../../snapshots/web/file-upload-round/replay.override.json', import.meta.url)) @@ -277,7 +277,7 @@ describe('web e2e: generic file upload through the real assembly', () => { expect(tripwire.pageErrors).toEqual([]) expect(tripwire.warnings).toEqual([]) await assertFixtureInventory(SNAPSHOT_DIR, [ - 'session.v2.jsonl', 'replay.override.json', 'ui.expected.md', 'trajectory.expected.md', + 'session.v3.jsonl', 'replay.override.json', 'ui.expected.md', 'trajectory.expected.md', ]) }) }) diff --git a/apps/web/tests/goal-multi-turn-actions.e2e.ts b/apps/web/tests/goal-multi-turn-actions.e2e.ts index 4259e71ab9..9b6ebf857f 100644 --- a/apps/web/tests/goal-multi-turn-actions.e2e.ts +++ b/apps/web/tests/goal-multi-turn-actions.e2e.ts @@ -17,7 +17,7 @@ import { import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/goal-multi-turn-actions', import.meta.url)) -const FIXTURE = join(SNAPSHOT_DIR, 'session.v2.jsonl') +const FIXTURE = join(SNAPSHOT_DIR, 'session.v3.jsonl') const OVERRIDE = join(SNAPSHOT_DIR, 'replay.override.json') const UI_EXPECTED = join(SNAPSHOT_DIR, 'ui.expected.md') const UI_EXPANDED_EXPECTED = join(SNAPSHOT_DIR, 'ui-expanded.expected.md') @@ -178,7 +178,7 @@ describe('web e2e: Goal keeps one assistant action row per completed turn', () = it.skipIf(MODE === 'record')('keeps a closed fixture inventory', async () => { await assertFixtureInventory(SNAPSHOT_DIR, [ - 'replay.override.json', 'session.v2.jsonl', 'ui.expected.md', 'ui-expanded.expected.md', + 'replay.override.json', 'session.v3.jsonl', 'ui.expected.md', 'ui-expanded.expected.md', ]) }) }) diff --git a/apps/web/tests/lifecycle-chrome.e2e.ts b/apps/web/tests/lifecycle-chrome.e2e.ts index 6052ea0a88..439aa8f0b8 100644 --- a/apps/web/tests/lifecycle-chrome.e2e.ts +++ b/apps/web/tests/lifecycle-chrome.e2e.ts @@ -27,7 +27,7 @@ import { } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/lifecycle-chrome', import.meta.url)) -const FIXTURE = join(SNAPSHOT_DIR, 'session.v2.jsonl') +const FIXTURE = join(SNAPSHOT_DIR, 'session.v3.jsonl') const REPLAY_OVERRIDE = join(SNAPSHOT_DIR, 'replay.override.json') const HERO_EXPECTED = join(SNAPSHOT_DIR, 'hero.expected.md') const COMMAND_MENU_EXPECTED = join(SNAPSHOT_DIR, 'command-menu.expected.md') @@ -413,7 +413,7 @@ describe('web e2e: lifecycle & chrome (workspace flow / reload / dark mode)', () it.skipIf(MODE === 'record')('keeps the fixture inventory closed', async () => { expect(tripwire.warnings).toEqual([]) await assertFixtureInventory(SNAPSHOT_DIR, [ - 'session.v2.jsonl', 'replay.override.json', 'command-menu.expected.md', + 'session.v3.jsonl', 'replay.override.json', 'command-menu.expected.md', 'command-menu-fuzzy.expected.md', 'connection-error.expected.md', 'hero.expected.md', 'plan-active.expected.md', 'reloaded.expected.md', 'reloaded-expanded.expected.md', ]) diff --git a/apps/web/tests/live-interactions.e2e.ts b/apps/web/tests/live-interactions.e2e.ts index 1001b51cf2..aeae5699a6 100644 --- a/apps/web/tests/live-interactions.e2e.ts +++ b/apps/web/tests/live-interactions.e2e.ts @@ -30,7 +30,7 @@ import { import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/live-interactions', import.meta.url)) -const FIXTURE = join(SNAPSHOT_DIR, 'session.v2.jsonl') +const FIXTURE = join(SNAPSHOT_DIR, 'session.v3.jsonl') // One golden pins the empty mid-turn loading state, one pins the sendable draft // state, and the other four capture what remains after cancel, after a // non-retryable failure, after retry recovery, and after retry exhaustion. @@ -325,7 +325,7 @@ describe('web e2e: live-turn interactions (cancel / error / retry)', () => { it.skipIf(MODE === 'record')('keeps the fixture inventory closed', async () => { await assertFixtureInventory(SNAPSHOT_DIR, [ - 'session.v2.jsonl', 'cancel.expected.md', 'cancel-expanded.expected.md', + 'session.v3.jsonl', 'cancel.expected.md', 'cancel-expanded.expected.md', 'loading.expected.md', 'running-draft.expected.md', 'error-auth.expected.md', 'retry.expected.md', 'retry-expanded.expected.md', 'retry-exhausted.expected.md', ]) diff --git a/apps/web/tests/message-actions.e2e.ts b/apps/web/tests/message-actions.e2e.ts index 20cb26da03..68feb8c0d5 100644 --- a/apps/web/tests/message-actions.e2e.ts +++ b/apps/web/tests/message-actions.e2e.ts @@ -19,7 +19,7 @@ import { newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/message-actions', import.meta.url)) // Borrowed read-only: this scenario needs any settled user+assistant pair, not // a new recording (workspace-management / sidebar-scrollbar pattern). -const SEED = fileURLToPath(new URL('../../../snapshots/web/seeded-history/session.v2.jsonl', import.meta.url)) +const SEED = fileURLToPath(new URL('../../../snapshots/web/seeded-history/session.v3.jsonl', import.meta.url)) const UI_EXPECTED = join(SNAPSHOT_DIR, 'ui.expected.md') const FORK_EXPECTED = join(SNAPSHOT_DIR, 'fork.expected.md') const MODE = webSnapshotMode() diff --git a/apps/web/tests/message-feedback-layout.e2e.ts b/apps/web/tests/message-feedback-layout.e2e.ts index 0f05b722d1..1d3ee180af 100644 --- a/apps/web/tests/message-feedback-layout.e2e.ts +++ b/apps/web/tests/message-feedback-layout.e2e.ts @@ -41,7 +41,7 @@ const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/message-feedb const GEOMETRY_EXPECTED = join(SNAPSHOT_DIR, 'geometry.expected.md') const MODE = webSnapshotMode() /** Borrowed read-only: this scenario needs any settled assistant message to rate. */ -const SEED = fileURLToPath(new URL('../../../snapshots/web/seeded-history/session.v2.jsonl', import.meta.url)) +const SEED = fileURLToPath(new URL('../../../snapshots/web/seeded-history/session.v3.jsonl', import.meta.url)) const SEED_ID = 'message-feedback-layout-e2e' /** Viewport widths from full-screen desktop down to a narrow window. */ const WIDTHS = [1680, 1280, 1024, 900, 700, 600] diff --git a/apps/web/tests/message-feedback-protocol.snapshot.ts b/apps/web/tests/message-feedback-protocol.snapshot.ts index e18a32b12d..6936479b61 100644 --- a/apps/web/tests/message-feedback-protocol.snapshot.ts +++ b/apps/web/tests/message-feedback-protocol.snapshot.ts @@ -12,7 +12,7 @@ import { } from './scaffold.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/message-feedback-protocol', import.meta.url)) -const SESSION_FIXTURE = join(SNAPSHOT_DIR, 'session.v2.jsonl') +const SESSION_FIXTURE = join(SNAPSHOT_DIR, 'session.v3.jsonl') const PROTOCOL_EXPECTED = join(SNAPSHOT_DIR, 'protocol.expected.json') const SESSION_ID = 'message-feedback-protocol' const MESSAGE_ID = fixtureIdentity('message', 2) @@ -112,6 +112,6 @@ describe('message feedback Host Remote protocol', () => { expect(exchanges.every(exchange => exchange.status === 200)).toBe(true) await compareOrRefreshGolden(PROTOCOL_EXPECTED, normalizeProtocol(exchanges, version), scaffold.mode) - await assertFixtureInventory(SNAPSHOT_DIR, ['protocol.expected.json', 'session.v2.jsonl']) + await assertFixtureInventory(SNAPSHOT_DIR, ['protocol.expected.json', 'session.v3.jsonl']) }) }) diff --git a/apps/web/tests/message-feedback.e2e.ts b/apps/web/tests/message-feedback.e2e.ts index efcd992137..62cdb33ef2 100644 --- a/apps/web/tests/message-feedback.e2e.ts +++ b/apps/web/tests/message-feedback.e2e.ts @@ -15,7 +15,7 @@ import { newEnglishPage, saveFailureShot } from './support.ts' // Borrowed read-only: this scenario needs any settled assistant message to // address, not a new recording (message-actions / sidebar-scrollbar pattern). -const SEED = fileURLToPath(new URL('../../../snapshots/web/seeded-history/session.v2.jsonl', import.meta.url)) +const SEED = fileURLToPath(new URL('../../../snapshots/web/seeded-history/session.v3.jsonl', import.meta.url)) const MODE = webSnapshotMode() const SEED_ID = 'message-feedback-web-e2e' const NOTE = 'Read both files before answering.' diff --git a/apps/web/tests/minimal-preset.snapshot.ts b/apps/web/tests/minimal-preset.snapshot.ts index 406ea9086d..1b34c986b0 100644 --- a/apps/web/tests/minimal-preset.snapshot.ts +++ b/apps/web/tests/minimal-preset.snapshot.ts @@ -22,7 +22,7 @@ import { import { newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/minimal-preset', import.meta.url)) -const FIXTURE = join(SNAPSHOT_DIR, 'session.v2.jsonl') +const FIXTURE = join(SNAPSHOT_DIR, 'session.v3.jsonl') const UI_EXPECTED = join(SNAPSHOT_DIR, 'ui.expected.md') const MODE = webSnapshotMode() const PROMPT = "Use the bash tool to run exactly: printf 'MINIMAL_BASH_CARD_OK\\n'. Then reply exactly MINIMAL_PRESET_REQUEST_OK and stop." @@ -189,7 +189,7 @@ describe('minimal agent preset', () => { it('keeps its snapshot inventory closed', async () => { await assertFixtureInventory(SNAPSHOT_DIR, [ - 'session.v2.jsonl', + 'session.v3.jsonl', 'system-prompt.expected.md', 'tool-schemas.expected.json', 'ui.expected.md', diff --git a/apps/web/tests/navigation-panes.e2e.ts b/apps/web/tests/navigation-panes.e2e.ts index 0e06e222ee..114b97f976 100644 --- a/apps/web/tests/navigation-panes.e2e.ts +++ b/apps/web/tests/navigation-panes.e2e.ts @@ -22,7 +22,7 @@ import { import { expandOwningTurnProcess, newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/navigation-panes', import.meta.url)) -const SEED = join(SNAPSHOT_DIR, 'session.v2.jsonl') +const SEED = join(SNAPSHOT_DIR, 'session.v3.jsonl') const TRAJECTORY_EXPECTED = join(SNAPSHOT_DIR, 'trajectory.expected.md') const SEARCH_EXPECTED = join(SNAPSHOT_DIR, 'search-results.expected.md') const TERMINAL_EXPECTED = join(SNAPSHOT_DIR, 'terminal-card.expected.md') @@ -499,7 +499,7 @@ describe('web e2e: navigation & panes over a rich seeded session', () => { it.skipIf(MODE === 'record')('keeps the recorded fixture inventory exact', async () => { await assertFixtureInventory(SNAPSHOT_DIR, [ - 'session.v2.jsonl', 'search-results.expected.md', 'trajectory.expected.md', + 'session.v3.jsonl', 'search-results.expected.md', 'trajectory.expected.md', 'terminal-card.expected.md', ]) }) diff --git a/apps/web/tests/permission-policy-context.e2e.ts b/apps/web/tests/permission-policy-context.e2e.ts index 863526d19c..f0b9448470 100644 --- a/apps/web/tests/permission-policy-context.e2e.ts +++ b/apps/web/tests/permission-policy-context.e2e.ts @@ -18,7 +18,7 @@ import { import { connectFreshWorkspace, newEnglishPage, saveFailureShot, writeComposerDraft } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/permission-policy-context', import.meta.url)) -const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/permission-policy-context/session.v2.jsonl', import.meta.url)) +const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/permission-policy-context/session.v3.jsonl', import.meta.url)) const MODE = webSnapshotMode() const PROMPTS = [ @@ -168,6 +168,6 @@ describe('web e2e: current sandbox policy reaches the model before tools', () => it.skipIf(MODE === 'record')('stays clean and keeps the fixture inventory closed', async () => { expect(tripwire.pageErrors).toEqual([]) expect(tripwire.warnings).toEqual([]) - await assertFixtureInventory(SNAPSHOT_DIR, ['session.v2.jsonl', 'workspace.expected']) + await assertFixtureInventory(SNAPSHOT_DIR, ['session.v3.jsonl', 'workspace.expected']) }) }) diff --git a/apps/web/tests/plan-control-row.e2e.ts b/apps/web/tests/plan-control-row.e2e.ts index 7ccf54db9f..a9a1faec95 100644 --- a/apps/web/tests/plan-control-row.e2e.ts +++ b/apps/web/tests/plan-control-row.e2e.ts @@ -37,7 +37,7 @@ import { import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/plan-narrow-viewport', import.meta.url)) -const FIXTURE = join(SNAPSHOT_DIR, 'session.v2.jsonl') +const FIXTURE = join(SNAPSHOT_DIR, 'session.v3.jsonl') const LAYOUT_EXPECTED = join(SNAPSHOT_DIR, 'layout.expected.md') const MODE = webSnapshotMode() @@ -143,6 +143,6 @@ describe('web e2e: plan chip click area at the narrow viewport', () => { }, 200_000) it('keeps the snapshot inventory closed', async () => { - await assertFixtureInventory(SNAPSHOT_DIR, ['session.v2.jsonl', 'layout.expected.md']) + await assertFixtureInventory(SNAPSHOT_DIR, ['session.v3.jsonl', 'layout.expected.md']) }) }) diff --git a/apps/web/tests/plan-review.e2e.ts b/apps/web/tests/plan-review.e2e.ts index ff9e3af4ee..dadaf2d5f6 100644 --- a/apps/web/tests/plan-review.e2e.ts +++ b/apps/web/tests/plan-review.e2e.ts @@ -22,7 +22,7 @@ import { import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/plan-review', import.meta.url)) -const FIXTURE = join(SNAPSHOT_DIR, 'session.v2.jsonl') +const FIXTURE = join(SNAPSHOT_DIR, 'session.v3.jsonl') // The waiting golden owns the decision card; the approved golden owns the // transcript the approval leaves behind — the state the card cannot see. const REVIEW_EXPECTED = join(SNAPSHOT_DIR, 'review.expected.md') @@ -125,7 +125,7 @@ describe('web e2e: plan review takeover round trip', () => { it.skipIf(MODE === 'record')('keeps the fixture inventory closed', async () => { await assertFixtureInventory(SNAPSHOT_DIR, [ - 'session.v2.jsonl', 'review.expected.md', 'sidebar.expected.md', + 'session.v3.jsonl', 'review.expected.md', 'sidebar.expected.md', 'approved.expected.md', 'approved-expanded.expected.md', ]) }) diff --git a/apps/web/tests/ptc-round.e2e.ts b/apps/web/tests/ptc-round.e2e.ts index 66ff93de30..a5e9cf2abc 100644 --- a/apps/web/tests/ptc-round.e2e.ts +++ b/apps/web/tests/ptc-round.e2e.ts @@ -1,5 +1,5 @@ // PTC mode browser round trip with nested sub-calls and details selection. -// Record: DSH_SNAPSHOT=record writes session.v2.jsonl, then a keyless +// Record: DSH_SNAPSHOT=record writes session.v3.jsonl, then a keyless // DSH_SNAPSHOT=refresh regenerates ui.expected.md. import { readFile } from 'node:fs/promises' import { fileURLToPath } from 'node:url' @@ -13,7 +13,7 @@ import { } from './scaffold.ts' import { connectFreshWorkspace, expandOwningTurnProcess, newEnglishPage, saveFailureShot } from './support.ts' -const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/ptc-round/session.v2.jsonl', import.meta.url)) +const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/ptc-round/session.v3.jsonl', import.meta.url)) const UI_EXPECTED = fileURLToPath(new URL('../../../snapshots/web/ptc-round/ui.expected.md', import.meta.url)) const MODE = webSnapshotMode() diff --git a/apps/web/tests/pwsh-terminal.e2e.ts b/apps/web/tests/pwsh-terminal.e2e.ts index ec86ddc4b1..6069367555 100644 --- a/apps/web/tests/pwsh-terminal.e2e.ts +++ b/apps/web/tests/pwsh-terminal.e2e.ts @@ -25,7 +25,7 @@ import { import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/pwsh-terminal', import.meta.url)) -const SEED = join(SNAPSHOT_DIR, 'session.v2.jsonl') +const SEED = join(SNAPSHOT_DIR, 'session.v3.jsonl') const TERMINAL_EXPECTED = join(SNAPSHOT_DIR, 'terminal-card.expected.md') const OVERLAY = fileURLToPath(new URL('./pwsh-terminal.overlay.yml', import.meta.url)) const PROMPT = 'Run a PowerShell command that fails, then stop.' @@ -101,6 +101,6 @@ describe.skipIf(MODE === 'record' || !HAS_PWSH)('web e2e: pwsh calls use the bas }, 60_000) it('guards the lane fixture inventory', async () => { - await assertFixtureInventory(SNAPSHOT_DIR, ['session.v2.jsonl', 'terminal-card.expected.md']) + await assertFixtureInventory(SNAPSHOT_DIR, ['session.v3.jsonl', 'terminal-card.expected.md']) }) }) diff --git a/apps/web/tests/question-composer.e2e.ts b/apps/web/tests/question-composer.e2e.ts index d12abc2712..45ba98f7ac 100644 --- a/apps/web/tests/question-composer.e2e.ts +++ b/apps/web/tests/question-composer.e2e.ts @@ -24,7 +24,7 @@ import { } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/question-composer', import.meta.url)) -const FIXTURE = join(SNAPSHOT_DIR, 'session.v2.jsonl') +const FIXTURE = join(SNAPSHOT_DIR, 'session.v3.jsonl') const UI_EXPECTED = join(SNAPSHOT_DIR, 'ui.expected.md') const SIDEBAR_EXPECTED = join(SNAPSHOT_DIR, 'sidebar.expected.md') const COMPOSED_EXPECTED = join(SNAPSHOT_DIR, 'composed.expected.md') @@ -410,7 +410,7 @@ describe.skipIf(MODE === 'record')('web e2e: cancelled question transcript', () it('keeps the fixture inventory closed', async () => { await assertFixtureInventory(SNAPSHOT_DIR, [ - 'session.v2.jsonl', + 'session.v3.jsonl', 'ui.expected.md', 'sidebar.expected.md', 'composed.expected.md', diff --git a/apps/web/tests/queue-actions.e2e.ts b/apps/web/tests/queue-actions.e2e.ts index 2f45d38d37..d47324474f 100644 --- a/apps/web/tests/queue-actions.e2e.ts +++ b/apps/web/tests/queue-actions.e2e.ts @@ -19,7 +19,7 @@ import { import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/queue-actions', import.meta.url)) -const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/live-interactions/session.v2.jsonl', import.meta.url)) +const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/live-interactions/session.v3.jsonl', import.meta.url)) const COLLAPSED_EXPECTED = join(SNAPSHOT_DIR, 'collapsed.expected.md') const EDITING_EXPECTED = join(SNAPSHOT_DIR, 'editing.expected.md') const LAYOUT_EXPECTED = join(SNAPSHOT_DIR, 'layout.expected.md') diff --git a/apps/web/tests/queue-image.e2e.ts b/apps/web/tests/queue-image.e2e.ts index 9e641bc77c..faa675affc 100644 --- a/apps/web/tests/queue-image.e2e.ts +++ b/apps/web/tests/queue-image.e2e.ts @@ -21,7 +21,7 @@ import { import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/queued-image', import.meta.url)) -const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/live-interactions/session.v2.jsonl', import.meta.url)) +const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/live-interactions/session.v3.jsonl', import.meta.url)) const PNG = fileURLToPath(new URL('../../../snapshots/session/read-image/workspace/red.png', import.meta.url)) const QUEUED_EXPECTED = join(SNAPSHOT_DIR, 'queued.expected.md') const DELIVERED_EXPECTED = join(SNAPSHOT_DIR, 'delivered.expected.md') diff --git a/apps/web/tests/replay-round-trip.e2e.ts b/apps/web/tests/replay-round-trip.e2e.ts index ca634d7ffd..f51fa2e2fa 100644 --- a/apps/web/tests/replay-round-trip.e2e.ts +++ b/apps/web/tests/replay-round-trip.e2e.ts @@ -6,7 +6,7 @@ // record cannot hang on a live model answering differently); assertion steps // run in replay/refresh only. Settled states only — streaming fidelity is // asserted from the durable embedded Assistant stream, not transient DOM. -// Record: DSH_SNAPSHOT=record writes session.v2.jsonl, then a keyless +// Record: DSH_SNAPSHOT=record writes session.v3.jsonl, then a keyless // DSH_SNAPSHOT=refresh regenerates ui.expected.md. import { readFile } from 'node:fs/promises' import { join } from 'node:path' @@ -26,7 +26,7 @@ import { } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/fresh-round-trip', import.meta.url)) -const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/fresh-round-trip/session.v2.jsonl', import.meta.url)) +const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/fresh-round-trip/session.v3.jsonl', import.meta.url)) const UI_EXPECTED = fileURLToPath(new URL('../../../snapshots/web/fresh-round-trip/ui.expected.md', import.meta.url)) const ECHO_EXPECTED = fileURLToPath(new URL('../../../snapshots/web/fresh-round-trip/submission-echo.expected.md', import.meta.url)) const UI_EXPANDED_EXPECTED = fileURLToPath( @@ -224,7 +224,7 @@ describe('web e2e: fresh round trip through the real assembly', () => { expect(tripwire.pageErrors).toEqual([]) expect(tripwire.warnings).toEqual([]) await assertFixtureInventory(SNAPSHOT_DIR, [ - 'session.v2.jsonl', + 'session.v3.jsonl', 'submission-echo.expected.md', 'system-prompt.expected.md', 'tool-schemas.expected.json', diff --git a/apps/web/tests/schedule-after.e2e.ts b/apps/web/tests/schedule-after.e2e.ts index adf5ca1b1e..587a355f4f 100644 --- a/apps/web/tests/schedule-after.e2e.ts +++ b/apps/web/tests/schedule-after.e2e.ts @@ -58,7 +58,7 @@ const EVERY_REPLY = 'Reminders: Check primary metrics; Check secondary metrics.' const EVERY_INTERVAL_SECONDS = 60 * 60 const EVERY_FIXTURE_AGE_MS = 90 * 60 * 1_000 const CATALOG_SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/schedule-catalog', import.meta.url)) -const CATALOG_FIXTURE = join(CATALOG_SNAPSHOT_DIR, 'session.v2.jsonl') +const CATALOG_FIXTURE = join(CATALOG_SNAPSHOT_DIR, 'session.v3.jsonl') const CATALOG_EXPECTED = join(CATALOG_SNAPSHOT_DIR, 'catalog.expected.md') const BASE_PATCH = fileURLToPath(new URL('../../../packages/bundle/base/cordis.patch.yml', import.meta.url)) const WEB_PATCH = fileURLToPath(new URL('../../../packages/bundle/web-app/cordis.patch.yml', import.meta.url)) @@ -816,7 +816,7 @@ describe.skipIf(MODE === 'record')('web e2e: active Schedule catalog', () => { }).toBe(0) await assertFixtureInventory(CATALOG_SNAPSHOT_DIR, [ 'catalog.expected.md', - 'session.v2.jsonl', + 'session.v3.jsonl', 'system-prompt.expected.md', 'tool-schemas.expected.json', ]) diff --git a/apps/web/tests/seeded-history.e2e.ts b/apps/web/tests/seeded-history.e2e.ts index 925bb5322b..5fbb39a15c 100644 --- a/apps/web/tests/seeded-history.e2e.ts +++ b/apps/web/tests/seeded-history.e2e.ts @@ -9,7 +9,7 @@ // `/feedback` pins its expandable correlation ids. The seed is a recorded // fixture under the same record discipline as every other: DSH_SNAPSHOT=record drives the turn // live through the composer (real read tool against seeded workspace files) -// and harvests session.v2.jsonl; replay/refresh seed it cold and only render. +// and harvests session.v3.jsonl; replay/refresh seed it cold and only render. import { readFile, writeFile, mkdir } from 'node:fs/promises' import { fileURLToPath } from 'node:url' import type { Browser, Page } from 'playwright' @@ -30,7 +30,7 @@ import { import { expandOwningTurnProcess, newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/seeded-history', import.meta.url)) -const SEED = fileURLToPath(new URL('../../../snapshots/web/seeded-history/session.v2.jsonl', import.meta.url)) +const SEED = fileURLToPath(new URL('../../../snapshots/web/seeded-history/session.v3.jsonl', import.meta.url)) const UI_EXPECTED = fileURLToPath(new URL('../../../snapshots/web/seeded-history/ui.expected.md', import.meta.url)) const UI_EXPANDED_EXPECTED = fileURLToPath( new URL('../../../snapshots/web/seeded-history/ui-expanded.expected.md', import.meta.url), @@ -564,7 +564,7 @@ describe('web e2e: seeded history renders through cold resume', () => { expect(tripwire.warnings).toEqual([]) await assertFixtureInventory(SNAPSHOT_DIR, [ 'command-row.expected.md', 'feedback-row.expected.md', 'file-open-failure.expected.md', - 'session.v2.jsonl', 'ui.expected.md', 'ui-expanded.expected.md', + 'session.v3.jsonl', 'ui.expected.md', 'ui-expanded.expected.md', ]) }) }) diff --git a/apps/web/tests/sidebar-scrollbar.e2e.ts b/apps/web/tests/sidebar-scrollbar.e2e.ts index d1c14f0379..0f1443c8d9 100644 --- a/apps/web/tests/sidebar-scrollbar.e2e.ts +++ b/apps/web/tests/sidebar-scrollbar.e2e.ts @@ -15,7 +15,7 @@ import { } from './scaffold.ts' import { newEnglishPage, saveFailureShot } from './support.ts' -const SEED = fileURLToPath(new URL('../../../snapshots/web/seeded-history/session.v2.jsonl', import.meta.url)) +const SEED = fileURLToPath(new URL('../../../snapshots/web/seeded-history/session.v3.jsonl', import.meta.url)) const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/sidebar-scrollbar', import.meta.url)) /** Geometry and resolved style are absent from ARIA snapshots, so this scenario records them directly. */ const GEOMETRY_EXPECTED = join(SNAPSHOT_DIR, 'geometry.expected.md') @@ -412,7 +412,7 @@ describe('web e2e: sidebar session list scrollbar (reserved gutter / themed thum }, 60_000) it('commits exactly the fixtures it reads', async () => { - // The scenario borrows seeded-history's session.v2.jsonl rather than committing a + // The scenario borrows seeded-history's session.v3.jsonl rather than committing a // second copy, so this directory holds the golden alone. await assertFixtureInventory(SNAPSHOT_DIR, ['geometry.expected.md']) }) diff --git a/apps/web/tests/skill-tool-row.e2e.ts b/apps/web/tests/skill-tool-row.e2e.ts index d32e50ea86..ce84e0b76e 100644 --- a/apps/web/tests/skill-tool-row.e2e.ts +++ b/apps/web/tests/skill-tool-row.e2e.ts @@ -12,7 +12,7 @@ import { } from './scaffold.ts' import { expandOwningTurnProcess, newEnglishPage, saveFailureShot } from './support.ts' -const FIXTURE = fileURLToPath(new URL('../../../snapshots/session/skill-load/session.v2.jsonl', import.meta.url)) +const FIXTURE = fileURLToPath(new URL('../../../snapshots/session/skill-load/session.v3.jsonl', import.meta.url)) const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/skill-tool-row', import.meta.url)) const UI_EXPECTED = fileURLToPath(new URL('../../../snapshots/web/skill-tool-row/ui.expected.md', import.meta.url)) const MODE = webSnapshotMode() diff --git a/apps/web/tests/steering.e2e.ts b/apps/web/tests/steering.e2e.ts index f96fbdbe1f..b73f0d5c5d 100644 --- a/apps/web/tests/steering.e2e.ts +++ b/apps/web/tests/steering.e2e.ts @@ -19,7 +19,7 @@ import { import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/steering', import.meta.url)) -const FIXTURE = join(SNAPSHOT_DIR, 'session.v2.jsonl') +const FIXTURE = join(SNAPSHOT_DIR, 'session.v3.jsonl') // Two goldens pin the transient Host projection and its durable handoff: the // mid-turn state renders accepted steering from the Session control queue while the // question blocks admission, then the settled state renders the same message @@ -186,7 +186,7 @@ describe('web e2e: mid-turn steering lands durably and visibly', () => { it.skipIf(MODE === 'record')('keeps the fixture inventory closed', async () => { await assertFixtureInventory(SNAPSHOT_DIR, [ - 'session.v2.jsonl', 'mid-steer.expected.md', 'settled.expected.md', 'settled-expanded.expected.md', + 'session.v3.jsonl', 'mid-steer.expected.md', 'settled.expected.md', 'settled-expanded.expected.md', ]) }) }) diff --git a/apps/web/tests/subagent-conversation.e2e.ts b/apps/web/tests/subagent-conversation.e2e.ts index d9b66749de..dfca321123 100644 --- a/apps/web/tests/subagent-conversation.e2e.ts +++ b/apps/web/tests/subagent-conversation.e2e.ts @@ -19,7 +19,7 @@ import { } from './scaffold.ts' import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts' -const BASE_FIXTURE = fileURLToPath(new URL('../../../snapshots/web/live-interactions/session.v2.jsonl', import.meta.url)) +const BASE_FIXTURE = fileURLToPath(new URL('../../../snapshots/web/live-interactions/session.v3.jsonl', import.meta.url)) const AVAILABLE_CHILD_EXPECTED = fileURLToPath(new URL('../../../snapshots/web/subagent-conversation/ui.expected.md', import.meta.url)) const AVAILABLE_CHILD_EXPANDED_EXPECTED = fileURLToPath( new URL('../../../snapshots/web/subagent-conversation/ui-expanded.expected.md', import.meta.url), diff --git a/apps/web/tests/subagent-interrupt-ui.e2e.ts b/apps/web/tests/subagent-interrupt-ui.e2e.ts index 5cf3efa820..e6653f1cec 100644 --- a/apps/web/tests/subagent-interrupt-ui.e2e.ts +++ b/apps/web/tests/subagent-interrupt-ui.e2e.ts @@ -27,7 +27,7 @@ import { } from './scaffold.ts' import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts' -const BASE_FIXTURE = fileURLToPath(new URL('../../../snapshots/web/live-interactions/session.v2.jsonl', import.meta.url)) +const BASE_FIXTURE = fileURLToPath(new URL('../../../snapshots/web/live-interactions/session.v3.jsonl', import.meta.url)) const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/subagent-interrupt', import.meta.url)) const OFFLINE_COMPOSER_EXPECTED = join(SNAPSHOT_DIR, 'offline-composer.expected.md') diff --git a/apps/web/tests/turn-tail-actions.e2e.ts b/apps/web/tests/turn-tail-actions.e2e.ts index 1ae0f8be62..9457407c0a 100644 --- a/apps/web/tests/turn-tail-actions.e2e.ts +++ b/apps/web/tests/turn-tail-actions.e2e.ts @@ -23,7 +23,7 @@ import { import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/turn-tail-actions', import.meta.url)) -const FIXTURE = join(SNAPSHOT_DIR, 'session.v2.jsonl') +const FIXTURE = join(SNAPSHOT_DIR, 'session.v3.jsonl') // Three goldens for the same message: parked mid-turn, aborted, and completed. const RUNNING_EXPECTED = join(SNAPSHOT_DIR, 'running.expected.md') const SETTLED_EXPECTED = join(SNAPSHOT_DIR, 'settled.expected.md') @@ -291,7 +291,7 @@ describe('web e2e: assistant IconActions wait for the turn to end', () => { await assertFixtureInventory( SNAPSHOT_DIR, [ - 'completed.expected.md', 'focused.expected.md', 'running.expected.md', 'session.v2.jsonl', + 'completed.expected.md', 'focused.expected.md', 'running.expected.md', 'session.v3.jsonl', 'settled.expected.md', 'usage-expanded.expected.md', ], ) diff --git a/apps/web/tests/web-search-round.e2e.ts b/apps/web/tests/web-search-round.e2e.ts index 7ffde8594f..4128d0d46e 100644 --- a/apps/web/tests/web-search-round.e2e.ts +++ b/apps/web/tests/web-search-round.e2e.ts @@ -19,7 +19,7 @@ import { import { connectFreshWorkspace, expandOwningTurnProcess, newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/web-search-round', import.meta.url)) -const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/web-search-round/session.v2.jsonl', import.meta.url)) +const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/web-search-round/session.v3.jsonl', import.meta.url)) const UI_EXPECTED = fileURLToPath(new URL('../../../snapshots/web/web-search-round/ui.expected.md', import.meta.url)) const MODE = webSnapshotMode() const QUERIES = ['DeepSeek Harness snapshot search', 'DeepSeek Harness multi-query search'] as const @@ -323,6 +323,6 @@ describe('web e2e: shipped default web search', () => { it.skipIf(MODE === 'record')('stayed clean and kept the exact fixture inventory', async () => { expect(tripwire.pageErrors).toEqual([]) expect(tripwire.warnings).toEqual([]) - await assertFixtureInventory(SNAPSHOT_DIR, ['session.v2.jsonl', 'ui.expected.md']) + await assertFixtureInventory(SNAPSHOT_DIR, ['session.v3.jsonl', 'ui.expected.md']) }) }) diff --git a/apps/web/tests/workflow-run.e2e.ts b/apps/web/tests/workflow-run.e2e.ts index 08f4c5e193..5b800d4b74 100644 --- a/apps/web/tests/workflow-run.e2e.ts +++ b/apps/web/tests/workflow-run.e2e.ts @@ -22,8 +22,8 @@ const MODE = webSnapshotMode() const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/workflow-run', import.meta.url)) const UI_LIVE_EXPECTED = join(SNAPSHOT_DIR, 'ui-live.expected.md') const UI_EXPECTED = join(SNAPSHOT_DIR, 'ui.expected.md') -const PARENT_FIXTURE = join(REPO_ROOT, 'snapshots/session/workflow-run/session.v2.jsonl') -const CHILD_FIXTURE = join(REPO_ROOT, 'snapshots/session/workflow-run/session.1.v2.jsonl') +const PARENT_FIXTURE = join(REPO_ROOT, 'snapshots/session/workflow-run/session.v3.jsonl') +const CHILD_FIXTURE = join(REPO_ROOT, 'snapshots/session/workflow-run/session.1.v3.jsonl') const CHILD_PROMPT = 'Reply with exactly the word WF_CHILD_OK and nothing else.' describe.skipIf(MODE === 'record')('web e2e: durable workflow run in Chat', () => { diff --git a/apps/web/tests/workspace-management.e2e.ts b/apps/web/tests/workspace-management.e2e.ts index 382e549ddf..ee64883339 100644 --- a/apps/web/tests/workspace-management.e2e.ts +++ b/apps/web/tests/workspace-management.e2e.ts @@ -28,7 +28,7 @@ import { newEnglishPage, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/workspace-management', import.meta.url)) // The seed is another scenario's committed fixture, reused read-only: this // spec needs any one cold session row, not new recorded content. -const SEED = fileURLToPath(new URL('../../../snapshots/web/seeded-history/session.v2.jsonl', import.meta.url)) +const SEED = fileURLToPath(new URL('../../../snapshots/web/seeded-history/session.v3.jsonl', import.meta.url)) const MODE = webSnapshotMode() const BROWSER_EXPECTED = join(SNAPSHOT_DIR, 'directory-browser.expected.md') const SEED_ID = 'workspace-management-web-e2e' diff --git a/apps/web/tests/workspace-new-session-folding.e2e.ts b/apps/web/tests/workspace-new-session-folding.e2e.ts index 8e45770e2f..21bdd58871 100644 --- a/apps/web/tests/workspace-new-session-folding.e2e.ts +++ b/apps/web/tests/workspace-new-session-folding.e2e.ts @@ -20,7 +20,7 @@ import { newEnglishPage, saveFailureShot } from './support.ts' const EXPECTED_DIR = fileURLToPath(new URL('./expected/workspace-new-session-folding', import.meta.url)) const SIDEBAR_EXPECTED = join(EXPECTED_DIR, 'sidebar.expected.md') -const SEED = fileURLToPath(new URL('../../../snapshots/web/message-feedback-protocol/session.v2.jsonl', import.meta.url)) +const SEED = fileURLToPath(new URL('../../../snapshots/web/message-feedback-protocol/session.v3.jsonl', import.meta.url)) const MODE = webSnapshotMode() const EXISTING_SESSION_COUNT = 6 diff --git a/packages/test-support/session-snapshot/src/index.ts b/packages/test-support/session-snapshot/src/index.ts index 771560de8b..1b53cccfe2 100644 --- a/packages/test-support/session-snapshot/src/index.ts +++ b/packages/test-support/session-snapshot/src/index.ts @@ -82,6 +82,7 @@ export { sessionFixtureName, sessionFixtureNames, sessionHeaderVersion, + writerSnapshotName, type PersistedSessionFile, type SessionFixtureFile, } from './session-files.ts' diff --git a/packages/test-support/session-snapshot/src/normalize.ts b/packages/test-support/session-snapshot/src/normalize.ts index 4f3768f3b7..c0624918f3 100644 --- a/packages/test-support/session-snapshot/src/normalize.ts +++ b/packages/test-support/session-snapshot/src/normalize.ts @@ -451,59 +451,21 @@ export function normalizeSessionSnapshots( } /** - * Omit only generation-qualified operational provenance from expected-output comparison. + * Omit the artifact header generation after official migration for comparison. + * Delivery and captured-source generations retain their opaque recorded values. * @param rawLog - Session records or events as compact JSON lines. - * @returns the same records without delivery or captured-source generation qualifiers. + * @returns the same records with only the Session header version omitted. */ export function normalizeSessionFormatProvenance(rawLog: string): string { return rawLog.split('\n').map((line) => { if (line.trim().length === 0) return line const record = JSON.parse(line) as Record - let changed = normalizeCapturedFormatProvenance(record) - if (record.type === 'session' && Object.hasOwn(record, 'version')) { - delete record.version - changed = true - } - if (record.type === 'session-log-deepseek/delivery-accepted' - && record.data !== null && typeof record.data === 'object' && !Array.isArray(record.data)) { - const data = { ...record.data as Record } - if (Object.hasOwn(data, 'sessionFormatVersion')) { - delete data.sessionFormatVersion - record.data = data - changed = true - } - } - return changed ? JSON.stringify(record) : line + if (record.type !== 'session' || !Object.hasOwn(record, 'version')) return line + delete record.version + return JSON.stringify(record) }).join('\n') } -/** Omit captured generations only from an actual current Message source position. */ -function normalizeCapturedFormatProvenance(event: Record): boolean { - if (event.data === null || typeof event.data !== 'object' || Array.isArray(event.data)) return false - const data = event.data as Record - const message = event.type === 'user/message' - ? data - : event.type === 'assistant/message' || event.type === 'tool/result' - ? data.message - : undefined - if (message === null || typeof message !== 'object' || Array.isArray(message)) return false - const source = (message as Record).source - if (source === null || typeof source !== 'object' || Array.isArray(source)) return false - const record = source as Record - if (record.kind !== 'session-reference' || record.form !== 'recall' || record.version !== 1 - || !Array.isArray(record.references)) return false - let changed = false - for (const reference of record.references) { - if (reference === null || typeof reference !== 'object' || Array.isArray(reference)) continue - const captured = reference as Record - if (Object.hasOwn(captured, 'capturedFormatVersion')) { - delete captured.capturedFormatVersion - changed = true - } - } - return changed -} - /** Whether a fixture declares a released Session format and therefore participates in migration burn-in. */ function hasSessionFormatVersion(rawLog: string): boolean { const firstLine = rawLog.split(/\r?\n/).find(line => line.trim().length > 0) diff --git a/packages/test-support/session-snapshot/src/session-files.ts b/packages/test-support/session-snapshot/src/session-files.ts index bcb5609b52..8578abe416 100644 --- a/packages/test-support/session-snapshot/src/session-files.ts +++ b/packages/test-support/session-snapshot/src/session-files.ts @@ -46,6 +46,17 @@ export function sessionFixtureName(index: number, version: number): string { return `session${ordinal}${generation}.jsonl` } +/** + * Name the native-writer oracle for a retained historical replay role. + * This expected output never participates in replay generation selection. + * @param index - Parent `0` or a positive child/ordinal slot. + * @returns The expected-output JSONL basename. + */ +export function writerSnapshotName(index: number): string { + nonNegativeSafeInteger(index, 'writer snapshot index') + return `writer${index === 0 ? '' : `.${index}`}.expected.jsonl` +} + /** * Parse one canonical recorded-session fixture filename. * diff --git a/packages/test-support/session-snapshot/tests/normalize.spec.ts b/packages/test-support/session-snapshot/tests/normalize.spec.ts index faf269309b..083781e417 100644 --- a/packages/test-support/session-snapshot/tests/normalize.spec.ts +++ b/packages/test-support/session-snapshot/tests/normalize.spec.ts @@ -658,78 +658,16 @@ describe('normalizeSessionSnapshot', () => { .toThrow('session snapshot must start with a session header') }) - it('compares migrated and fresh delivery watermarks without changing their raw generation identity', () => { - const id = '11111111-2222-3333-4444-555555555555' - const session = (version: 0 | 1, sessionFormatVersion?: number): string => [ - JSON.stringify({ type: 'session', version, id, createdAt: 0, delegationDepth: 0 }), - JSON.stringify({ type: 'turn/start', data: { turn: 1 } }), - JSON.stringify({ - type: 'session-log-deepseek/delivery-accepted', - data: { - sessionId: id, - throughSeq: 0, - ...sessionFormatVersion === undefined ? {} : { sessionFormatVersion }, - }, - }), - JSON.stringify({ type: 'turn/end', data: { turn: 1, reason: { kind: 'completed' } } }), - '', - ].join('\n') - - const migratedV0 = normalizeSessionSnapshots([session(0)], { sessionIds: [], cwd: '/unused' }) - const freshV1 = normalizeSessionSnapshots([session(1, 1)], { sessionIds: [], cwd: '/unused' }) - - expect(migratedV0).toEqual(freshV1) - expect(freshV1[0]).not.toContain('"sessionFormatVersion"') + it('preserves delivery and captured-source generations after artifact migration', () => { + const event = (version: number): string => JSON.stringify({ + type: 'session-log-deepseek/delivery-accepted', + data: { sessionId: 's', throughSeq: 4, sessionFormatVersion: version }, + }) + expect(normalizeSessionFormatProvenance(event(0))).toBe(event(0)) + expect(normalizeSessionFormatProvenance(event(3))).not.toBe(normalizeSessionFormatProvenance(event(0))) }) - it('compares migrated and fresh session-reference captures without hiding other source fields', () => { - const id = '11111111-2222-3333-4444-555555555555' - const sourceId = '22222222-3333-4444-5555-666666666666' - const messageId = '33333333-4444-4555-8666-777777777777' - const session = (version: 0 | 1, capturedFormatVersion?: number): string => [ - JSON.stringify({ type: 'session', version, id, createdAt: 0, delegationDepth: 0 }), - JSON.stringify({ type: 'turn/start', data: { turn: 1 } }), - JSON.stringify({ - type: 'user/message', - data: { - id: messageId, - role: 'user', - content: [{ type: 'text', text: 'remember' }], - source: { - kind: 'session-reference', - form: 'recall', - version: 1, - references: [{ - sessionId: sourceId, - label: 'Source', - capturedThroughSeq: 0, - ...capturedFormatVersion === undefined ? {} : { capturedFormatVersion }, - compacted: false, - originalMessages: 1, - retainedMessages: 1, - omittedMessages: 0, - omittedBytes: 0, - truncated: false, - inputIndex: 0, - }], - }, - }, - surfaceOp: 'append', - }), - JSON.stringify({ type: 'turn/end', data: { turn: 1, reason: { kind: 'completed' } } }), - '', - ].join('\n') - - const migratedV0 = normalizeSessionSnapshots([session(0)], { sessionIds: [], cwd: '/unused' }) - const freshV1 = normalizeSessionSnapshots([session(1, 1)], { sessionIds: [], cwd: '/unused' }) - - expect(migratedV0).toEqual(freshV1) - expect(freshV1[0]).toContain('"label":"Source"') - expect(freshV1[0]).toContain('"version":1') - expect(freshV1[0]).not.toContain('"capturedFormatVersion"') - }) - - it('removes generation qualifiers only from their exact provenance positions', () => { + it('preserves opaque generation qualifiers and their lookalikes', () => { const raw = [ JSON.stringify({ type: 'session', @@ -782,14 +720,14 @@ describe('normalizeSessionSnapshot', () => { .split('\n') .map(line => JSON.parse(line) as Record) ?? [] - expect(delivery?.data).toEqual({ throughSeq: 21, otherVersion: 8 }) + expect(delivery?.data).toEqual({ sessionFormatVersion: 1, throughSeq: 21, otherVersion: 8 }) expect(captured?.data).toMatchObject({ source: { references: [ null, 'opaque', [{ capturedFormatVersion: 6 }], - { otherVersion: 9 }, + { capturedFormatVersion: 1, otherVersion: 9 }, ], }, }) diff --git a/packages/test-support/session-snapshot/tests/suite.spec.ts b/packages/test-support/session-snapshot/tests/suite.spec.ts index f5ce6ad916..ebeb91e17f 100644 --- a/packages/test-support/session-snapshot/tests/suite.spec.ts +++ b/packages/test-support/session-snapshot/tests/suite.spec.ts @@ -15,6 +15,7 @@ import { sessionFixtureName, sessionFixtureNames, sessionHeaderVersion, + writerSnapshotName, stabilizeFixtureMessageIds, tokenizeSessionFixtureCwd, type HarvestedLog, @@ -486,6 +487,19 @@ describe('shared snapshot content', () => { }) }) +describe('writerSnapshotName', () => { + it('keeps native writer expectations outside historical replay selection', () => { + expect(writerSnapshotName(0)).toBe('writer.expected.jsonl') + expect(writerSnapshotName(2)).toBe('writer.2.expected.jsonl') + expect(sessionFixtureNames(['session.v1.jsonl', writerSnapshotName(0), writerSnapshotName(1)])) + .toEqual(['session.v1.jsonl']) + }) + + it.each([-1, -0, 0.5, Number.MAX_SAFE_INTEGER + 1])('rejects invalid role %s', (index) => { + expect(() => writerSnapshotName(index)).toThrow('writer snapshot index must be a non-negative safe integer') + }) +}) + describe('sessionFixtureNames', () => { it('selects the highest generation for each contiguous role while ignoring other files', () => { expect(sessionFixtureNames([ diff --git a/python/sdk/tests/test_smoke_model.py b/python/sdk/tests/test_smoke_model.py index 4ff22908ed..a2e1b90be8 100644 --- a/python/sdk/tests/test_smoke_model.py +++ b/python/sdk/tests/test_smoke_model.py @@ -75,7 +75,7 @@ def test_mcp_smoke_accepts_the_external_server_result() -> None: ) -def test_snapshot_comparison_normalizes_only_session_generation_provenance() -> None: +def test_snapshot_comparison_preserves_opaque_generation_provenance() -> None: normalize = SMOKE["normalize_session_format_comparison"] expected = { "header": {"type": "session", "version": 0, "otherVersion": 7}, @@ -104,8 +104,24 @@ def test_snapshot_comparison_normalizes_only_session_generation_provenance() -> }, } - assert normalize(expected) == normalize(actual) + assert normalize(expected) != normalize(actual) + assert normalize(expected)["header"] == normalize(actual)["header"] assert normalize(expected)["header"]["otherVersion"] == 7 + assert normalize(actual)["accepted"] == actual["accepted"] + assert normalize(actual)["source"] == actual["source"] + + +def test_snapshot_value_scrubs_system_nodes_without_erasing_header_fields() -> None: + normalize = SMOKE["normalize_snapshot_value"] + system = { + "type": "system/message", + "data": {"message": {"role": "system", "content": [{"type": "text", "text": "prompt"}]}}, + } + header = {"type": "request/header", "data": {"header": {"system": "unexpected"}}} + assert normalize(system, [])["data"]["message"]["content"] == [{"type": "text", "text": "{{system}}"}] + assert normalize(header, []) == header + empty = {"type": "system/message", "data": {"message": {"role": "system", "content": []}}} + assert normalize(empty, []) == empty def test_snapshot_value_normalizes_embedded_assistant_stream_timing() -> None: diff --git a/scripts/smoke-python-runtime.py b/scripts/smoke-python-runtime.py index 03babe7121..fb96b7bf71 100644 --- a/scripts/smoke-python-runtime.py +++ b/scripts/smoke-python-runtime.py @@ -126,7 +126,7 @@ ADVANCED_SNAPSHOT_DIRECTORY = ( Path(__file__).resolve().parent / "snapshots" / "python-sdk-single-exe" / "advanced" ) ADVANCED_SNAPSHOT_FILENAMES = ( - "result.json", "session.v2.jsonl", "session.1.v2.jsonl", "session.2.v2.jsonl", + "result.json", "session.v3.jsonl", "session.1.v3.jsonl", "session.2.v3.jsonl", ) MINIMAL_SNAPSHOT_DIRECTORY = ( Path(__file__).resolve().parent / "snapshots" / "python-sdk-single-exe" / "minimal" @@ -138,7 +138,7 @@ RESTART_SNAPSHOT_DIRECTORY = ( Path(__file__).resolve().parent / "snapshots" / "python-sdk-single-exe" / "restart" ) RESTART_SNAPSHOT_FILENAMES = ( - "result.json", "requests.json", "session.1.v2.jsonl", "session.2.v2.jsonl", + "result.json", "requests.json", "session.1.v3.jsonl", "session.2.v3.jsonl", ) MCP_SERVER_SCRIPT = """\ import json @@ -2139,19 +2139,6 @@ def normalize_session_format_comparison( normalized.pop("time", None) if source_session_version == 1 and normalized.get("type") == "assistant/message": normalized.pop("sourceEventSeqs", None) - if normalized.get("type") == "session-log-deepseek/delivery-accepted": - data = normalized.get("data") - if isinstance(data, dict): - data.pop("throughSeq", None) - data.pop("sessionFormatVersion", None) - data["sessionFormatVersion"] = SESSION_FORMAT_PROVENANCE - if normalized.get("kind") == "session-reference": - references = normalized.get("references") - if isinstance(references, list): - for reference in references: - if isinstance(reference, dict): - reference.pop("capturedFormatVersion", None) - reference["capturedFormatVersion"] = SESSION_FORMAT_PROVENANCE return normalized diff --git a/snapshots/sdk/sdk.snapshot.ts b/snapshots/sdk/sdk.snapshot.ts index fb913cb9fc..fbfaa4578a 100644 --- a/snapshots/sdk/sdk.snapshot.ts +++ b/snapshots/sdk/sdk.snapshot.ts @@ -39,6 +39,7 @@ import { systemPromptPrecedesRequests, sessionFixtureNames, sessionHeaderVersion, + writerSnapshotName, stabilizeFixtureMessageIds, stabilizeRefreshLog, tokenizeSessionFixtureCwd, @@ -59,7 +60,7 @@ import { type RunResult, type SdkPromptContentBlock, } from '@deepseek-ai/dsh-sdk-client' -import { prepareSessionEventNotificationsForComparison } from '@deepseek-ai/dsh-llm-replay' +import { SESSION_FORMAT_VERSION } from '@deepseek-ai/dsh-session' const corpusRoot = fileURLToPath(new URL('../', import.meta.url)) @@ -235,7 +236,7 @@ async function persistedLogs(sessionsRoot: string): Promise { const files = await jsonlFiles(sessionsRoot) return Promise.all(files.map(async (path) => { const content = await readFile(path, 'utf8') - assertPersistedSessionVersion(basename(path), content) + expect(assertPersistedSessionVersion(basename(path), content), `${path}: current writer`).toBe(SESSION_FORMAT_VERSION) const header = JSON.parse(content.slice(0, content.indexOf('\n'))) as Record return { path, content, header } })) @@ -774,13 +775,20 @@ describe('TypeScript SDK snapshots over the jsonrpc runtime', () => { : it scenarioTest(`${mode}s ${scenario.name} through dsh --profile sdk`, async () => { const scenarioDir = scenario.dir - const notificationsExpectedPath = join(scenarioDir, 'notifications.expected.jsonl') + const retained = scenario.manifest.sessionFormat !== undefined + const notificationsExpectedPath = join(scenarioDir, retained ? 'notifications.current.expected.jsonl' : 'notifications.expected.jsonl') const resultExpectedPath = join(scenarioDir, 'result.expected.json') const hasWireGoldens = existsSync(notificationsExpectedPath) || existsSync(resultExpectedPath) const assertions = SDK_ASSERTIONS[scenario.name] ?? {} const writesSessionFixtures = writesCurrentSessionFixtures(scenario.manifest, sessionWriteMode) let files = await fixtureFiles(scenario) + const replayContents = await Promise.all(files.map(file => readFile(file, 'utf8'))) + if (!recording && !refreshing) { + const writerFiles = (await readdir(scenarioDir)).filter(name => /^writer(?:\.[1-9]\d*)?\.expected\.jsonl$/u.test(name)).sort() + expect(writerFiles, 'native writer oracle inventory').toEqual(retained + ? files.map((_, index) => writerSnapshotName(index)).sort() : []) + } const { results, notifications, observedMethods, logs, initialWorkspace, finalWorkspace, cwd } = await runScenario(scenario) const ordered = orderLogs( logs, @@ -789,7 +797,9 @@ describe('TypeScript SDK snapshots over the jsonrpc runtime', () => { ) const actualContext = contextOf(ordered, cwd) - let expectedContents = await Promise.all(files.map(file => readFile(file, 'utf8'))) + let expectedContents = retained && !refreshing + ? await Promise.all(files.map((_, index) => readFile(join(scenarioDir, writerSnapshotName(index)), 'utf8'))) + : replayContents if (recording) { expectedContents = redactSessionSnapshotIds(stabilizeFixtureMessageIds( @@ -798,7 +808,7 @@ describe('TypeScript SDK snapshots over the jsonrpc runtime', () => { )) } - if (refreshing && writesSessionFixtures) { + if (refreshing && (writesSessionFixtures || retained)) { const harvested = ordered.map((log): HarvestedLog => ({ id: String(log.header.id), createdAt: Number(log.header.createdAt), @@ -816,11 +826,10 @@ describe('TypeScript SDK snapshots over the jsonrpc runtime', () => { expectedContents = redactSessionSnapshotIds(stabilizeFixtureMessageIds(refreshed, expectedContents)) } - if (writesSessionFixtures) { - const outputFiles = ordered.map((log, index) => join(scenarioDir, sessionFixtureName( - index, - sessionHeaderVersion(log.content, `harvested Session ${index}`), - ))) + if (writesSessionFixtures || refreshing && retained) { + const outputFiles = ordered.map((log, index) => join(scenarioDir, retained + ? writerSnapshotName(index) + : sessionFixtureName(index, sessionHeaderVersion(log.content, `harvested Session ${index}`)))) await Promise.all(expectedContents.map((stable, index) => writeFile(outputFiles[index] as string, stable))) files = outputFiles await writeHeaderSidecars(scenario, ordered, actualContext) @@ -833,6 +842,14 @@ describe('TypeScript SDK snapshots over the jsonrpc runtime', () => { expect(redactSessionSnapshotIds(expectedContents), `${scenario.name}: identity redaction fixed point`) .toEqual(expectedContents) + if (retained) { + expect(await Promise.all((await fixtureFiles(scenario)).map(file => readFile(file, 'utf8'))), + 'historical replay input remains unchanged').toEqual(replayContents) + for (const [index, content] of expectedContents.entries()) { + expect(sessionHeaderVersion(content, writerSnapshotName(index))).toBe(SESSION_FORMAT_VERSION) + } + } + // Persisted transcripts match the committed fixtures. const expectedContext = contextOfContents(expectedContents) const actualSnapshots = normalizeSessionSnapshots(ordered.map(log => log.content), actualContext) @@ -852,10 +869,10 @@ describe('TypeScript SDK snapshots over the jsonrpc runtime', () => { } const expectedNotifications = await readFile(notificationsExpectedPath, 'utf8') expect( - records(prepareSessionEventNotificationsForComparison(normalizedNotifications)), + records(normalizedNotifications), `${scenario.name}: notifications`, ) - .toEqual(records(prepareSessionEventNotificationsForComparison(expectedNotifications))) + .toEqual(records(expectedNotifications)) expect(normalizedResult).toBe(await readFile(resultExpectedPath, 'utf8')) } diff --git a/snapshots/session/headless.snapshot.ts b/snapshots/session/headless.snapshot.ts index 2ce8eb280e..4e41d8494d 100644 --- a/snapshots/session/headless.snapshot.ts +++ b/snapshots/session/headless.snapshot.ts @@ -35,6 +35,7 @@ import { systemPromptPrecedesRequests, sessionFixtureNames, sessionHeaderVersion, + writerSnapshotName, snapshotSpillRoot, stabilizeFixtureMessageIds, stabilizeRefreshLog, @@ -168,7 +169,7 @@ async function persistedSessions(cwd: string): Promise { const files = latestPersistedSessionPaths(await readdir(root, { recursive: true })) const logs = await Promise.all(files.map(async (file): Promise => { const content = await readFile(join(root, file), 'utf8') - assertPersistedSessionVersion(basename(file), content) + expect(assertPersistedSessionVersion(basename(file), content), `${file}: current writer`).toBe(SESSION_FORMAT_VERSION) return { content, header: headerOf(content) } })) return logs.sort((left, right) => { @@ -202,10 +203,9 @@ async function writeSessionFixtures( existing: readonly string[], ctx: NormalizeContext, ): Promise { - const names = actualLogs.map((log, index) => sessionFixtureName( - index, - sessionHeaderVersion(log.content, `harvested Session ${index}`), - )) + const names = actualLogs.map((log, index) => scenario.manifest.sessionFormat === undefined + ? sessionFixtureName(index, sessionHeaderVersion(log.content, `harvested Session ${index}`)) + : writerSnapshotName(index)) const prior = names.map((_, index) => existing[index] ?? '') const replacements = mode === 'refresh' ? refreshFixtureReplacements(actualLogs.map(harvested), prior) @@ -535,36 +535,6 @@ function pinOf(scenario: HeadlessScenario): HeadlessScenario { return pin } -/** Compare retained prompt-in-header semantics without weakening current-writer snapshots. */ -function historicalComparison(log: string): string { - const current = prepareSessionSnapshotFixtureForComparison(log) - const events = parseSessionLog(current) - const retained = events.filter(event => event.type !== 'system/message') - const positions = new Map(retained.map((event, index) => [Number(event.seq), index])) - let system: string | undefined - const output: JsonObject[] = [] - for (const event of events) { - if (event.type === 'system/message') { - system = event.data.message.content.flatMap(block => block.type === 'text' ? [block.text] : []).join('') - continue - } - const row = JSON.parse(JSON.stringify(event)) as JsonObject - row.seq = positions.get(Number(event.seq)) - const data = row.data as JsonObject - if (event.type === 'request/header' && system !== undefined) { - data.header = { ...data.header as JsonObject, system } - } - if (Array.isArray(row.sourceEventSeqs)) { - row.sourceEventSeqs = row.sourceEventSeqs.map(seq => positions.get(Number(seq))) - } - if (event.type === 'session/title' && Array.isArray(data.messageSeqs)) { - data.messageSeqs = data.messageSeqs.map(seq => positions.get(Number(seq))) - } - output.push(row) - } - return [records(current)[0], ...output].map(row => JSON.stringify(row)).join('\n') + '\n' -} - /** Require successful verification and the complete canonical event before refresh can write a fixture. */ async function verifySessionQuerySpill(log: string): Promise { const events = parseSessionLog(log) @@ -653,30 +623,6 @@ describe('headless recorded-session snapshots', () => { } }) - it('preserves prompt, request, and citation differences in historical comparisons', async () => { - const fixture = await readFile(join(snapshotsRoot, 'tool-call-turn/session.v2.jsonl'), 'utf8') - const baseline = records(historicalComparison(fixture)) - const changed = (mutate: (rows: JsonObject[]) => void): JsonObject[] => { - const rows = records(fixture) - mutate(rows) - return records(historicalComparison(rows.map(row => JSON.stringify(row)).join('\n') + '\n')) - } - expect(baseline.find(row => row.type === 'request/header')?.data).toMatchObject({ header: { system: '{{system}}' } }) - expect(changed(rows => { - const data = rows.find(row => row.type === 'system/message')?.data as JsonObject - const message = data.message as JsonObject - message.content = [{ type: 'text', text: 'wrong system prompt' }] - })).not.toEqual(baseline) - expect(changed(rows => { - const header = rows.find(row => row.type === 'request/header')?.data as JsonObject - ;((header.header as JsonObject).config as JsonObject).model = 'different-model' - })).not.toEqual(baseline) - expect(() => changed(rows => { - const title = rows.find(row => row.type === 'session/title')?.data as JsonObject - title.messageSeqs = [0] - })).toThrow('messageSeqs must cite earlier human user/message events') - }) - it('recognizes the supported OS-assigned listener forms', () => { expect(listenerPortViolations('accepted.mjs', [ "server.listen(0, '127.0.0.1')", @@ -716,7 +662,7 @@ describe('headless recorded-session snapshots', () => { it('keeps packed chunk rows logically equal to their unpacked recording', async () => { const sourceDir = join(snapshotsRoot, 'hook-cc-pretool-deny') const packedDir = join(snapshotsRoot, 'packed-chunks') - const source = await readFile(join(sourceDir, await primaryFixtureFile(sourceDir)), 'utf8') + const source = await readFile(join(sourceDir, 'session.jsonl'), 'utf8') const packed = await readFile(join(packedDir, await primaryFixtureFile(packedDir)), 'utf8') const rowTypes = records(packed).flatMap((record) => { const type = record.type @@ -751,15 +697,10 @@ describe('headless recorded-session snapshots', () => { } } if (cloned.type === 'hook/result') delete cloned.data?.durationMs - if (cloned.type === 'session/title') delete (cloned.data as { messageSeqs?: unknown })?.messageSeqs - // Positional seq references shift with the `system/message` node the - // retained packed generation cannot invent; both sides cite their own - // step's Assistant settlement, so the citation is not the comparison. - delete (cloned as { sourceEventSeqs?: unknown }).sourceEventSeqs return cloned } const logical = (fixture: string): unknown[] => { - const current = historicalComparison(fixture) + const current = prepareSessionSnapshotFixtureForComparison(fixture) return [ records(current)[0], ...parseSessionLog(current).map(withoutVolatileMessage), @@ -1032,12 +973,22 @@ describe('headless recorded-session snapshots', () => { expect(result.stdout).toBe(`${finalTextFromSession(fixtures[0] as string)}\n`) expect(result.stderr).toBe(expectedStderr) expect(actualLogs, `${scenario.name}: persisted session count`).toHaveLength(fixtures.length) - const fixtureContext = contextOf(fixtures) - const comparison = scenario.manifest.sessionFormat === undefined - ? (log: string): string => log - : (log: string): string => historicalComparison(scrubSystemPrompts(log)) - const actualSnapshots = normalizeSessionSnapshots(actualLogs.map(log => comparison(log.content)), actualContext) - const expectedSnapshots = normalizeSessionSnapshots(fixtures.map(comparison), fixtureContext) + const writerFiles = (await readdir(scenario.dir)).filter(name => /^writer(?:\.[1-9]\d*)?\.expected\.jsonl$/u.test(name)).sort() + if (mode === 'replay') { + expect(writerFiles, 'native writer oracle inventory').toEqual(scenario.manifest.sessionFormat === undefined + ? [] : fixtures.map((_, index) => writerSnapshotName(index)).sort()) + } + let expected = fixtures + if (scenario.manifest.sessionFormat !== undefined) { + if (mode === 'refresh') await writeSessionFixtures(scenario, actualLogs, fixtures, actualContext) + expected = await Promise.all(fixtures.map((_, index) => readFile(join(scenario.dir, writerSnapshotName(index)), 'utf8'))) + for (const [index, content] of expected.entries()) { + expect(sessionHeaderVersion(content, writerSnapshotName(index))).toBe(SESSION_FORMAT_VERSION) + } + expect(await fixtureSessions(scenario), 'historical replay input remains unchanged').toEqual(fixtures) + } + const actualSnapshots = normalizeSessionSnapshots(actualLogs.map(log => log.content), actualContext) + const expectedSnapshots = normalizeSessionSnapshots(expected, contextOf(expected)) for (const [index, actual] of actualSnapshots.entries()) { expect(records(actual), `${scenario.name}: session ${index}`).toEqual(records(expectedSnapshots[index] as string)) }