refactor(native-chat): drop the transcript helpers only the handoff called

appendLegacyTranscriptMessages fed the terminal transcript catch-up and
proveClaudeTranscriptBranch backed the terminal owner's exit proof. Both lost
their last caller with the handoff. Their tests now go through the live entry
points instead: the roster bounds through the legacy import, the pinned-read and
growth tests through the ancestry replay the history window uses, and the marker
rules through the string proof in their own file rather than the session-file
resolver's.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Brennan Benson
2026-09-25 00:27:15 -07:00
co-authored by Claude
parent 42d10eee15
commit 048391506f
7 changed files with 462 additions and 498 deletions
@@ -137,19 +137,6 @@ async function runPinnedTranscriptPasses<T>(
}
}
export async function proveClaudeTranscriptBranch(
input: BranchProofInput & { transcriptPath: string }
): Promise<ClaudeTranscriptBranchProof> {
return runPinnedTranscriptPasses(input.transcriptPath, undefined, async (readLines) => {
const builder = createBranchProof(input)
let index = 0
for await (const record of readLines()) {
builder.add(record.line, index++, record.terminated)
}
return builder.finish()
})
}
/**
* Prove the branch from the file's last transcript row back to the anchor, then
* replay the anchor..tail records off the SAME pinned bytes. Two bounded passes
@@ -73,28 +73,35 @@ import { appendFile, mkdtemp, rename, rm, truncate, unlink, writeFile } from 'no
import {
ClaudeTranscriptPreviousCursorMissingError,
ClaudeTranscriptTailIncompleteError,
proveClaudeTranscriptBranch,
proveClaudeTranscriptBranchFromJsonl
replayClaudeTranscriptBranchAncestry,
replayClaudeTranscriptBranchAncestryFromJsonl
} from './claude-transcript-branch-proof'
const row = (uuid: string, parentUuid: string | null, extra = {}) =>
`${JSON.stringify({ type: 'user', uuid, parentUuid, sessionId: 'provider', ...extra })}\n`
const marker = (leafUuid: string, sessionId = 'provider') =>
`${JSON.stringify({ type: 'last-prompt', leafUuid, sessionId })}\n`
/** Carries no uuid, so it can be neither a tip nor a marker. */
const SUMMARY = `${JSON.stringify({ type: 'summary', summary: 'title' })}\n`
const ROOT = row('root', null)
const CHILD = row('child', 'root')
const SOURCE = ROOT + marker('root')
let directory = ''
const read = (previousLeafUuid: string | null = null, intentionalRewindUuid?: string) =>
proveClaudeTranscriptBranch({
let replayed: string[] = []
// The live caller always anchors the replay at the cursor it already holds.
const replay = (anchorUuid = 'root') =>
replayClaudeTranscriptBranchAncestry({
transcriptPath: state.path,
providerSessionId: 'provider',
previousLeafUuid,
...(intentionalRewindUuid === undefined ? {} : { intentionalRewindUuid })
previousLeafUuid: anchorUuid,
ancestryAnchorUuid: anchorUuid,
onAncestorRecord: (_record, uuid) => replayed.push(uuid)
})
const read = async (anchorUuid?: string) => (await replay(anchorUuid)).proof
beforeEach(async () => {
directory = await mkdtemp(join(tmpdir(), 'orca-branch-streaming-'))
replayed = []
Object.assign(state, {
path: join(directory, 'transcript.jsonl'),
opens: 0,
@@ -121,7 +128,7 @@ afterEach(async () => {
}
})
it('decodes UTF-8 across chunks and proves old ancestry beyond large message bodies', async () => {
it('decodes UTF-8 across chunks and replays ancestry beyond large message bodies', async () => {
const prefix = JSON.stringify({ type: 'comment', content: '' }).indexOf('"content":"') + 11
const comment = `${JSON.stringify({ type: 'comment', content: `${'x'.repeat(65535 - prefix)}🙂é漢字` })}\n`
const contents =
@@ -130,13 +137,18 @@ it('decodes UTF-8 across chunks and proves old ancestry beyond large message bod
CHILD +
marker('child').trimEnd()
await writeFile(state.path, contents)
expect(await read('root')).toEqual(
proveClaudeTranscriptBranchFromJsonl({
const fromString: string[] = []
expect(await replay()).toEqual(
replayClaudeTranscriptBranchAncestryFromJsonl({
contents,
providerSessionId: 'provider',
previousLeafUuid: 'root'
previousLeafUuid: 'root',
ancestryAnchorUuid: 'root',
onAncestorRecord: (_record, uuid) => fromString.push(uuid)
})
)
expect(replayed).toEqual(fromString)
expect(replayed).toEqual(['child'])
})
it.each([
@@ -156,31 +168,36 @@ it.each([
it('proves only the original finite prefix while the same file grows', async () => {
state.afterStat = () => appendFile(state.path, CHILD + marker('child') + ' '.repeat(1024 * 1024))
expect(await read()).toEqual({ leafUuid: 'root', relation: 'initial' })
expect(await read()).toEqual({ leafUuid: 'root', relation: 'same' })
expect(state.bytesRead).toBe(Buffer.byteLength(SOURCE))
})
it('keeps a proved rewind tied to the original prefix', async () => {
await writeFile(state.path, ROOT + CHILD + marker('root'))
state.afterStat = () => appendFile(state.path, marker('child'))
expect(await read('child', 'root')).toEqual({ leafUuid: 'root', relation: 'intentional-rewind' })
it('replays the ancestry off the same pinned prefix the proof read', async () => {
const contents = ROOT + CHILD
await writeFile(state.path, contents)
state.afterStat = () => appendFile(state.path, row('grandchild', 'child'))
expect(await replay()).toEqual({
proof: { leafUuid: 'child', relation: 'descendant' },
chain: ['child']
})
expect(replayed).toEqual(['child'])
expect(state.bytesRead).toBe(Buffer.byteLength(contents) * 2)
})
it.each([
['empty', '', SOURCE, null],
['missing marker', ROOT, marker('root'), null],
['empty', '', SOURCE, 'root'],
['missing previous cursor', SOURCE, CHILD + marker('child'), 'child']
] as const)(
'finishes a growing %s proof on the same open handle',
async (_name, contents, growth, previous) => {
async (_name, contents, growth, anchor) => {
await writeFile(state.path, contents)
state.afterStat = () => appendFile(state.path, growth)
expect((await read(previous)).leafUuid).toBe(previous ?? 'root')
expect((await read(anchor)).leafUuid).toBe(anchor)
expect(state.opens).toBe(1)
}
)
it.each(['', ROOT])('keeps a static missing marker fatal', async (contents) => {
it.each(['', SUMMARY])('keeps a static missing tip fatal', async (contents) => {
await writeFile(state.path, contents)
await expect(read()).rejects.toThrow('missing last-prompt marker')
})
@@ -190,22 +207,22 @@ it('preserves the typed static missing-cursor error for existing root reproof',
})
it.each([
['conflict', ROOT + row('root', 'foreign') + marker('root'), 'conflicting ancestry'],
['wrong session', ROOT + marker('root', 'foreign'), 'invalid last-prompt'],
['append order', CHILD + ROOT + marker('child'), 'parent row follows'],
['missing ancestor', CHILD + marker('child'), 'missing ancestor']
['conflict', ROOT + row('root', 'foreign') + marker('root'), 'root', 'conflicting ancestry'],
['wrong session', ROOT + marker('root', 'foreign'), 'root', 'invalid last-prompt'],
['append order', CHILD + ROOT, 'root', 'parent row follows'],
['missing ancestor', CHILD, 'child', 'missing ancestor']
] as const)(
'does not turn %s into a retry when the file grows',
async (_name, contents, message) => {
async (_name, contents, anchor, message) => {
await writeFile(state.path, contents)
state.afterStat = () => appendFile(state.path, CHILD)
await expect(read()).rejects.toThrow(message)
await expect(read(anchor)).rejects.toThrow(message)
}
)
it('does not infer growth from a failed second stat', async () => {
await writeFile(state.path, ROOT)
state.afterStat = () => appendFile(state.path, marker('root'))
await writeFile(state.path, '')
state.afterStat = () => appendFile(state.path, SOURCE)
state.statErrorOn = 2
await expect(read()).rejects.toThrow('missing last-prompt marker')
})
@@ -219,7 +236,7 @@ it('keeps reading the original handle after pathname replacement', async () => {
})
it('does not use a replacement file to establish growth', async () => {
await writeFile(state.path, ROOT)
await writeFile(state.path, '')
state.afterStat = async () => {
await rename(state.path, `${state.path}.original`)
await writeFile(state.path, ROOT + CHILD + marker('child'))
@@ -252,16 +269,18 @@ it('completes a record appended after the first observed extent without caller r
})
it('validates the entire refreshed prefix instead of accepting a malformed repair', async () => {
await writeFile(state.path, ROOT)
state.afterStat = () => appendFile(state.path, row('root', 'foreign') + marker('root'))
const conflicting = row('root', 'foreign')
await writeFile(state.path, ROOT + conflicting.slice(0, -5))
state.afterStat = () => appendFile(state.path, conflicting.slice(-5))
await expect(read()).rejects.toThrow('conflicting ancestry')
expect(state.opens).toBe(1)
})
it('keeps an unfinished growing repair retryable after one internal refresh', async () => {
await writeFile(state.path, ROOT)
state.afterStat = () => appendFile(state.path, ROOT)
const torn = ROOT.slice(0, -5)
await writeFile(state.path, torn)
state.afterStat = () => appendFile(state.path, ' ')
await expect(read()).rejects.toBeInstanceOf(ClaudeTranscriptTailIncompleteError)
expect(state.opens).toBe(1)
expect(state.bytesRead).toBe(Buffer.byteLength(ROOT) * 3)
expect(state.bytesRead).toBe(Buffer.byteLength(torn) * 2 + 1)
})
@@ -0,0 +1,342 @@
// The branch rules a Claude transcript must satisfy, read off the marker tip. The live replay
// proves from the file tail instead and falls back to these rules when no tail row qualifies.
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import {
ClaudeTranscriptTailIncompleteError,
proveClaudeTranscriptBranchFromJsonl
} from './claude-transcript-branch-proof'
let tempRoots: string[] = []
afterEach(async () => {
await Promise.all(tempRoots.map((root) => rm(root, { recursive: true, force: true })))
tempRoots = []
})
async function makeRoot(prefix: string): Promise<string> {
const root = await mkdtemp(join(tmpdir(), prefix))
tempRoots.push(root)
return root
}
async function proveMarkerLeafUuid(
transcriptPath: string,
providerSessionId: string,
previousLeafUuid: string | null = null
): Promise<string> {
const contents = await readFile(transcriptPath, 'utf8')
return proveClaudeTranscriptBranchFromJsonl({ contents, providerSessionId, previousLeafUuid })
.leafUuid
}
describe('Claude transcript marker branch proof', () => {
it('reads Claude last-prompt leaf metadata as the durable branch marker', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-leaf-')
const transcript = join(root, 'session.jsonl')
await writeFile(
transcript,
[
{ type: 'user', uuid: 'leaf-old', parentUuid: null, sessionId: 'session-1' },
{
type: 'assistant',
uuid: 'leaf-current',
parentUuid: 'leaf-old',
sessionId: 'session-1'
},
{ type: 'last-prompt', leafUuid: 'leaf-current', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(proveMarkerLeafUuid(transcript, 'session-1', 'leaf-old')).resolves.toBe(
'leaf-current'
)
})
it('fails closed when a Claude transcript has no branch marker', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-no-leaf-')
const transcript = join(root, 'session.jsonl')
await writeFile(
transcript,
'{"type":"assistant","uuid":"not-a-leaf","parentUuid":null,"sessionId":"session-1"}\n',
'utf8'
)
await expect(proveMarkerLeafUuid(transcript, 'session-1')).rejects.toThrow(
'missing last-prompt marker'
)
})
it('distinguishes an incomplete final Claude JSONL record from durable malformed content', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-torn-tail-')
const transcript = join(root, 'session.jsonl')
await writeFile(transcript, '{"type":"last-prompt"', 'utf8')
await expect(proveMarkerLeafUuid(transcript, 'session-1')).rejects.toBeInstanceOf(
ClaudeTranscriptTailIncompleteError
)
await writeFile(transcript, '{"type":"last-prompt"\n', 'utf8')
await expect(proveMarkerLeafUuid(transcript, 'session-1')).rejects.not.toBeInstanceOf(
ClaudeTranscriptTailIncompleteError
)
})
it('refuses a Claude marker on a sibling branch', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-sibling-')
const transcript = join(root, 'session.jsonl')
await writeFile(
transcript,
[
{ type: 'user', uuid: 'root', parentUuid: null, sessionId: 'session-1' },
{ type: 'assistant', uuid: 'expected', parentUuid: 'root', sessionId: 'session-1' },
{ type: 'system', uuid: 'sibling', parentUuid: 'root', sessionId: 'session-1' },
{ type: 'last-prompt', leafUuid: 'sibling', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(proveMarkerLeafUuid(transcript, 'session-1', 'expected')).rejects.toThrow(
'sibling branch'
)
})
it('refuses missing and cyclic Claude parent chains', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-invalid-ancestry-')
const missing = join(root, 'missing.jsonl')
const cycle = join(root, 'cycle.jsonl')
await writeFile(
missing,
[
{ type: 'user', uuid: 'expected', parentUuid: null, sessionId: 'session-1' },
{ type: 'assistant', uuid: 'leaf', parentUuid: 'absent', sessionId: 'session-1' },
{ type: 'last-prompt', leafUuid: 'leaf', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await writeFile(
cycle,
[
{ type: 'user', uuid: 'expected', parentUuid: null, sessionId: 'session-1' },
{ type: 'assistant', uuid: 'left', parentUuid: 'right', sessionId: 'session-1' },
{ type: 'system', uuid: 'right', parentUuid: 'left', sessionId: 'session-1' },
{ type: 'last-prompt', leafUuid: 'right', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(proveMarkerLeafUuid(missing, 'session-1', 'expected')).rejects.toThrow(
'missing ancestor absent'
)
await expect(proveMarkerLeafUuid(cycle, 'session-1', 'expected')).rejects.toThrow(
'cycle in parentUuid ancestry'
)
})
it('rejects non-transcript and sidechain UUIDs as the durable leaf', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-leaf-filter-')
const transcript = join(root, 'session.jsonl')
await writeFile(
transcript,
[
{ type: 'user', uuid: 'main-user', parentUuid: null, sessionId: 'session-1' },
{
type: 'assistant',
uuid: 'sidechain-assistant',
parentUuid: 'main-user',
sessionId: 'session-1',
isSidechain: true
},
{ type: 'result', uuid: 'result-frame', parentUuid: 'main-user', sessionId: 'session-1' },
{
type: 'system',
subtype: 'init',
uuid: 'init-frame',
parentUuid: null,
sessionId: 'session-1'
},
{ type: 'stream_event', uuid: 'stream-frame', parentUuid: null, sessionId: 'session-1' },
{ type: 'last-prompt', leafUuid: 'sidechain-assistant', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(proveMarkerLeafUuid(transcript, 'session-1')).rejects.toThrow(
'marker leaf is missing from the session graph'
)
})
it('rejects a main leaf whose ancestry crosses a subagent sidechain', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-sidechain-ancestry-')
const transcript = join(root, 'session.jsonl')
await writeFile(
transcript,
[
{ type: 'user', uuid: 'main-user', parentUuid: null, sessionId: 'session-1' },
{
type: 'assistant',
uuid: 'sidechain-assistant',
parentUuid: 'main-user',
sessionId: 'session-1',
isSidechain: true
},
{
type: 'assistant',
uuid: 'main-after-sidechain',
parentUuid: 'sidechain-assistant',
sessionId: 'session-1'
},
{ type: 'last-prompt', leafUuid: 'main-after-sidechain', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(proveMarkerLeafUuid(transcript, 'session-1')).rejects.toThrow(
'not on the main transcript'
)
})
it('rejects a main leaf whose ancestry crosses a parent-tool-use sidechain', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-parent-tool-ancestry-')
const transcript = join(root, 'transcript.jsonl')
await writeFile(
transcript,
[
{ type: 'user', uuid: 'main-user', parentUuid: null, sessionId: 'session-1' },
{
type: 'assistant',
uuid: 'subagent-assistant',
parentUuid: 'main-user',
sessionId: 'session-1',
parent_tool_use_id: 'tool-use-1'
},
{
type: 'assistant',
uuid: 'main-after-sidechain',
parentUuid: 'subagent-assistant',
sessionId: 'session-1'
},
{ type: 'last-prompt', leafUuid: 'main-after-sidechain', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(proveMarkerLeafUuid(transcript, 'session-1')).rejects.toThrow(
'not on the main transcript'
)
})
it('rejects a previous cursor descended from a parent-tool-use sidechain', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-parent-tool-cursor-')
const transcript = join(root, 'transcript.jsonl')
await writeFile(
transcript,
[
{ type: 'user', uuid: 'main-user', parentUuid: null, sessionId: 'session-1' },
{
type: 'assistant',
uuid: 'subagent-assistant',
parentUuid: 'main-user',
sessionId: 'session-1',
parent_tool_use_id: 'tool-use-1'
},
{
type: 'assistant',
uuid: 'main-after-sidechain',
parentUuid: 'subagent-assistant',
sessionId: 'session-1'
},
{ type: 'last-prompt', leafUuid: 'main-after-sidechain', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(
proveMarkerLeafUuid(transcript, 'session-1', 'main-after-sidechain')
).rejects.toThrow('not on the main transcript')
})
it('rejects a latest marker descended from a parent-tool-use cursor sidechain', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-parent-tool-cursor-descendant-')
const transcript = join(root, 'transcript.jsonl')
await writeFile(
transcript,
[
{ type: 'user', uuid: 'main-user', parentUuid: null, sessionId: 'session-1' },
{
type: 'assistant',
uuid: 'subagent-assistant',
parentUuid: 'main-user',
sessionId: 'session-1',
parent_tool_use_id: 'tool-use-1'
},
{
type: 'assistant',
uuid: 'main-after-sidechain',
parentUuid: 'subagent-assistant',
sessionId: 'session-1'
},
{
type: 'assistant',
uuid: 'latest-after-sidechain',
parentUuid: 'main-after-sidechain',
sessionId: 'session-1'
},
{ type: 'last-prompt', leafUuid: 'latest-after-sidechain', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(
proveMarkerLeafUuid(transcript, 'session-1', 'main-after-sidechain')
).rejects.toThrow('not on the main transcript')
})
it('rejects a post-snapshot descendant whose parent row was observed later', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-post-snapshot-')
const transcript = join(root, 'transcript.jsonl')
await writeFile(
transcript,
[
{
type: 'assistant',
uuid: 'descendant',
parentUuid: 'previous',
sessionId: 'session-1'
},
{ type: 'assistant', uuid: 'previous', parentUuid: null, sessionId: 'session-1' },
{ type: 'last-prompt', leafUuid: 'descendant', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(proveMarkerLeafUuid(transcript, 'session-1', 'previous')).rejects.toThrow(
'parent row follows descendant'
)
})
})
@@ -5,21 +5,32 @@
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { agentJournalItemKey } from '../../../shared/agent-session-journal-item-key'
import type {
AgentSessionJournalIdentity,
AgentSessionProviderHandle
} from '../../../shared/agent-session-journal-types'
import type { NativeChatMessage } from '../../../shared/native-chat-types'
import type * as TranscriptLineDecoders from '../transcript-line-decoders'
import { createLegacyIdentityTracker } from './journal-legacy-identity'
import {
appendLegacyTranscriptMessages,
importLegacyTranscriptIntoJournal
} from './journal-legacy-import'
import { importLegacyTranscriptIntoJournal } from './journal-legacy-import'
import { DEFAULT_JOURNAL_PAYLOAD_LIMITS } from './journal-payload-bounds'
import { openAgentSessionJournal } from './journal-store-factory'
import type { AgentSessionJournal } from './journal-store'
// No shipped decoder emits a subagent roster, so the roster bounds are reached by standing one in.
const decodedClaudeOverride = vi.hoisted(() => ({ message: null as NativeChatMessage | null }))
vi.mock('../transcript-line-decoders', async (importOriginal) => {
const actual = await importOriginal<typeof TranscriptLineDecoders>()
return {
...actual,
decodeClaudeTranscriptLine: (...args: Parameters<typeof actual.decodeClaudeTranscriptLine>) =>
decodedClaudeOverride.message ?? actual.decodeClaudeTranscriptLine(...args)
}
})
const CLAUDE_SESSION = '29eb22a4-6a5f-4f21-9b0c-1d7f3a2e5c88'
const CODEX_SESSION = '019fd532-7c11-7a90-b6de-4e1a2c3d5f60'
@@ -189,6 +200,7 @@ beforeEach(async () => {
})
afterEach(async () => {
decodedClaudeOverride.message = null
await rm(root, { recursive: true, force: true })
})
@@ -297,46 +309,6 @@ describe('claude import', () => {
})
describe('codex import', () => {
it('upserts live transcript messages without rolling the structured epoch', async () => {
const journal = await open('codex', CODEX_SESSION)
const epoch = journal.epoch
const message = {
id: 'live-tui-message',
role: 'assistant' as const,
blocks: [{ type: 'text' as const, text: 'first version' }],
timestamp: 1_800_000_000_000,
source: 'transcript' as const
}
await appendLegacyTranscriptMessages({
journal,
agent: 'codex',
sessionId: CODEX_SESSION,
fence: 2,
messages: [message]
})
await appendLegacyTranscriptMessages({
journal,
agent: 'codex',
sessionId: CODEX_SESSION,
fence: 2,
messages: [{ ...message, blocks: [{ type: 'text', text: 'final version' }] }]
})
expect(journal.epoch).toBe(epoch)
expect(journal.snapshot().items).toMatchObject([
{
itemId: legacyKey('live-tui-message'),
revision: 2,
body: {
kind: 'message',
role: 'assistant',
blocks: [{ type: 'text', text: 'final version' }]
}
}
])
})
it('keys rollout records in the import-scoped namespace, not as app-server ordinals', async () => {
const filePath = await writeFixture('rollout.jsonl', CODEX_LINES)
const journal = await open('codex', CODEX_SESSION)
@@ -396,6 +368,32 @@ describe('codex import', () => {
})
})
type RosterAgents = Extract<
NativeChatMessage['blocks'][number],
{ type: 'subagent-group' }
>['agents']
async function importDecodedRoster(agents: RosterAgents): Promise<AgentSessionJournal> {
decodedClaudeOverride.message = {
id: 'legacy-roster',
role: 'assistant',
timestamp: null,
source: 'transcript',
blocks: [{ type: 'subagent-group', groupId: 'group-1', agents }]
}
const filePath = await writeFixture('claude-roster.jsonl', [CLAUDE_LINES[2]])
const journal = await open('claude', CLAUDE_SESSION)
const result = await importLegacyTranscriptIntoJournal({
journal,
agent: 'claude',
sessionId: CLAUDE_SESSION,
fence: 1,
options: { filePath }
})
expect(result).toMatchObject({ ok: true, imported: 1 })
return journal
}
describe('payload bounds on import', () => {
it('marks a clipped tool result and discards the remainder', async () => {
const output = 'y'.repeat(64 * 1024)
@@ -436,32 +434,13 @@ describe('payload bounds on import', () => {
it('bounds an imported subagent roster by entry count, label and id', async () => {
// The import reads an untrusted file: nothing upstream bounded either string.
const oversized = 'z'.repeat(20 * 1024)
const journal = await open('claude', CLAUDE_SESSION)
await appendLegacyTranscriptMessages({
journal,
agent: 'claude',
sessionId: CLAUDE_SESSION,
fence: 1,
messages: [
{
id: 'legacy-roster',
role: 'assistant',
timestamp: null,
source: 'transcript',
blocks: [
{
type: 'subagent-group',
groupId: 'group-1',
agents: Array.from({ length: 80 }, (_, index) => ({
id: index === 0 ? oversized : `task-${index}`,
label: index === 0 ? oversized : `label-${index}`,
state: 'working' as const
}))
}
]
}
]
})
const journal = await importDecodedRoster(
Array.from({ length: 80 }, (_, index) => ({
id: index === 0 ? oversized : `task-${index}`,
label: index === 0 ? oversized : `label-${index}`,
state: 'working' as const
}))
)
const body = journal.snapshot().items[0]?.body
const block = body?.kind === 'message' ? body.blocks[0] : undefined
@@ -478,31 +457,10 @@ describe('payload bounds on import', () => {
// The id is the roster key: it takes the same bounded-id format the wires
// use, so a later wire bound is a no-op instead of a second, merging clip.
const head = 'y'.repeat(512)
const journal = await open('claude', CLAUDE_SESSION)
await appendLegacyTranscriptMessages({
journal,
agent: 'claude',
sessionId: CLAUDE_SESSION,
fence: 1,
messages: [
{
id: 'legacy-roster-collision',
role: 'assistant',
timestamp: null,
source: 'transcript',
blocks: [
{
type: 'subagent-group',
groupId: 'group-1',
agents: [
{ id: `${head}-one`, label: 'Audit', state: 'working' as const },
{ id: `${head}-two`, label: 'Audit', state: 'working' as const }
]
}
]
}
]
})
const journal = await importDecodedRoster([
{ id: `${head}-one`, label: 'Audit', state: 'working' as const },
{ id: `${head}-two`, label: 'Audit', state: 'working' as const }
])
const body = journal.snapshot().items[0]?.body
const block = body?.kind === 'message' ? body.blocks[0] : undefined
@@ -62,30 +62,6 @@ export type LegacyImportResult =
}
| { ok: false; error: string }
export async function appendLegacyTranscriptMessages(input: {
journal: AgentSessionJournal
agent: AgentType
sessionId: string
fence: number
messages: NativeChatMessage[]
}): Promise<number> {
let appended = 0
for (const message of input.messages) {
await input.journal.appendItem(
{
provider: 'legacy',
agent: input.agent,
sessionId: input.sessionId,
recordId: message.id
},
legacyItemBody(message, DEFAULT_JOURNAL_PAYLOAD_LIMITS),
{ fence: input.fence, observedAt: message.timestamp ?? undefined }
)
appended += 1
}
return appended
}
export async function importLegacyTranscriptIntoJournal(input: {
journal: AgentSessionJournal
agent: AgentType
@@ -3,10 +3,6 @@ import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import {
ClaudeTranscriptTailIncompleteError,
proveClaudeTranscriptBranch
} from '../claude/claude-transcript-branch-proof'
import { resolveSessionFilePath } from './session-file-resolver'
let tempRoots: string[] = []
@@ -16,16 +12,6 @@ afterEach(async () => {
tempRoots = []
})
async function readClaudeTranscriptLeafUuid(
transcriptPath: string,
providerSessionId: string,
previousLeafUuid: string | null = null
): Promise<string> {
return (
await proveClaudeTranscriptBranch({ transcriptPath, providerSessionId, previousLeafUuid })
).leafUuid
}
async function makeRoot(prefix: string): Promise<string> {
const root = await mkdtemp(join(tmpdir(), prefix))
tempRoots.push(root)
@@ -41,311 +27,6 @@ function restoreEnv(key: string, previous: string | undefined): void {
}
describe('resolveSessionFilePath', () => {
it('reads Claude last-prompt leaf metadata as the durable branch marker', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-leaf-')
const transcript = join(root, 'session.jsonl')
await writeFile(
transcript,
[
{ type: 'user', uuid: 'leaf-old', parentUuid: null, sessionId: 'session-1' },
{
type: 'assistant',
uuid: 'leaf-current',
parentUuid: 'leaf-old',
sessionId: 'session-1'
},
{ type: 'last-prompt', leafUuid: 'leaf-current', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(readClaudeTranscriptLeafUuid(transcript, 'session-1', 'leaf-old')).resolves.toBe(
'leaf-current'
)
})
it('fails closed when a Claude transcript has no branch marker', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-no-leaf-')
const transcript = join(root, 'session.jsonl')
await writeFile(
transcript,
'{"type":"assistant","uuid":"not-a-leaf","parentUuid":null,"sessionId":"session-1"}\n',
'utf8'
)
await expect(readClaudeTranscriptLeafUuid(transcript, 'session-1')).rejects.toThrow(
'missing last-prompt marker'
)
})
it('distinguishes an incomplete final Claude JSONL record from durable malformed content', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-torn-tail-')
const transcript = join(root, 'session.jsonl')
await writeFile(transcript, '{"type":"last-prompt"', 'utf8')
await expect(readClaudeTranscriptLeafUuid(transcript, 'session-1')).rejects.toBeInstanceOf(
ClaudeTranscriptTailIncompleteError
)
await writeFile(transcript, '{"type":"last-prompt"\n', 'utf8')
await expect(readClaudeTranscriptLeafUuid(transcript, 'session-1')).rejects.not.toBeInstanceOf(
ClaudeTranscriptTailIncompleteError
)
})
it('refuses a Claude marker on a sibling branch', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-sibling-')
const transcript = join(root, 'session.jsonl')
await writeFile(
transcript,
[
{ type: 'user', uuid: 'root', parentUuid: null, sessionId: 'session-1' },
{ type: 'assistant', uuid: 'expected', parentUuid: 'root', sessionId: 'session-1' },
{ type: 'system', uuid: 'sibling', parentUuid: 'root', sessionId: 'session-1' },
{ type: 'last-prompt', leafUuid: 'sibling', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(readClaudeTranscriptLeafUuid(transcript, 'session-1', 'expected')).rejects.toThrow(
'sibling branch'
)
})
it('refuses missing and cyclic Claude parent chains', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-invalid-ancestry-')
const missing = join(root, 'missing.jsonl')
const cycle = join(root, 'cycle.jsonl')
await writeFile(
missing,
[
{ type: 'user', uuid: 'expected', parentUuid: null, sessionId: 'session-1' },
{ type: 'assistant', uuid: 'leaf', parentUuid: 'absent', sessionId: 'session-1' },
{ type: 'last-prompt', leafUuid: 'leaf', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await writeFile(
cycle,
[
{ type: 'user', uuid: 'expected', parentUuid: null, sessionId: 'session-1' },
{ type: 'assistant', uuid: 'left', parentUuid: 'right', sessionId: 'session-1' },
{ type: 'system', uuid: 'right', parentUuid: 'left', sessionId: 'session-1' },
{ type: 'last-prompt', leafUuid: 'right', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(readClaudeTranscriptLeafUuid(missing, 'session-1', 'expected')).rejects.toThrow(
'missing ancestor absent'
)
await expect(readClaudeTranscriptLeafUuid(cycle, 'session-1', 'expected')).rejects.toThrow(
'cycle in parentUuid ancestry'
)
})
it('rejects non-transcript and sidechain UUIDs as the durable leaf', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-leaf-filter-')
const transcript = join(root, 'session.jsonl')
await writeFile(
transcript,
[
{ type: 'user', uuid: 'main-user', parentUuid: null, sessionId: 'session-1' },
{
type: 'assistant',
uuid: 'sidechain-assistant',
parentUuid: 'main-user',
sessionId: 'session-1',
isSidechain: true
},
{ type: 'result', uuid: 'result-frame', parentUuid: 'main-user', sessionId: 'session-1' },
{
type: 'system',
subtype: 'init',
uuid: 'init-frame',
parentUuid: null,
sessionId: 'session-1'
},
{ type: 'stream_event', uuid: 'stream-frame', parentUuid: null, sessionId: 'session-1' },
{ type: 'last-prompt', leafUuid: 'sidechain-assistant', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(readClaudeTranscriptLeafUuid(transcript, 'session-1')).rejects.toThrow(
'marker leaf is missing from the session graph'
)
})
it('rejects a main leaf whose ancestry crosses a subagent sidechain', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-sidechain-ancestry-')
const transcript = join(root, 'session.jsonl')
await writeFile(
transcript,
[
{ type: 'user', uuid: 'main-user', parentUuid: null, sessionId: 'session-1' },
{
type: 'assistant',
uuid: 'sidechain-assistant',
parentUuid: 'main-user',
sessionId: 'session-1',
isSidechain: true
},
{
type: 'assistant',
uuid: 'main-after-sidechain',
parentUuid: 'sidechain-assistant',
sessionId: 'session-1'
},
{ type: 'last-prompt', leafUuid: 'main-after-sidechain', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(readClaudeTranscriptLeafUuid(transcript, 'session-1')).rejects.toThrow(
'not on the main transcript'
)
})
it('rejects a main leaf whose ancestry crosses a parent-tool-use sidechain', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-parent-tool-ancestry-')
const transcript = join(root, 'transcript.jsonl')
await writeFile(
transcript,
[
{ type: 'user', uuid: 'main-user', parentUuid: null, sessionId: 'session-1' },
{
type: 'assistant',
uuid: 'subagent-assistant',
parentUuid: 'main-user',
sessionId: 'session-1',
parent_tool_use_id: 'tool-use-1'
},
{
type: 'assistant',
uuid: 'main-after-sidechain',
parentUuid: 'subagent-assistant',
sessionId: 'session-1'
},
{ type: 'last-prompt', leafUuid: 'main-after-sidechain', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(readClaudeTranscriptLeafUuid(transcript, 'session-1')).rejects.toThrow(
'not on the main transcript'
)
})
it('rejects a previous cursor descended from a parent-tool-use sidechain', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-parent-tool-cursor-')
const transcript = join(root, 'transcript.jsonl')
await writeFile(
transcript,
[
{ type: 'user', uuid: 'main-user', parentUuid: null, sessionId: 'session-1' },
{
type: 'assistant',
uuid: 'subagent-assistant',
parentUuid: 'main-user',
sessionId: 'session-1',
parent_tool_use_id: 'tool-use-1'
},
{
type: 'assistant',
uuid: 'main-after-sidechain',
parentUuid: 'subagent-assistant',
sessionId: 'session-1'
},
{ type: 'last-prompt', leafUuid: 'main-after-sidechain', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(
readClaudeTranscriptLeafUuid(transcript, 'session-1', 'main-after-sidechain')
).rejects.toThrow('not on the main transcript')
})
it('rejects a latest marker descended from a parent-tool-use cursor sidechain', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-parent-tool-cursor-descendant-')
const transcript = join(root, 'transcript.jsonl')
await writeFile(
transcript,
[
{ type: 'user', uuid: 'main-user', parentUuid: null, sessionId: 'session-1' },
{
type: 'assistant',
uuid: 'subagent-assistant',
parentUuid: 'main-user',
sessionId: 'session-1',
parent_tool_use_id: 'tool-use-1'
},
{
type: 'assistant',
uuid: 'main-after-sidechain',
parentUuid: 'subagent-assistant',
sessionId: 'session-1'
},
{
type: 'assistant',
uuid: 'latest-after-sidechain',
parentUuid: 'main-after-sidechain',
sessionId: 'session-1'
},
{ type: 'last-prompt', leafUuid: 'latest-after-sidechain', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(
readClaudeTranscriptLeafUuid(transcript, 'session-1', 'main-after-sidechain')
).rejects.toThrow('not on the main transcript')
})
it('rejects a post-snapshot descendant whose parent row was observed later', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-post-snapshot-')
const transcript = join(root, 'transcript.jsonl')
await writeFile(
transcript,
[
{
type: 'assistant',
uuid: 'descendant',
parentUuid: 'previous',
sessionId: 'session-1'
},
{ type: 'assistant', uuid: 'previous', parentUuid: null, sessionId: 'session-1' },
{ type: 'last-prompt', leafUuid: 'descendant', sessionId: 'session-1' }
]
.map((record) => JSON.stringify(record))
.join('\n'),
'utf8'
)
await expect(readClaudeTranscriptLeafUuid(transcript, 'session-1', 'previous')).rejects.toThrow(
'parent row follows descendant'
)
})
it('globs Claude project subdirs for <sessionId>.jsonl', async () => {
const root = await makeRoot('orca-native-chat-resolve-claude-')
const claudeProjectsDir = join(root, 'claude-projects')
@@ -6,7 +6,7 @@
// that ship. The fake app-server answers the same JSON-RPC calls the real one
// does and pushes the same notifications and blocking requests back.
import { mkdtemp, readFile, rm } from 'node:fs/promises'
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
@@ -31,7 +31,7 @@ import type {
import { attachFingerprintFields } from '../native-chat/agent-session-wire/structured-agent-session-attach'
import { getStructuredAgentSessionHost } from '../native-chat/agent-session-wire/structured-agent-session-registry'
import { journalDirectoryFor } from '../native-chat/agent-session-journal/journal-paths'
import { appendLegacyTranscriptMessages } from '../native-chat/agent-session-journal/journal-legacy-import'
import { importLegacyTranscriptIntoJournal } from '../native-chat/agent-session-journal/journal-legacy-import'
import type { AgentSessionJournal } from '../native-chat/agent-session-journal/journal-store'
import { createTrackedJournalOpener } from '../native-chat/agent-session-journal/journal-store-test-open'
import type { OrcaRuntimeService } from './orca-runtime'
@@ -407,20 +407,21 @@ describe('a structured codex session over agentSession.*', () => {
identity,
journalDir: journalDirectoryFor(root, identity)
})
await appendLegacyTranscriptMessages({
const rollout = join(root, 'legacy-rollout.jsonl')
await writeFile(
rollout,
`${JSON.stringify({
type: 'event_msg',
timestamp: '2026-08-05T10:00:02.000Z',
payload: { type: 'user_message', message: 'legacy question', kind: 'plain' }
})}\n`
)
await importLegacyTranscriptIntoJournal({
journal,
agent: 'codex',
sessionId: THREAD,
fence: 0,
messages: [
{
id: 'legacy-user-1',
role: 'user',
source: 'transcript',
timestamp: 1_800_000_000_000,
blocks: [{ type: 'text', text: 'legacy question' }]
}
]
options: { filePath: rollout }
})
const created = await ok<{ page: { items: AgentJournalRenderItem[] } }>(