Files
Meldwork/desktop/test/runs/run-ledger.test.cjs
T
Ryder Sun ec6674d96a feat: ship concurrent collaboration harness
Implement frozen concurrent responses, Agent-negotiated V4 discussion work, strict durable receipts and recovery, validated runtime phase UI, and the animated return-to-bottom control. Prepare the 0.1.2 prerelease metadata and documentation.\n\nChecks before commit: frontend 299/299; desktop 1270/1270; deterministic eval 6 cases/18 results; web and desktop renderer builds; desktop pack; staged and unstaged diff checks.
2026-08-17 11:35:32 +08:00

2547 lines
94 KiB
JavaScript

const test = require('node:test')
const assert = require('node:assert/strict')
const { createHash } = require('node:crypto')
const fs = require('node:fs')
const os = require('node:os')
const path = require('node:path')
const { RunLedger } = require('../../src/runs/run-ledger.cjs')
const {
appendHandoff,
createBlackboardEntryRecord,
emptyCollaborationState,
} = require('../../src/collaboration/collaboration-records.cjs')
const {
createCollaborationReceipt,
createCoordinationPlan,
createOrchestrationV4,
createSynthesisBinding,
createWorkReceipt,
hashValue,
} = require('../../src/collaboration/orchestration-v4-records.cjs')
const { createTaskGraph, createTaskGraphCursor } = require('../../src/collaboration/task-graph-records.cjs')
test('keeps the ledger facade limited to RunLedger', () => {
assert.deepEqual(Object.keys(require('../../src/runs/run-ledger.cjs')), ['RunLedger'])
})
function fixture(t) {
const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'meldwork-run-ledger-'))
t.after(() => fs.rmSync(directory, { recursive: true, force: true }))
return {
directory,
storagePath: path.join(directory, 'private', 'run-ledger.json'),
}
}
function discussionSnapshot(targetKinds, round = 0) {
const hash = createHash('sha256').update(JSON.stringify({ targetKinds, round })).digest('hex')
return {
contextPackId: null,
taskId: 'task-discussion',
round,
targetKinds: [...targetKinds],
sourceIds: ['task-discussion'],
capturedAt: 1000,
charCount: 123,
contentHash: hash,
bodyHash: createHash('sha256').update(JSON.stringify({ targetKinds, round, body: true })).digest('hex'),
contentRef: {
algorithm: 'sha256', hash, size: 123, mediaType: 'application/json',
},
}
}
function candidateCommitState({ runId, taskId, groupId, threadRootId, writerKind }) {
const candidateBody = 'Ledger candidate body.'
const candidateContentHash = createHash('sha256').update(candidateBody).digest('hex')
const candidateArtifactId = `artifact-${'5'.repeat(64)}`
const evidenceIds = [`evidence-${'7'.repeat(64)}`]
const writerRole = 'integrator'
const sinkId = (prefix, sink) => {
const body = JSON.stringify({ candidateContentHash, runId, sink, taskId })
return `${prefix}-${createHash('sha256').update(body).digest('hex')}`
}
const commitId = sinkId('candidate-commit', 'commit')
const blackboardEntry = createBlackboardEntryRecord({
entryType: 'artifact-ref', subject: `candidate-commit:${commitId}`,
statement: `Accepted candidate Artifact ${candidateArtifactId} `
+ `(sha256:${candidateContentHash}).`,
value: candidateContentHash,
owner: { type: 'agent', agentKind: writerKind, role: writerRole },
audience: { roles: [], agentKinds: ['codex', 'hermes', 'workbuddy'] },
lifecycle: { state: 'active', sequence: 1, recordedAt: 0, supersedesEntryId: null },
provenance: {
runId, taskId, round: 2, agentRunId: null,
artifactIds: [candidateArtifactId], evidenceIds,
},
refs: [candidateArtifactId, ...evidenceIds],
})
return {
status: 'intent', runId, taskId, groupId, threadRootId,
candidateArtifactId,
candidateContentHash,
candidateContentRef: {
algorithm: 'sha256', hash: candidateContentHash, size: 31, mediaType: 'text/plain',
},
evidenceIds,
writerKind, writerRole,
commitId,
messageId: sinkId('message', 'message'),
blackboardEntryId: blackboardEntry.entryId,
blackboardSequence: 1, blackboardRecordedAt: 0,
messageStatus: 'pending', blackboardStatus: 'pending',
attempt: 1, updatedAt: 1001,
}
}
function runRecord(runId, groupId, status = 'running', agentRuns = []) {
return {
runId,
taskId: `${runId}-task`,
contextPackId: `context-pack-${'a'.repeat(64)}`,
contextPackState: 'captured',
groupId,
threadRootId: `${groupId}-root`,
mode: 'manual',
targetKinds: ['codex'],
status,
permissionMode: 'read-only',
agentRuns,
}
}
function budgetSnapshot(overrides = {}) {
const dimensions = [
'inputTokens', 'outputTokens', 'costMicros', 'toolCalls', 'outboundBytes', 'elapsedMs',
]
return {
limits: Object.fromEntries(dimensions.map(dimension => [dimension, null])),
used: Object.fromEntries(dimensions.map(dimension => [dimension, 0])),
source: Object.fromEntries(dimensions.map(dimension => [dimension, 'unknown'])),
enforcement: Object.fromEntries(dimensions.map(dimension => [dimension, 'soft'])),
startedAt: 1000,
...overrides,
}
}
function attemptHistory() {
return [{
sequence: 1,
agentKind: 'hermes',
phase: 'initial',
attempt: 1,
failureCategory: 'authentication',
policyAction: 'retry',
backoffMs: 250,
recoveryAgentKind: '',
finalOutcome: 'failed',
outcomeCertainty: 'unknown_outcome',
sideEffectsPossible: true,
operationId: `agent-operation-${'a'.repeat(64)}`,
idempotencyMode: 'none',
timestamp: 1000,
}, {
sequence: 2,
agentKind: 'hermes',
phase: 'recovery_agent',
attempt: 1,
failureCategory: null,
policyAction: 'verify',
backoffMs: 250,
recoveryAgentKind: 'codex',
finalOutcome: 'succeeded',
sideEffectsPossible: false,
operationId: `agent-operation-${'b'.repeat(64)}`,
idempotencyMode: 'durable',
timestamp: 1100,
}]
}
test('loads exact v1 orchestration cursors and persists strict v2 collaboration state', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const baseCursor = {
workflow: 'auto',
currentKind: '',
pendingKinds: ['codex', 'hermes'],
activeKinds: ['codex', 'hermes'],
successfulKinds: [],
agreementKinds: [],
attachmentRecipients: [],
totalSuccesses: 0,
terminalFailureOccurred: false,
}
const legacy = ledger.checkpoint({
...runRecord('run-orchestration-v1', 'group-orchestration-v1'),
mode: 'auto',
targetKinds: ['codex', 'hermes'],
currentRound: 1,
maxRounds: 2,
orchestration: { version: 1, ...baseCursor },
})
assert.deepEqual(legacy.orchestration, { version: 1, ...baseCursor })
const collaboration = appendHandoff(emptyCollaborationState(), {
source: { type: 'harness' },
destination: { agentKind: 'codex', role: 'primary' },
objective: 'Assess the release.',
selectedEntryIds: [],
expectedOutput: 'One conclusion.',
acceptanceCriteria: ['Use selected state only.'],
provenance: {
runId: 'run-orchestration-v2',
taskId: 'run-orchestration-v2-task',
round: 1,
agentRunId: null,
artifactIds: [],
evidenceIds: [],
},
createdAt: 1000,
})
const current = ledger.checkpoint({
...runRecord('run-orchestration-v2', 'group-orchestration-v2'),
mode: 'auto',
targetKinds: ['codex', 'hermes'],
currentRound: 1,
maxRounds: 2,
orchestration: { version: 2, ...baseCursor, collaboration },
})
assert.deepEqual(current.orchestration.collaboration, collaboration)
const restarted = new RunLedger({ storagePath, now: () => 1100 })
assert.deepEqual(restarted.get(legacy.runId).orchestration, legacy.orchestration)
assert.deepEqual(restarted.get(current.runId).orchestration, current.orchestration)
assert.throws(() => ledger.checkpoint({
...runRecord('run-orchestration-invalid', 'group-orchestration-invalid'),
mode: 'auto',
targetKinds: ['codex', 'hermes'],
currentRound: 1,
maxRounds: 2,
orchestration: { version: 1, ...baseCursor, collaboration },
}), { message: 'RUN_LEDGER_RECORD_INVALID' })
})
test('persists strict v3 task-graph cursors without weakening v1 and v2 loading', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const graph = createTaskGraph({
template: 'task-graph',
nodes: [{
nodeId: 'primary-codex', role: 'primary', agentKind: 'codex',
dependsOn: [], inputNodeIds: [], expectedOutput: 'Produce a durable conclusion.',
acceptance: { requireConclusion: true, minArtifactRefs: 1, minEvidenceRefs: 1 },
terminal: true, parallel: false, decisionOptions: [],
}],
}, ['codex'])
const taskGraph = createTaskGraphCursor(graph, 1000)
const saved = ledger.checkpoint({
...runRecord('run-orchestration-v3', 'group-orchestration-v3'),
mode: 'auto',
currentRound: 0,
maxRounds: 2,
orchestration: {
version: 3,
workflow: 'auto',
template: 'task-graph',
currentKind: '',
pendingKinds: ['codex'],
activeKinds: ['codex'],
successfulKinds: [],
agreementKinds: [],
attachmentRecipients: [],
totalSuccesses: 0,
terminalFailureOccurred: false,
collaboration: emptyCollaborationState(),
taskGraph,
},
})
assert.equal(saved.orchestration.version, 3)
assert.deepEqual(saved.orchestration.taskGraph, taskGraph)
assert.deepEqual(
new RunLedger({ storagePath, now: () => 1100 }).get(saved.runId).orchestration,
saved.orchestration,
)
assert.throws(() => ledger.checkpoint({
runId: saved.runId,
orchestration: {
...saved.orchestration,
taskGraph: { ...taskGraph, terminalState: 'accepted' },
},
}), { message: 'RUN_LEDGER_RECORD_INVALID' })
})
test('persists strict v4 concurrent batch state and rejects mutated snapshot bindings', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const orchestration = createOrchestrationV4({
workflow: 'manual',
template: 'concurrent-batch',
targetKinds: ['codex', 'hermes'],
collaboration: emptyCollaborationState(),
}, { targetKinds: ['codex', 'hermes'], now: 1000 })
const saved = ledger.checkpoint({
...runRecord('run-orchestration-v4', 'group-orchestration-v4'),
targetKinds: ['codex', 'hermes'],
orchestration,
})
assert.equal(saved.orchestration.version, 4)
assert.deepEqual(saved.orchestration.currentKinds, ['codex', 'hermes'])
assert.equal(saved.orchestration.plan.snapshotHash, saved.orchestration.snapshotHash)
const restored = new RunLedger({ storagePath, now: () => 1100 })
assert.deepEqual(restored.get(saved.runId).orchestration, saved.orchestration)
assert.throws(() => ledger.checkpoint({
runId: saved.runId,
orchestration: {
...saved.orchestration,
slots: saved.orchestration.slots.map((slot, index) => index === 0
? { ...slot, snapshotHash: 'b'.repeat(64) }
: slot),
},
}), { message: 'RUN_LEDGER_RECORD_INVALID' })
assert.deepEqual(ledger.get(saved.runId).orchestration, saved.orchestration)
})
test('roundtrips Auto candidate commit state and rejects parent or sink tampering', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const targetKinds = ['codex', 'hermes', 'workbuddy']
const base = createOrchestrationV4({
workflow: 'auto', template: 'discussion', targetKinds, phase: 'coordination', round: 2,
snapshot: discussionSnapshot(targetKinds, 2),
}, { targetKinds, now: 1000 })
const coordinationPlan = createCoordinationPlan({
snapshotHash: base.snapshotHash,
targetKinds,
assignments: [
{ taskId: 'codex-work', ownerKind: 'codex', role: 'worker', objective: 'Codex work.', expectedOutput: 'Codex Artifact.', inputRefs: [], artifactIds: [], dependsOn: [] },
{ taskId: 'hermes-work', ownerKind: 'hermes', role: 'worker', objective: 'Hermes work.', expectedOutput: 'Hermes Artifact.', inputRefs: [], artifactIds: [], dependsOn: [] },
{ taskId: 'integrate-work', ownerKind: 'workbuddy', role: 'integrator', objective: 'Integrate work.', expectedOutput: 'Integrated Artifact.', inputRefs: [], artifactIds: [], dependsOn: ['codex-work', 'hermes-work'] },
],
finalizerKind: 'workbuddy', verifierKinds: ['codex', 'hermes'], agreedBy: targetKinds,
})
const runId = 'run-v4-candidate-commit'
const groupId = 'group-v4-candidate-commit'
const parent = runRecord(runId, groupId)
const candidateCommit = candidateCommitState({
runId, taskId: parent.taskId, groupId, threadRootId: parent.threadRootId,
writerKind: coordinationPlan.finalizerKind,
})
const orchestration = createOrchestrationV4({
...base,
phase: 'commit',
currentKinds: [], pendingKinds: [],
coordinationPlan,
commitState: { ...base.commitState, writerKind: coordinationPlan.finalizerKind },
candidateCommit,
}, { targetKinds, now: 1001 })
const saved = ledger.checkpoint({
...parent, mode: 'auto', targetKinds, currentRound: 2, maxRounds: 3, orchestration,
})
assert.deepEqual(saved.orchestration.candidateCommit, candidateCommit)
assert.deepEqual(
new RunLedger({ storagePath, now: () => 1100 }).get(runId).orchestration.candidateCommit,
candidateCommit,
)
for (const candidateCommitOverride of [
{ ...candidateCommit, taskId: 'different-task' },
{ ...candidateCommit, groupId: 'different-group' },
{ ...candidateCommit, threadRootId: 'different-thread' },
{ ...candidateCommit, messageId: `message-${'8'.repeat(64)}` },
]) {
assert.throws(() => ledger.checkpoint({
runId,
orchestration: { ...saved.orchestration, candidateCommit: candidateCommitOverride },
}))
}
})
test('preserves validated V4 recipient delivery acknowledgement through the Run Ledger', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const orchestration = createOrchestrationV4({
workflow: 'manual',
template: 'concurrent-batch',
targetKinds: ['codex', 'hermes'],
deliveryState: [{
recipientKind: 'codex',
sessionRefHash: '1'.repeat(64),
sessionProvenanceHash: '2'.repeat(64),
sourceAgentKind: 'hermes',
sourcePhase: 'proposal',
watermark: 3,
snapshotHash: '3'.repeat(64),
operationId: 'operation-hermes',
packageHash: '4'.repeat(64),
deliveryId: 'delivery-1',
status: 'acknowledged',
updatedAt: 1000,
}],
}, { targetKinds: ['codex', 'hermes'], now: 1000 })
const saved = ledger.checkpoint({
...runRecord('run-v4-delivery-state', 'group-v4-delivery-state'),
targetKinds: ['codex', 'hermes'],
orchestration,
})
assert.equal(orchestration.deliveryState?.length, 1)
assert.deepEqual(new RunLedger({ storagePath, now: () => 1100 }).get(saved.runId).orchestration.deliveryState,
orchestration.deliveryState)
})
test('restart retains same-Run delivery acknowledgements without sharing a concurrent Run state', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const targetKinds = ['codex', 'hermes', 'workbuddy', 'kimi', 'openclaw']
const sessionBindings = [
{ recipientKind: 'codex', sessionRefHash: '1'.repeat(64), sessionProvenanceHash: '2'.repeat(64) },
{ recipientKind: 'hermes', sessionRefHash: '3'.repeat(64), sessionProvenanceHash: '4'.repeat(64) },
]
const deliveryState = sessionBindings.flatMap(binding => targetKinds.map((sourceAgentKind) => ({
...binding,
sourceAgentKind,
sourcePhase: 'proposal',
watermark: 3,
snapshotHash: '5'.repeat(64),
operationId: `operation-wave-3-${sourceAgentKind}`,
packageHash: '6'.repeat(64),
deliveryId: `delivery-${binding.recipientKind}-${sourceAgentKind}-wave-3`,
status: 'acknowledged',
updatedAt: 1000,
}))).sort((left, right) => [
left.recipientKind, left.sessionRefHash, left.sessionProvenanceHash,
left.sourceAgentKind, left.sourcePhase,
].join('\u0000').localeCompare([
right.recipientKind, right.sessionRefHash, right.sessionProvenanceHash,
right.sourceAgentKind, right.sourcePhase,
].join('\u0000')))
const orchestration = createOrchestrationV4({
workflow: 'manual', template: 'concurrent-batch', targetKinds, deliveryState,
}, { targetKinds, now: 1000 })
const runA = ledger.checkpoint({
...runRecord('run-v4-restart-a', 'group-v4-restart-a'),
targetKinds,
orchestration,
})
const runB = ledger.checkpoint({
...runRecord('run-v4-restart-b', 'group-v4-restart-b'),
targetKinds,
orchestration: createOrchestrationV4({
workflow: 'manual', template: 'concurrent-batch', targetKinds,
}, { targetKinds, now: 1000 }),
})
const restarted = new RunLedger({ storagePath, now: () => 1100 })
assert.deepEqual(restarted.get(runA.runId).orchestration.deliveryState, deliveryState)
assert.equal(restarted.get(runB.runId).orchestration.deliveryState, undefined)
})
test('preserves validated V4 challenge bindings through the Run Ledger allowlist', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const targetKinds = ['codex', 'hermes']
const base = createOrchestrationV4({
workflow: 'auto', template: 'discussion', targetKinds, phase: 'challenge', round: 2,
snapshot: discussionSnapshot(targetKinds, 2),
}, { targetKinds, now: 1000 })
const slots = base.slots.map((slot) => {
const receipt = createCollaborationReceipt({
phase: 'proposal', agentKind: slot.agentKind, slotId: slot.slotId,
operationId: `operation-proposal-${slot.agentKind}`, status: 'completed',
summary: `${slot.agentKind} proposal`, artifactIds: [`artifact-${slot.agentKind}`],
evidenceIds: [`evidence-${slot.agentKind}`], snapshotHash: base.snapshotHash,
deliveryWatermark: 1,
})
return {
...slot,
resultRefs: {
artifactIds: [...receipt.artifactIds], evidenceIds: [...receipt.evidenceIds],
workflowOutcomeRefs: [{ receipt }],
},
}
})
const challengeBindings = slots.map((slot, index) => {
const proposalSlot = slots[1 - index]
const receipt = proposalSlot.resultRefs.workflowOutcomeRefs[0].receipt
return {
round: 2,
reviewerKind: slot.agentKind,
reviewerSlotId: slot.slotId,
reviewerOperationId: slot.operationId,
proposalKind: proposalSlot.agentKind,
proposalSlotId: proposalSlot.slotId,
proposalOperationId: receipt.operationId,
proposalReceiptId: receipt.receiptId,
artifactIds: [...receipt.artifactIds],
evidenceIds: [...receipt.evidenceIds],
}
})
const orchestration = { ...base, slots, challengeBindings }
const saved = ledger.checkpoint({
...runRecord('run-v4-challenge-bindings', 'group-v4-challenge-bindings'),
mode: 'auto', targetKinds, currentRound: 2, maxRounds: 3, orchestration,
})
assert.deepEqual(saved.orchestration.challengeBindings, challengeBindings)
assert.deepEqual(
new RunLedger({ storagePath, now: () => 1100 }).get(saved.runId).orchestration.challengeBindings,
challengeBindings,
)
})
test('roundtrips exact V4 work receipts and rejects tampered receipt hashes', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const targetKinds = ['codex', 'hermes', 'workbuddy']
const base = createOrchestrationV4({
workflow: 'auto', template: 'discussion', targetKinds, phase: 'coordination', round: 2,
snapshot: discussionSnapshot(targetKinds, 2),
}, { targetKinds, now: 1000 })
const coordinationPlan = createCoordinationPlan({
snapshotHash: base.snapshotHash,
targetKinds,
assignments: [
{ taskId: 'codex-work', ownerKind: 'codex', role: 'worker', objective: 'Codex work.', expectedOutput: 'Codex Artifact.', inputRefs: [], artifactIds: [], dependsOn: [] },
{ taskId: 'hermes-work', ownerKind: 'hermes', role: 'worker', objective: 'Hermes work.', expectedOutput: 'Hermes Artifact.', inputRefs: [], artifactIds: [], dependsOn: [] },
{ taskId: 'integrate-work', ownerKind: 'workbuddy', role: 'integrator', objective: 'Integrate work.', expectedOutput: 'Integrated Artifact.', inputRefs: [], artifactIds: [], dependsOn: ['codex-work', 'hermes-work'] },
],
finalizerKind: 'workbuddy',
verifierKinds: ['codex', 'hermes'],
agreedBy: targetKinds,
})
const slot = base.slots.find(candidate => candidate.agentKind === 'codex')
const artifactId = `artifact-${'a'.repeat(64)}`
const artifactHash = 'b'.repeat(64)
const collaborationReceipt = createCollaborationReceipt({
phase: 'work', agentKind: 'codex', slotId: slot.slotId,
operationId: slot.operationId, status: 'completed',
summary: 'Codex completed its negotiated work package.',
artifactIds: [artifactId], evidenceIds: [], workItemId: 'codex-work',
snapshotHash: base.snapshotHash, deliveryWatermark: 3,
})
const workReceipt = createWorkReceipt({
snapshotHash: base.snapshotHash,
snapshotBodyHash: base.snapshot.bodyHash,
snapshotContentRef: base.snapshot.contentRef,
planHash: coordinationPlan.planHash,
taskId: 'codex-work', ownerKind: 'codex', slotId: slot.slotId,
operationId: slot.operationId, collaborationReceipt,
artifacts: [{
artifactId,
contentHash: artifactHash,
contentRef: {
algorithm: 'sha256', hash: artifactHash, size: 42, mediaType: 'application/json',
},
}],
})
const orchestration = createOrchestrationV4({
...base,
phase: 'work',
currentKinds: ['hermes', 'workbuddy'],
pendingKinds: ['hermes', 'workbuddy'],
coordinationPlan,
workReceipts: [workReceipt],
commitState: { ...base.commitState, writerKind: coordinationPlan.finalizerKind },
slots: base.slots.map(candidate => candidate.slotId === slot.slotId ? {
...candidate,
phase: 'work',
status: 'completed',
finishedAt: 1001,
receiptId: collaborationReceipt.receiptId,
resultHash: hashValue(collaborationReceipt),
resultRefs: {
artifactIds: [artifactId],
evidenceIds: [],
workflowOutcomeRefs: [{ receipt: collaborationReceipt }],
},
} : { ...candidate, phase: 'work' }),
}, { targetKinds, now: 1001 })
const saved = ledger.checkpoint({
...runRecord('run-v4-work-receipt', 'group-v4-work-receipt'),
mode: 'auto', targetKinds, currentRound: 2, maxRounds: 3, orchestration,
})
assert.deepEqual(saved.orchestration.workReceipts, [workReceipt])
assert.deepEqual(
new RunLedger({ storagePath, now: () => 1100 }).get(saved.runId).orchestration.workReceipts,
[workReceipt],
)
assert.throws(() => ledger.checkpoint({
runId: saved.runId,
orchestration: {
...saved.orchestration,
workReceipts: [{ ...workReceipt, resultHash: 'f'.repeat(64) }],
},
}), { message: 'RUN_LEDGER_RECORD_INVALID' })
assert.deepEqual(ledger.get(saved.runId).orchestration.workReceipts, [workReceipt])
})
test('persists the V4 synthesis binding through journal recovery and restart', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const targetKinds = ['codex', 'hermes']
const base = createOrchestrationV4({
workflow: 'auto', template: 'discussion', targetKinds, phase: 'challenge', round: 2,
snapshot: discussionSnapshot(targetKinds, 2),
}, { targetKinds, now: 1000 })
const slots = base.slots.map((slot) => {
const proposalReceipt = createCollaborationReceipt({
phase: 'proposal', agentKind: slot.agentKind, slotId: slot.slotId,
operationId: `operation-proposal-${slot.agentKind}`, status: 'completed',
summary: `${slot.agentKind} proposal`, snapshotHash: base.snapshotHash,
deliveryWatermark: 1,
})
const challengeReceipt = createCollaborationReceipt({
phase: 'challenge', agentKind: slot.agentKind, slotId: slot.slotId,
operationId: slot.operationId, status: 'completed',
summary: `${slot.agentKind} challenge`, snapshotHash: base.snapshotHash,
deliveryWatermark: 2,
})
return {
...slot,
status: 'completed',
finishedAt: 1001,
resultRefs: {
artifactIds: [], evidenceIds: [],
workflowOutcomeRefs: [
{ receipt: proposalReceipt },
{ receipt: challengeReceipt, verdict: 'support' },
],
},
}
})
const challengeBindings = slots.map((slot, index) => {
const proposalSlot = slots[1 - index]
const proposalReceipt = proposalSlot.resultRefs.workflowOutcomeRefs[0].receipt
return {
round: 2,
reviewerKind: slot.agentKind,
reviewerSlotId: slot.slotId,
reviewerOperationId: slot.operationId,
proposalKind: proposalSlot.agentKind,
proposalSlotId: proposalSlot.slotId,
proposalOperationId: proposalReceipt.operationId,
proposalReceiptId: proposalReceipt.receiptId,
artifactIds: [],
evidenceIds: [],
}
})
const synthesisBinding = createSynthesisBinding({
snapshotContentHash: base.snapshot.bodyHash,
targetKinds,
candidates: [
{ kind: 'codex', score: 700 },
{ kind: 'hermes', score: 600 },
],
})
const saved = ledger.checkpoint({
...runRecord('run-v4-synthesis-binding', 'group-v4-synthesis-binding'),
mode: 'auto', targetKinds, currentRound: 2, maxRounds: 3,
orchestration: {
...base,
currentKinds: [],
pendingKinds: [],
slots,
challengeBindings,
synthesisBinding,
commitState: { ...base.commitState, writerKind: synthesisBinding.writerKind },
},
})
assert.deepEqual(saved.orchestration.synthesisBinding, synthesisBinding)
assert.deepEqual(
new RunLedger({ storagePath, now: () => 1100 }).get(saved.runId).orchestration.synthesisBinding,
synthesisBinding,
)
const writerKind = synthesisBinding.writerKind
const writerSlot = slots.find(slot => slot.agentKind === writerKind)
const operationId = `operation-synthesis-${writerKind}-1`
const rankedKinds = [writerKind, ...targetKinds.filter(kind => kind !== writerKind)]
const recovery = {
revision: 1,
originalWriterKind: writerKind,
activeWriterKind: writerKind,
verificationKinds: rankedKinds.filter(kind => kind !== writerKind).slice(0, 1),
rankedKinds,
rankingFingerprint: createHash('sha256').update(JSON.stringify({
rankedKinds,
selectionInputHash: synthesisBinding.selectionInputHash,
})).digest('hex'),
stateEpoch: 0,
triedWriters: [writerKind],
attempts: [{
attemptId: `synthesis-attempt-${writerKind}-1`,
writerKind,
slotId: writerSlot.slotId,
operationId,
attempt: 1,
status: 'intent',
permission: 'workspace-write',
leaseAcquired: false,
sideEffectsPossible: false,
outcomeCertainty: 'not_started',
updatedAt: 1001,
}],
}
const recoverySlots = slots.map(slot => slot.agentKind === writerKind
? { ...slot, phase: 'synthesis', status: 'planned', operationId, permission: 'workspace-write' }
: slot)
const recoveryPlan = {
...base.plan,
assignments: base.plan.assignments.map(assignment => assignment.agentKind === writerKind
? { ...assignment, operationId }
: assignment),
}
const recovered = ledger.checkpoint({
runId: saved.runId,
orchestration: {
...saved.orchestration,
phase: 'synthesis',
currentKind: '',
currentKinds: [writerKind],
pendingKinds: [writerKind],
plan: recoveryPlan,
slots: recoverySlots,
synthesisRecovery: recovery,
},
})
assert.deepEqual(recovered.orchestration.synthesisRecovery, recovery)
assert.deepEqual(
new RunLedger({ storagePath, now: () => 1200 }).get(saved.runId).orchestration.synthesisRecovery,
recovery,
)
})
test('preserves strict V4 convergence and Gate epoch through restart', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const targetKinds = ['codex', 'hermes']
const base = createOrchestrationV4({
workflow: 'auto', template: 'discussion', targetKinds, phase: 'challenge', round: 2,
snapshot: discussionSnapshot(targetKinds, 2),
}, { targetKinds, now: 1000 })
const slots = base.slots.map((slot) => {
const proposalReceipt = createCollaborationReceipt({
phase: 'proposal', agentKind: slot.agentKind, slotId: slot.slotId,
operationId: `operation-proposal-${slot.agentKind}`, status: 'completed',
summary: `${slot.agentKind} proposal`, snapshotHash: base.snapshotHash,
deliveryWatermark: 1,
})
const challengeReceipt = createCollaborationReceipt({
phase: 'challenge', agentKind: slot.agentKind, slotId: slot.slotId,
operationId: slot.operationId, status: 'completed',
summary: `${slot.agentKind} challenge`, snapshotHash: base.snapshotHash,
deliveryWatermark: 2,
})
return {
...slot,
status: 'completed',
finishedAt: 1001,
resultRefs: {
artifactIds: [], evidenceIds: [],
workflowOutcomeRefs: [
{ receipt: proposalReceipt },
{ receipt: challengeReceipt, verdict: 'support' },
],
},
}
})
const challengeBindings = slots.map((slot, index) => {
const proposalSlot = slots[1 - index]
const proposalReceipt = proposalSlot.resultRefs.workflowOutcomeRefs[0].receipt
return {
round: 2,
reviewerKind: slot.agentKind,
reviewerSlotId: slot.slotId,
reviewerOperationId: slot.operationId,
proposalKind: proposalSlot.agentKind,
proposalSlotId: proposalSlot.slotId,
proposalOperationId: proposalReceipt.operationId,
proposalReceiptId: proposalReceipt.receiptId,
artifactIds: [],
evidenceIds: [],
}
})
const synthesisBinding = createSynthesisBinding({
snapshotContentHash: base.snapshot.bodyHash,
targetKinds,
candidates: [
{ kind: 'codex', score: 700 },
{ kind: 'hermes', score: 600 },
],
})
const candidateContentHash = '2'.repeat(64)
const openIssueIds = [`issue-${'3'.repeat(64)}`]
const convergence = {
candidateArtifactId: `artifact-${'1'.repeat(64)}`,
candidateContentHash,
openIssueIds,
stateKey: createHash('sha256').update(JSON.stringify({
candidateContentHash,
openIssueIds,
})).digest('hex'),
lastCompletedRound: 2,
consecutiveStableRounds: 2,
stateEpoch: 7,
acknowledgedGateEpoch: 7,
}
const saved = ledger.checkpoint({
...runRecord('run-v4-convergence', 'group-v4-convergence'),
mode: 'auto', targetKinds, currentRound: 2, maxRounds: 3,
orchestration: {
...base,
currentKinds: [],
pendingKinds: [],
slots,
challengeBindings,
synthesisBinding,
commitState: { ...base.commitState, writerKind: synthesisBinding.writerKind },
convergence,
},
})
assert.deepEqual(saved.orchestration.convergence, convergence)
assert.deepEqual(
new RunLedger({ storagePath, now: () => 1100 }).get(saved.runId).orchestration.convergence,
convergence,
)
})
test('persists sanitized attempt history through journal recovery and restart', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1200 })
const saved = ledger.checkpoint({
...runRecord('run-attempt-history', 'group-attempt-history'),
attemptHistory: attemptHistory(),
})
assert.deepEqual(saved.attemptHistory, attemptHistory())
const serialized = fs.readFileSync(storagePath, 'utf8')
assert.doesNotMatch(serialized, /error|command|credential|Users|private-token/i)
fs.writeFileSync(storagePath, '{corrupt snapshot', 'utf8')
const restored = new RunLedger({ storagePath, now: () => 1300 })
assert.deepEqual(restored.get(saved.runId).attemptHistory, attemptHistory())
assert.equal(restored.snapshotError instanceof Error, true)
})
test('rejects malformed durable attempt history without changing the Run', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const saved = ledger.checkpoint({
...runRecord('run-attempt-invalid', 'group-attempt-invalid'),
attemptHistory: attemptHistory(),
})
for (const value of [
[{ ...attemptHistory()[0], phase: 'raw_retry' }],
[{ ...attemptHistory()[0], failureCategory: 'HTTP 401 private-token' }],
[{ ...attemptHistory()[0], outcomeCertainty: 'maybe' }],
[attemptHistory()[1], attemptHistory()[0]],
]) {
assert.throws(
() => ledger.checkpoint({ runId: saved.runId, attemptHistory: value }),
{ message: 'RUN_LEDGER_RECORD_INVALID' },
)
assert.deepEqual(ledger.get(saved.runId), saved)
}
const sanitized = ledger.checkpoint({
runId: saved.runId,
attemptHistory: [{
...attemptHistory()[0],
command: 'cat /Users/private/config',
error: 'Authorization: Bearer private-token',
}],
})
assert.equal('command' in sanitized.attemptHistory[0], false)
assert.equal('error' in sanitized.attemptHistory[0], false)
})
test('persists strict budget snapshots through the journal and restart', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const budget = budgetSnapshot({
limits: { ...budgetSnapshot().limits, inputTokens: 4000, toolCalls: 1 },
used: { ...budgetSnapshot().used, inputTokens: 750, toolCalls: 2 },
source: { ...budgetSnapshot().source, inputTokens: 'estimated', toolCalls: 'reported' },
enforcement: { ...budgetSnapshot().enforcement, inputTokens: 'hard', toolCalls: 'hard' },
exhaustion: {
dimension: 'toolCalls',
limit: 1,
priorUsed: 1,
attemptedUsage: 1,
used: 2,
source: 'reported',
enforcement: 'hard',
reason: 'BUDGET_LIMIT_EXCEEDED',
},
})
const saved = ledger.checkpoint({
...runRecord('run-budget', 'group-budget'),
budget,
})
assert.deepEqual(saved.budget, budget)
const updatedBudget = {
...budget,
used: { ...budget.used, inputTokens: 1000, elapsedMs: 250 },
source: { ...budget.source, elapsedMs: 'reported' },
}
ledger.checkpoint({ runId: saved.runId, status: 'waiting', budget: updatedBudget })
const restored = new RunLedger({ storagePath, now: () => 2000 })
assert.deepEqual(restored.get(saved.runId).budget, updatedBudget)
})
test('rejects malformed budget snapshots without changing the durable Run', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const saved = ledger.checkpoint({
...runRecord('run-budget-invalid', 'group-budget-invalid'),
budget: budgetSnapshot(),
})
for (const budget of [
{ ...budgetSnapshot(), secret: 'nope' },
{ ...budgetSnapshot(), used: { ...budgetSnapshot().used, inputTokens: -1 } },
{ ...budgetSnapshot(), source: { ...budgetSnapshot().source, costMicros: 'guessed' } },
{ ...budgetSnapshot(), enforcement: { ...budgetSnapshot().enforcement, toolCalls: 'warn' } },
{ ...budgetSnapshot(), limits: { inputTokens: null } },
]) {
assert.throws(
() => ledger.checkpoint({ runId: saved.runId, budget }),
{ message: 'RUN_LEDGER_RECORD_INVALID' },
)
assert.deepEqual(ledger.get(saved.runId), saved)
}
})
test('rejects new Runs without a captured Context Pack', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
assert.throws(
() => ledger.checkpoint({
...runRecord('run-no-context', 'group-no-context'),
contextPackId: '',
contextPackState: 'legacy-unavailable',
}),
{ message: 'RUN_LEDGER_RECORD_INVALID' },
)
assert.deepEqual(ledger.list(), [])
})
test('keeps captured Task and Context Pack bindings immutable across checkpoints', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const saved = ledger.checkpoint(runRecord('run-bound', 'group-bound'))
for (const patch of [
{ taskId: 'different-task' },
{ contextPackId: `context-pack-${'b'.repeat(64)}` },
{ contextPackState: 'legacy-unavailable' },
]) {
assert.throws(
() => ledger.checkpoint({ runId: saved.runId, ...patch }),
{ message: 'RUN_LEDGER_RECORD_INVALID' },
)
assert.deepEqual(ledger.get(saved.runId), saved)
}
const updated = ledger.checkpoint({ runId: saved.runId, status: 'waiting' })
assert.equal(updated.taskId, saved.taskId)
assert.equal(updated.contextPackId, saved.contextPackId)
assert.equal(updated.contextPackState, 'captured')
})
test('restart reconciliation downgrades missing Context Packs without losing delivery provenance', (t) => {
const { storagePath } = fixture(t)
const baseContextPackId = `context-pack-${'a'.repeat(64)}`
const attemptContextPackId = `context-pack-${'b'.repeat(64)}`
const deliveryRecordId = `delivery-record-${'c'.repeat(64)}`
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const saved = ledger.checkpoint({
...runRecord('run-missing-pack', 'group-missing-pack', 'running', [{
agentRunId: 'agent-missing-pack',
kind: 'codex',
status: 'running',
context: {
contextPackId: attemptContextPackId,
deliveryRecordIds: [deliveryRecordId],
},
}]),
contextPackId: baseContextPackId,
})
const [reconciled] = ledger.reconcileContextPacks(() => {
throw new Error('CONTEXT_PACK_NOT_FOUND')
})
assert.equal(reconciled.contextPackId, '')
assert.equal(reconciled.contextPackState, 'legacy-unavailable')
assert.equal(reconciled.agentRuns[0].context.contextPackId, undefined)
assert.equal(reconciled.agentRuns[0].context.contextPackState, 'legacy-unavailable')
assert.deepEqual(reconciled.agentRuns[0].context.deliveryRecordIds, [deliveryRecordId])
assert.equal(saved.contextPackState, 'captured')
const restarted = new RunLedger({ storagePath, now: () => 2000 })
assert.equal(restarted.get(saved.runId).contextPackState, 'legacy-unavailable')
})
test('restart reconciliation isolates a missing attempt Pack from a valid Run Pack', (t) => {
const { storagePath } = fixture(t)
const baseContextPackId = `context-pack-${'a'.repeat(64)}`
const attemptContextPackId = `context-pack-${'b'.repeat(64)}`
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const saved = ledger.checkpoint({
...runRecord('run-missing-attempt', 'group-missing-attempt', 'running', [{
agentRunId: 'agent-missing-attempt',
kind: 'codex',
status: 'running',
context: { contextPackId: attemptContextPackId },
}]),
contextPackId: baseContextPackId,
})
ledger.reconcileContextPacks(contextPackId => {
if (contextPackId === baseContextPackId) {
return { contextPackId, taskId: saved.taskId }
}
throw new Error('CONTEXT_PACK_TAMPERED')
})
const reconciled = ledger.get(saved.runId)
assert.equal(reconciled.contextPackId, baseContextPackId)
assert.equal(reconciled.contextPackState, 'captured')
assert.equal(reconciled.agentRuns[0].context.contextPackId, undefined)
assert.equal(reconciled.agentRuns[0].context.contextPackState, 'legacy-unavailable')
})
test('restart reconciliation downgrades a Context Pack assigned to another Task', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const saved = ledger.checkpoint(runRecord('run-wrong-pack-task', 'group-wrong-pack-task'))
ledger.reconcileContextPacks(contextPackId => ({
contextPackId,
taskId: 'another-task',
}))
const reconciled = ledger.get(saved.runId)
assert.equal(reconciled.contextPackId, '')
assert.equal(reconciled.contextPackState, 'legacy-unavailable')
})
test('Context Pack reconciliation fails closed when its downgrade cannot persist', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const saved = ledger.checkpoint(runRecord('run-pack-write-failure', 'group-pack-write-failure'))
failNextPersist(ledger)
assert.throws(
() => ledger.reconcileContextPacks(() => {
throw new Error('CONTEXT_PACK_TAMPERED')
}),
{ message: 'RUN_LEDGER_WRITE_FAILED' },
)
assert.deepEqual(ledger.get(saved.runId), saved)
})
function storedTraceRecord(
runId = 'run-stored', groupId = 'group-stored', agentRunId = 'agent-stored',
) {
return {
...runRecord(runId, groupId, 'completed', [{
agentRunId,
kind: 'codex',
round: 1,
status: 'completed',
output: 'Done',
events: [{
runId,
agentRunId,
groupId,
threadRootId: `${groupId}-root`,
agentKind: 'codex',
round: 1,
seq: 1,
timestamp: 1000,
status: 'completed',
type: 'warning',
summary: 'Summary',
}],
sourceMessageIds: [`${runId}-message`],
startedAt: 1000,
lastActivityAt: 1000,
finishedAt: 1000,
silent: false,
truncated: false,
context: {
includedCount: 1,
omittedCount: 0,
charCount: 100,
sessionRotated: false,
},
}]),
mode: 'auto',
permissionMode: 'read-only',
createdAt: 1000,
startedAt: 1000,
updatedAt: 1000,
finishedAt: 1000,
currentRound: 1,
maxRounds: 2,
unlimitedRounds: false,
}
}
function failNextPersist(ledger) {
const persist = ledger.persist.bind(ledger)
let failed = false
ledger.persist = (runs) => {
if (!failed) {
failed = true
throw new Error('RUN_LEDGER_WRITE_FAILED')
}
return persist(runs)
}
}
function journalEntries(storagePath) {
return fs.readFileSync(`${storagePath}.journal`, 'utf8')
.trim()
.split('\n')
.filter(Boolean)
.map(line => JSON.parse(line))
}
test('roundtrips sanitized bounded run and Agent snapshots', (t) => {
const { storagePath } = fixture(t)
const events = Array.from({ length: 85 }, (_, index) => ({
id: `event-${index}`,
type: 'warning',
status: 'waiting',
seq: index + 1,
timestamp: 1100 + index,
title: `Warning ${index}`,
summary: `Summary ${index}`,
command: `cat /Users/private/${index}`,
}))
const sourceMessageIds = Array.from({ length: 40 }, (_, index) => `message-${index}`)
const extraAgentRuns = Array.from({ length: 260 }, (_, index) => ({
agentRunId: `extra-agent-${index}`,
kind: 'codex',
status: 'completed',
}))
const ledger = new RunLedger({ storagePath, now: () => 2000 })
const saved = ledger.checkpoint({
runId: 'run-1',
taskId: 'task-1',
contextPackId: `context-pack-${'a'.repeat(64)}`,
contextPackState: 'captured',
groupId: 'group-1',
threadRootId: 'root-1',
mode: 'auto',
targetKinds: ['codex', 'hermes', 'codex', '../../private'],
status: 'running',
startedAt: '1970-01-01T00:00:01.000Z',
permissionMode: 'workspace-write',
currentRound: 12,
maxRounds: 8,
arbitrary: 'drop-me',
agentRuns: [...extraAgentRuns, {
agentRunId: 'agent-1',
kind: 'codex',
round: 2,
status: 'running',
output: `Final output ${'x'.repeat(21000)}`,
events,
sourceMessageIds,
startedAt: 1050,
lastActivityAt: 1200,
silent: true,
context: {
includedCount: 2000,
omittedCount: -4,
charCount: 2000000,
sessionRotated: true,
externalRunRef: 'ocr+review:123',
},
seenSeqs: [1, 2, 3],
}],
})
assert.deepEqual(saved.targetKinds, ['codex', 'hermes'])
assert.equal(saved.taskId, 'task-1')
assert.equal(saved.currentRound, 8)
assert.equal(saved.startedAt, 1000)
assert.equal('arbitrary' in saved, false)
assert.equal(saved.agentRuns.length, 256)
const boundedAgent = saved.agentRuns.at(-1)
assert.equal(boundedAgent.output.length, 20000)
assert.equal(boundedAgent.events.length, 80)
assert.equal(boundedAgent.events[0].id, 'event-5')
assert.equal(boundedAgent.sourceMessageIds.length, 32)
assert.deepEqual(boundedAgent.context, {
includedCount: 1000,
omittedCount: 0,
charCount: 1000000,
sessionRotated: true,
externalRunRef: 'ocr+review:123',
})
assert.equal(boundedAgent.truncated, true)
assert.equal('seenSeqs' in boundedAgent, false)
const restored = new RunLedger({ storagePath, now: () => 3000 })
assert.deepEqual(restored.get('run-1'), saved)
const detached = restored.list()
detached[0].agentRuns.at(-1).context.charCount = 1
assert.equal(restored.get('run-1').agentRuns.at(-1).context.charCount, 1000000)
})
test('loads equivalent Session provenance regardless of stored field order', (t) => {
const { storagePath } = fixture(t)
const contextPackId = `context-pack-${'a'.repeat(64)}`
const deliveryRecordId = `delivery-record-${'b'.repeat(64)}`
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const saved = ledger.checkpoint({
...runRecord('run-provenance-order', 'group-provenance-order', 'completed', [{
agentRunId: 'agent-provenance-order',
kind: 'codex',
status: 'completed',
context: {
contextMode: 'continuation',
promptChars: 1200,
promptBytes: 1280,
promptHash: 'c'.repeat(64),
sourceCount: 3,
sourceHash: 'd'.repeat(64),
wirePayloadBytes: 1500,
wirePayloadHash: 'e'.repeat(64),
contextPackId,
deliveryRecordIds: [deliveryRecordId],
sessionProvenance: {
scope: 'conversation',
reuse: true,
origin: 'resumed',
originTaskId: 'task-origin',
inheritedTaskIds: ['task-inherited'],
completeness: 'complete',
},
},
}]),
contextPackId,
})
fs.unlinkSync(ledger.journalPath)
const stored = JSON.parse(fs.readFileSync(storagePath, 'utf8'))
stored.runs[0].agentRuns[0].context.sessionProvenance = {
completeness: 'complete',
inheritedTaskIds: ['task-inherited'],
originTaskId: 'task-origin',
origin: 'resumed',
reuse: true,
scope: 'conversation',
}
fs.writeFileSync(storagePath, `${JSON.stringify(stored, null, 2)}\n`)
const restored = new RunLedger({ storagePath, now: () => 2000 })
assert.equal(restored.loadError, null)
assert.deepEqual(restored.get(saved.runId), saved)
})
test('merges sliding live Agent snapshots into durable history across restart', (t) => {
const { storagePath } = fixture(t)
let now = 1000
const ledger = new RunLedger({ storagePath, now: () => now })
const attempts = Array.from({ length: 65 }, (_, index) => ({
agentRunId: `agent-${index + 1}`,
kind: 'codex',
round: index + 1,
status: 'completed',
output: `Output ${index + 1}`,
}))
const first = ledger.checkpoint(runRecord(
'run-sliding', 'group-sliding', 'running', attempts.slice(0, 64),
))
assert.deepEqual(
first.agentRuns.map(agentRun => agentRun.agentRunId),
attempts.slice(0, 64).map(attempt => attempt.agentRunId),
)
now = 2000
const nextSnapshot = attempts.slice(1).map(attempt => (
attempt.agentRunId === 'agent-32'
? { ...attempt, status: 'failed', output: 'Fresh terminal output', reason: 'updated' }
: attempt
))
const second = ledger.checkpoint(runRecord(
'run-sliding', 'group-sliding', 'running', nextSnapshot,
))
assert.equal(second.agentRuns.length, 65)
assert.equal(second.agentRuns[0].agentRunId, 'agent-1')
assert.equal(second.agentRuns.at(-1).agentRunId, 'agent-65')
const updated = second.agentRuns.find(agentRun => agentRun.agentRunId === 'agent-32')
assert.equal(updated.status, 'failed')
assert.equal(updated.output, 'Fresh terminal output')
assert.equal(updated.reason, 'updated')
const restored = new RunLedger({ storagePath, now: () => 3000 }).get('run-sliding')
assert.deepEqual(restored.agentRuns, second.agentRuns)
})
test('writer normalizes invalid Agent and event statuses before restart', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const saved = ledger.checkpoint(runRecord('run-status-domains', 'group-status-domains', 'running', [{
agentRunId: 'agent-status-domains',
kind: 'codex',
status: 'round-limit',
events: [{
type: 'warning',
status: 'preparing',
seq: 1,
summary: 'Waiting for valid status normalization.',
}],
}]))
assert.equal(saved.agentRuns[0].status, 'running')
assert.equal(saved.agentRuns[0].events[0].status, 'running')
const restored = new RunLedger({ storagePath, now: () => 2000 })
assert.equal(restored.loadError, null)
assert.deepEqual(restored.get('run-status-domains'), saved)
})
test('rejects paths, credentials, session references, raw commands, and arbitrary reasoning fields', (t) => {
const { storagePath } = fixture(t)
const googleKey = 'AIza12345678901234567890123456789012345'
const jwt = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'
const basicCredential = 'dXNlcjpwYXNz'
const legitimateText = 'Basic authentication is enabled. customer.platform.internal deadbeef.cafebabe.feedface'
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const saved = ledger.checkpoint({
runId: 'run-secure',
taskId: 'task-secure',
contextPackId: `context-pack-${'a'.repeat(64)}`,
contextPackState: 'captured',
groupId: 'group-secure',
targetKinds: ['codex'],
status: 'running',
reason: `token=run-secret /Users/alice/project ${googleKey} ${jwt}`,
executable: '/Applications/Agent.app',
sessionRef: 'session-ref-secret',
credentials: { apiKey: 'root-secret' },
privateChainOfThought: 'hidden-run-reasoning',
agentRuns: [{
agentRunId: 'agent-secure',
kind: 'codex',
status: 'running',
output: `Bearer abcdefghijklmnop Authorization: Basic ${basicCredential} /Users/alice/private-output https://url-user:[email protected] ${googleKey} ${jwt} ${legitimateText}`,
command: 'cat /Users/alice/private-command',
sessionRef: 'agent-session-secret',
privateReasoning: 'hidden-agent-reasoning',
events: [{
id: 'tool-1',
type: 'tool_result_summary',
status: 'completed',
seq: 1,
title: '/Users/alice/result',
summary: `apiKey=event-secret ${googleKey} ${jwt} ${legitimateText}`,
detail: 'Output: 2 lines, 30 bytes from /Users/alice/result',
command: 'rg secret /Users/alice',
chainOfThought: 'hidden-event-reasoning',
}],
}],
})
const serialized = fs.readFileSync(storagePath, 'utf8')
for (const forbidden of [
'/Users/alice', '/Applications/Agent.app', 'run-secret', 'root-secret',
'session-ref-secret', 'agent-session-secret', 'event-secret', 'url-user', 'url-pass',
'private-command', 'hidden-run-reasoning', 'hidden-agent-reasoning',
'hidden-event-reasoning', googleKey, jwt, basicCredential,
]) {
assert.doesNotMatch(serialized, new RegExp(forbidden.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')))
}
assert.match(saved.reason, /\[redacted\]/)
assert.match(saved.reason, /\[path\]/)
assert.match(saved.agentRuns[0].output, /Bearer \[redacted\]/)
assert.match(saved.agentRuns[0].output, new RegExp(legitimateText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')))
assert.match(saved.agentRuns[0].events[0].summary, new RegExp(legitimateText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')))
assert.equal('command' in saved.agentRuns[0].events[0], false)
assert.equal('detail' in saved.agentRuns[0].events[0], false)
})
test('writes atomically with private file and directory permissions', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
ledger.checkpoint(runRecord('run-1', 'group-1'))
ledger.checkpoint({ runId: 'run-1', currentRound: 1 })
ledger.checkpoint(runRecord('run-2', 'group-1'))
assert.equal(fs.statSync(storagePath).mode & 0o777, 0o600)
assert.equal(fs.statSync(ledger.journalPath).mode & 0o777, 0o600)
assert.equal(fs.statSync(path.dirname(storagePath)).mode & 0o777, 0o700)
assert.deepEqual(fs.readdirSync(path.dirname(storagePath)).sort(), [
'run-ledger.json', 'run-ledger.json.journal',
])
assert.equal(JSON.parse(fs.readFileSync(storagePath, 'utf8')).version, 1)
assert.deepEqual(ledger.list().map(record => record.runId), ['run-2', 'run-1'])
})
test('append-only journal recovers lifecycle state from a corrupt snapshot', (t) => {
const { storagePath } = fixture(t)
let now = 1000
const ledger = new RunLedger({ storagePath, now: () => now })
ledger.checkpoint({
...runRecord('run-journal', 'group-journal', 'running', [{
agentRunId: 'agent-journal',
kind: 'codex',
round: 1,
status: 'running',
output: 'Private in-progress output must stay out of the journal.',
events: [{
id: 'tool-1', type: 'tool_update', status: 'running', seq: 18, timestamp: 1000,
summary: 'Inspected local state.',
}],
}]),
mode: 'auto',
currentRound: 1,
maxRounds: 2,
})
now = 2000
ledger.checkpoint({
runId: 'run-journal',
currentRound: 1,
agentRuns: [{
agentRunId: 'agent-journal', kind: 'codex', round: 1, status: 'waiting',
output: 'Private in-progress output must stay out of the journal.',
events: [{
id: 'tool-1', type: 'tool_update', status: 'waiting', seq: 24, timestamp: 2000,
summary: 'Waiting for the tool result.',
}],
}],
})
const journal = fs.readFileSync(ledger.journalPath, 'utf8')
assert.doesNotMatch(journal, /Private in-progress output/)
assert.match(journal, /"eventCursor":24/)
fs.writeFileSync(storagePath, '{corrupt snapshot', 'utf8')
const restored = new RunLedger({ storagePath, now: () => 3000 })
const recovered = restored.get('run-journal')
assert.equal(restored.loadError, null)
assert.equal(restored.snapshotError instanceof Error, true)
assert.equal(recovered.status, 'running')
assert.equal(recovered.currentRound, 1)
assert.equal(recovered.agentRuns[0].status, 'waiting')
assert.equal(recovered.agentRuns[0].eventCursor, 24)
assert.equal(
recovered.agentRuns[0].outputChars,
'Private in-progress output must stay out of the journal.'.length,
)
assert.equal(recovered.agentRuns[0].output, '')
})
test('Outcome references survive checkpoint, journal recovery, and restart', (t) => {
const { storagePath } = fixture(t)
const artifactId = `artifact-${'a'.repeat(64)}`
const evidenceId = `evidence-${'b'.repeat(64)}`
const reviewerFindingId = `reviewer-finding-${'c'.repeat(64)}`
const adoptionId = `adoption-${'d'.repeat(64)}`
const workflowOutcomeRef = {
algorithm: 'sha256',
hash: 'e'.repeat(64),
size: 128,
mediaType: 'application/json',
}
const ledger = new RunLedger({ storagePath, now: () => 1000 })
ledger.checkpoint(runRecord('run-outcomes', 'group-outcomes', 'completed', [{
agentRunId: 'agent-outcomes',
kind: 'codex',
status: 'completed',
context: {
outcomeRefs: {
artifactIds: [artifactId],
evidenceIds: [evidenceId],
reviewerFindingIds: [reviewerFindingId],
adoptionIds: [adoptionId],
workflowOutcomeRefs: [workflowOutcomeRef],
},
},
}]))
const expected = {
artifactIds: [artifactId],
evidenceIds: [evidenceId],
reviewerFindingIds: [reviewerFindingId],
adoptionIds: [adoptionId],
workflowOutcomeRefs: [workflowOutcomeRef],
}
assert.deepEqual(ledger.get('run-outcomes').agentRuns[0].context.outcomeRefs, expected)
assert.match(fs.readFileSync(ledger.journalPath, 'utf8'), new RegExp(artifactId))
fs.writeFileSync(storagePath, '{corrupt snapshot', 'utf8')
const restored = new RunLedger({ storagePath, now: () => 2000 })
assert.deepEqual(restored.get('run-outcomes').agentRuns[0].context.outcomeRefs, expected)
assert.equal(restored.snapshotError instanceof Error, true)
})
test('committed journal lifecycle wins over a valid stale snapshot', (t) => {
const { storagePath } = fixture(t)
let now = 1000
const ledger = new RunLedger({ storagePath, now: () => now })
ledger.checkpoint({
...runRecord('run-stale', 'group-stale', 'running', [{
agentRunId: 'agent-stale',
kind: 'codex',
round: 1,
status: 'running',
output: 'Snapshot detail before the lifecycle advance.',
events: [{
type: 'tool_update', status: 'running', seq: 1, timestamp: 1000,
summary: 'Older snapshot detail.',
}],
}]),
mode: 'auto',
currentRound: 1,
maxRounds: 2,
})
const staleSnapshot = fs.readFileSync(storagePath, 'utf8')
now = 2000
ledger.checkpoint({
runId: 'run-stale',
status: 'waiting',
agentRuns: [{
agentRunId: 'agent-stale',
kind: 'codex',
round: 1,
status: 'waiting',
output: 'Newer snapshot detail.',
events: [{
type: 'tool_update', status: 'waiting', seq: 2, timestamp: 2000,
summary: 'Newer lifecycle detail.',
}],
}],
})
fs.writeFileSync(storagePath, staleSnapshot, 'utf8')
const restored = new RunLedger({ storagePath, now: () => 3000 })
const recovered = restored.get('run-stale')
assert.equal(restored.loadError, null)
assert.equal(restored.snapshotError, null)
assert.equal(recovered.status, 'waiting')
assert.equal(recovered.agentRuns[0].status, 'waiting')
assert.equal(recovered.agentRuns[0].eventCursor, 2)
assert.equal(recovered.agentRuns[0].output, 'Snapshot detail before the lifecycle advance.')
assert.equal(recovered.agentRuns[0].events[0].summary, 'Older snapshot detail.')
})
test('does not mix snapshot detail from an uncommitted lifecycle transaction', (t) => {
const { storagePath } = fixture(t)
let now = 1000
const ledger = new RunLedger({ storagePath, now: () => now })
ledger.checkpoint({
...runRecord('run-prepare-crash', 'group-prepare-crash', 'running', [{
agentRunId: 'agent-prepare-crash',
kind: 'codex',
round: 1,
status: 'running',
output: 'Committed running detail.',
events: [{
type: 'tool_update', status: 'running', seq: 1, timestamp: 1000,
summary: 'Committed running event.',
}],
}]),
mode: 'auto',
currentRound: 1,
maxRounds: 2,
})
now = 2000
const persist = ledger.persist.bind(ledger)
let persistCalls = 0
ledger.persist = (runs) => {
persistCalls += 1
if (persistCalls === 1) return persist(runs)
throw new Error('SIMULATED_CRASH_BEFORE_SNAPSHOT_ROLLBACK')
}
ledger.journal.commit = () => {
throw new Error('SIMULATED_CRASH_BEFORE_JOURNAL_COMMIT')
}
assert.throws(
() => ledger.checkpoint({
runId: 'run-prepare-crash',
agentRuns: [{
agentRunId: 'agent-prepare-crash',
kind: 'codex',
round: 1,
status: 'waiting',
output: 'Uncommitted waiting detail.',
events: [{
type: 'tool_update', status: 'waiting', seq: 2, timestamp: 2000,
summary: 'Uncommitted waiting event.',
}],
}],
}),
{ message: 'RUN_LEDGER_WRITE_FAILED' },
)
const uncommittedSnapshot = JSON.parse(fs.readFileSync(storagePath, 'utf8')).runs[0]
assert.equal(uncommittedSnapshot.status, 'running')
assert.equal(uncommittedSnapshot.agentRuns[0].status, 'waiting')
assert.equal(uncommittedSnapshot.agentRuns[0].output, 'Uncommitted waiting detail.')
assert.equal(journalEntries(storagePath).at(-1).phase, 'prepare')
const restored = new RunLedger({ storagePath, now: () => 3000 })
const recovered = restored.get('run-prepare-crash')
assert.equal(restored.loadError, null)
assert.equal(recovered.status, 'running')
assert.equal(recovered.agentRuns[0].status, 'running')
assert.equal(recovered.agentRuns[0].eventCursor, 1)
assert.equal(recovered.agentRuns[0].output, '')
assert.deepEqual(recovered.agentRuns[0].events, [])
})
test('detail-only checkpoints do not grow the lifecycle journal', (t) => {
const { storagePath } = fixture(t)
let now = 1000
const ledger = new RunLedger({ storagePath, now: () => now })
ledger.checkpoint(runRecord('run-bounded-journal', 'group-bounded-journal', 'running', [{
agentRunId: 'agent-bounded-journal',
kind: 'codex',
status: 'running',
output: 'first detail',
}]))
const initialEntryCount = journalEntries(storagePath).length
now = 2000
ledger.checkpoint({
runId: 'run-bounded-journal',
agentRuns: [{
agentRunId: 'agent-bounded-journal',
kind: 'codex',
status: 'running',
output: 'second detail in the same four kilobyte bucket',
}],
})
assert.equal(journalEntries(storagePath).length, initialEntryCount)
assert.equal(
JSON.parse(fs.readFileSync(storagePath, 'utf8')).runs[0].agentRuns[0].output,
'second detail in the same four kilobyte bucket',
)
assert.equal(
new RunLedger({ storagePath, now: () => 2500 })
.get('run-bounded-journal').agentRuns[0].output,
'second detail in the same four kilobyte bucket',
)
now = 3000
ledger.checkpoint({
runId: 'run-bounded-journal',
agentRuns: [{
agentRunId: 'agent-bounded-journal',
kind: 'codex',
status: 'running',
output: 'x'.repeat(4096),
}],
})
assert.equal(journalEntries(storagePath).length, initialEntryCount + 2)
})
test('journal retains completed attempts beyond the display snapshot limit', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const attempts = Array.from({ length: 256 }, (_, index) => ({
agentRunId: `agent-history-${index + 1}`,
kind: 'codex',
round: index + 1,
status: 'completed',
}))
ledger.checkpoint(runRecord(
'run-history-journal', 'group-history-journal', 'running', attempts,
))
ledger.checkpoint(runRecord('run-history-journal', 'group-history-journal', 'running', [{
agentRunId: 'agent-history-257',
kind: 'codex',
round: 257,
status: 'completed',
}]))
const snapshot = JSON.parse(fs.readFileSync(storagePath, 'utf8')).runs[0]
const journal = fs.readFileSync(`${storagePath}.journal`, 'utf8')
assert.equal(snapshot.agentRuns.length, 256)
assert.equal(snapshot.agentRuns.some(agentRun => agentRun.agentRunId === 'agent-history-1'), false)
assert.match(journal, /"agentRunId":"agent-history-1"/)
assert.match(journal, /"agentRunId":"agent-history-257"/)
})
test('journal ignores and repairs one truncated final append', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
ledger.checkpoint(runRecord('run-before-tail', 'group-tail'))
fs.appendFileSync(ledger.journalPath, '{"version":1,"sequence":999')
const restored = new RunLedger({ storagePath, now: () => 2000 })
assert.equal(restored.loadError, null)
assert.equal(restored.get('run-before-tail').runId, 'run-before-tail')
assert.equal(fs.readFileSync(restored.journalPath, 'utf8').endsWith('\n'), true)
restored.checkpoint(runRecord('run-after-tail', 'group-tail'))
assert.deepEqual(new RunLedger({ storagePath }).list().map(record => record.runId), [
'run-after-tail', 'run-before-tail',
])
})
test('journal failure prevents snapshot and in-memory mutation', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
ledger.checkpoint(runRecord('run-stable', 'group-stable'))
const beforeMemory = ledger.list()
const beforeDisk = fs.readFileSync(storagePath, 'utf8')
const append = ledger.journal.append.bind(ledger.journal)
let failed = false
ledger.journal.append = (entry) => {
if (!failed) {
failed = true
throw new Error('JOURNAL_APPEND_FAILED')
}
return append(entry)
}
assert.throws(
() => ledger.checkpoint(runRecord('run-blocked', 'group-blocked')),
{ message: 'RUN_LEDGER_WRITE_FAILED' },
)
assert.deepEqual(ledger.list(), beforeMemory)
assert.equal(fs.readFileSync(storagePath, 'utf8'), beforeDisk)
assert.equal(ledger.checkpoint(runRecord('run-blocked', 'group-blocked')).runId, 'run-blocked')
assert.equal(new RunLedger({ storagePath }).get('run-blocked').status, 'running')
})
test('journal rejects raw execution data and unknown lifecycle fields', (t) => {
const { directory, storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
ledger.checkpoint(runRecord('run-journal-fields', 'group-journal-fields', 'running', [{
agentRunId: 'agent-journal-fields', kind: 'codex', status: 'running',
}]))
const baselineEntries = journalEntries(storagePath)
const prepareIndex = baselineEntries.findIndex(entry => (
entry.phase === 'prepare' && entry.change?.upserts?.[0]?.agentRuns?.length
))
assert.notEqual(prepareIndex, -1)
const cases = [
['output', entry => { entry.change.upserts[0].agentRuns[0].output = 'raw output' }],
['events', entry => { entry.change.upserts[0].agentRuns[0].events = [] }],
['command', entry => { entry.change.upserts[0].agentRuns[0].command = 'run tool' }],
['unknown-agent', entry => { entry.change.upserts[0].agentRuns[0].privateState = true }],
['unknown-context', entry => {
entry.change.upserts[0].agentRuns[0].context.privateState = true
}],
]
for (const [index, [name, mutate]] of cases.entries()) {
const entries = JSON.parse(JSON.stringify(baselineEntries))
mutate(entries[prepareIndex])
const candidatePath = path.join(directory, `${name}.json`)
fs.copyFileSync(storagePath, candidatePath)
fs.writeFileSync(
`${candidatePath}.journal`,
`${entries.map(entry => JSON.stringify(entry)).join('\n')}\n`,
'utf8',
)
const restored = new RunLedger({ storagePath: candidatePath, now: () => 2000 })
assert.deepEqual(restored.list(), [], name)
assert.equal(restored.loadError instanceof Error, true, name)
assert.throws(
() => restored.checkpoint(runRecord(`run-${index}`, 'group-new')),
{ message: 'RUN_LEDGER_LOAD_FAILED' },
name,
)
}
})
test('remote recovery reattaches with a durable job id and cursor', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
ledger.checkpoint({
...runRecord('run-remote', 'group-remote', 'running', [{
agentRunId: 'agent-remote', kind: 'codex', round: 1, status: 'running',
}]),
remoteJob: {
connectorId: 'mock.remote',
jobId: 'job-123',
cursor: 'cursor-7',
},
})
const restarted = new RunLedger({ storagePath, now: () => 2000 })
const changed = restarted.recoverInterrupted({
remoteConnectorIds: ['mock.remote'],
recoveryOwnerId: 'desktop-recovery',
})
assert.equal(changed[0].status, 'reconciling')
assert.equal(changed[0].agentRuns[0].status, 'running')
const [claim] = restarted.remoteRecoveries('desktop-recovery')
assert.deepEqual(claim, {
runId: 'run-remote',
taskId: 'run-remote-task',
groupId: 'group-remote',
connectorId: 'mock.remote',
jobId: 'job-123',
cursor: 'cursor-7',
})
for (const status of ['preparing', 'queued', 'reconciling']) {
assert.throws(
() => restarted.reconcileRemote(
'run-remote', 'desktop-recovery', { status },
),
{ message: 'RUN_LEDGER_REMOTE_UPDATE_INVALID' },
status,
)
}
const mockConnector = ({ jobId, cursor, terminal = false }) => {
assert.equal(jobId, 'job-123')
return terminal
? { cursor: 'cursor-9', status: 'completed' }
: { cursor: 'cursor-8', status: 'running' }
}
const running = restarted.reconcileRemote(
'run-remote', 'desktop-recovery', mockConnector(claim),
)
assert.equal(running.status, 'running')
assert.equal(running.remoteJob.cursor, 'cursor-8')
assert.equal(running.remoteJob.recoveryOwnerId, 'desktop-recovery')
assert.equal(
restarted.remoteRecoveries('desktop-recovery')[0].cursor,
'cursor-8',
)
const remoteResult = mockConnector({
...claim,
cursor: running.remoteJob.cursor,
terminal: true,
})
const completed = restarted.reconcileRemote(
'run-remote', 'desktop-recovery', remoteResult,
)
assert.equal(completed.status, 'completed')
assert.equal(completed.remoteJob.cursor, 'cursor-9')
assert.equal(completed.remoteJob.recoveryOwnerId, '')
assert.equal(completed.agentRuns[0].status, 'completed')
const restored = new RunLedger({ storagePath, now: () => 3000 }).get('run-remote')
assert.equal(restored.status, 'completed')
assert.equal(restored.remoteJob.jobId, 'job-123')
assert.equal(restored.remoteJob.cursor, 'cursor-9')
})
test('finish preserves Agent snapshots and records a terminal reason', (t) => {
const { storagePath } = fixture(t)
let now = 1000
const ledger = new RunLedger({ storagePath, now: () => now })
const active = ledger.checkpoint(runRecord('run-1', 'group-1', 'running', [{
agentRunId: 'agent-1',
kind: 'codex',
status: 'completed',
output: 'Done',
context: { includedCount: 2, omittedCount: 1, charCount: 200 },
}]))
now = 2000
const finished = ledger.finish('run-1', 'completed', 'user')
assert.equal(finished.status, 'completed')
assert.equal(finished.reason, 'user')
assert.equal(finished.finishedAt, 2000)
assert.deepEqual(finished.agentRuns, active.agentRuns)
})
test('recovers interrupted runs and Agent attempts once', (t) => {
const { storagePath } = fixture(t)
let now = 1000
const ledger = new RunLedger({ storagePath, now: () => now })
ledger.checkpoint(runRecord('run-active', 'group-1', 'preparing', [{
agentRunId: 'agent-running',
kind: 'codex',
status: 'running',
context: { includedCount: 3, omittedCount: 2, charCount: 400 },
}, {
agentRunId: 'agent-complete',
kind: 'codex',
status: 'completed',
}]))
ledger.checkpoint(runRecord('run-terminal', 'group-2', 'completed', [{
agentRunId: 'agent-waiting',
kind: 'codex',
status: 'waiting',
context: { includedCount: 1, omittedCount: 0, charCount: 50 },
}]))
now = 5000
const restored = new RunLedger({ storagePath, now: () => now })
const changed = restored.recoverInterrupted()
assert.equal(changed.length, 2)
const active = restored.get('run-active')
assert.equal(active.status, 'interrupted')
assert.equal(active.reason, 'app_restart')
assert.equal(active.agentRuns[0].status, 'interrupted')
assert.equal(active.agentRuns[0].reason, 'app_restart')
assert.deepEqual(active.agentRuns[0].context, {
includedCount: 3, omittedCount: 2, charCount: 400,
})
assert.equal(active.agentRuns[1].status, 'completed')
const terminal = restored.get('run-terminal')
assert.equal(terminal.status, 'completed')
assert.equal(terminal.agentRuns[0].status, 'interrupted')
assert.equal(terminal.agentRuns[0].reason, 'app_restart')
const persisted = fs.readFileSync(storagePath, 'utf8')
assert.deepEqual(restored.recoverInterrupted(), [])
assert.equal(fs.readFileSync(storagePath, 'utf8'), persisted)
})
test('retention evicts the oldest terminal record before active records', (t) => {
const { storagePath } = fixture(t)
let now = 0
const ledger = new RunLedger({ storagePath, maxRuns: 3, now: () => now })
const add = (runId, status) => {
now += 10
ledger.checkpoint(runRecord(runId, 'group-1', status))
}
add('active-old', 'running')
add('terminal-old', 'completed')
add('terminal-new', 'failed')
add('active-new', 'running')
assert.deepEqual(ledger.list().map(record => record.runId), [
'active-new', 'terminal-new', 'active-old',
])
add('active-next', 'running')
assert.deepEqual(ledger.list().map(record => record.runId), [
'active-next', 'active-new', 'terminal-new', 'active-old',
])
add('active-last', 'running')
assert.deepEqual(ledger.list().map(record => record.runId), [
'active-last', 'active-next', 'active-new', 'terminal-new', 'active-old',
])
assert.deepEqual(
new RunLedger({ storagePath, maxRuns: 3 }).list().map(record => record.runId),
['active-last', 'active-next', 'active-new', 'terminal-new', 'active-old'],
)
ledger.finish('active-last', 'completed')
assert.deepEqual(ledger.list().map(record => record.runId), [
'active-last', 'active-next', 'active-new', 'active-old',
])
assert.equal(ledger.get('active-last').status, 'completed')
assert.equal(new RunLedger({ storagePath, maxRuns: 3 }).get('active-last').status, 'completed')
})
test('deleteGroup removes only matching records and persists the result', (t) => {
const { storagePath } = fixture(t)
let now = 0
const ledger = new RunLedger({ storagePath, now: () => ++now })
ledger.checkpoint(runRecord('run-a1', 'group-a'))
ledger.checkpoint(runRecord('run-b1', 'group-b'))
ledger.checkpoint(runRecord('run-a2', 'group-a'))
assert.equal(ledger.deleteGroup('group-a'), 2)
assert.deepEqual(ledger.list('group-a'), [])
assert.deepEqual(ledger.list().map(record => record.runId), ['run-b1'])
assert.deepEqual(
new RunLedger({ storagePath }).list().map(record => record.runId),
['run-b1'],
)
})
test('checkpoint remains retryable when persistence fails', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
ledger.checkpoint(runRecord('run-1', 'group-1'))
const beforeMemory = ledger.list()
const beforeDisk = fs.readFileSync(storagePath, 'utf8')
const next = runRecord('run-2', 'group-2')
failNextPersist(ledger)
assert.throws(() => ledger.checkpoint(next), { message: 'RUN_LEDGER_WRITE_FAILED' })
assert.deepEqual(ledger.list(), beforeMemory)
assert.equal(fs.readFileSync(storagePath, 'utf8'), beforeDisk)
assert.equal(ledger.checkpoint(next).runId, 'run-2')
assert.deepEqual(new RunLedger({ storagePath }).list().map(record => record.runId), [
'run-2', 'run-1',
])
})
test('finish remains retryable when persistence fails', (t) => {
const { storagePath } = fixture(t)
let now = 1000
const ledger = new RunLedger({ storagePath, now: () => now })
ledger.checkpoint(runRecord('run-1', 'group-1'))
const beforeMemory = ledger.list()
const beforeDisk = fs.readFileSync(storagePath, 'utf8')
now = 2000
failNextPersist(ledger)
assert.throws(
() => ledger.finish('run-1', 'completed', 'done'),
{ message: 'RUN_LEDGER_WRITE_FAILED' },
)
assert.deepEqual(ledger.list(), beforeMemory)
assert.equal(fs.readFileSync(storagePath, 'utf8'), beforeDisk)
assert.equal(ledger.finish('run-1', 'completed', 'done').status, 'completed')
assert.equal(new RunLedger({ storagePath }).get('run-1').status, 'completed')
})
test('interruption recovery remains retryable without mutating nested Agent state', (t) => {
const { storagePath } = fixture(t)
let now = 1000
const ledger = new RunLedger({ storagePath, now: () => now })
ledger.checkpoint(runRecord('run-1', 'group-1', 'running', [{
agentRunId: 'agent-1',
kind: 'codex',
status: 'running',
output: 'Work in progress',
}]))
const beforeMemory = ledger.list()
const beforeDisk = fs.readFileSync(storagePath, 'utf8')
now = 2000
failNextPersist(ledger)
assert.throws(() => ledger.recoverInterrupted(), { message: 'RUN_LEDGER_WRITE_FAILED' })
assert.deepEqual(ledger.list(), beforeMemory)
assert.equal(ledger.get('run-1').agentRuns[0].status, 'running')
assert.equal(fs.readFileSync(storagePath, 'utf8'), beforeDisk)
assert.equal(ledger.recoverInterrupted()[0].status, 'interrupted')
const restored = new RunLedger({ storagePath }).get('run-1')
assert.equal(restored.status, 'interrupted')
assert.equal(restored.agentRuns[0].status, 'interrupted')
})
test('group deletion remains retryable when persistence fails', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
ledger.checkpoint(runRecord('run-a', 'group-a'))
ledger.checkpoint(runRecord('run-b', 'group-b'))
const beforeMemory = ledger.list()
const beforeDisk = fs.readFileSync(storagePath, 'utf8')
failNextPersist(ledger)
assert.throws(() => ledger.deleteGroup('group-a'), { message: 'RUN_LEDGER_WRITE_FAILED' })
assert.deepEqual(ledger.list(), beforeMemory)
assert.equal(fs.readFileSync(storagePath, 'utf8'), beforeDisk)
assert.equal(ledger.deleteGroup('group-a'), 1)
assert.deepEqual(new RunLedger({ storagePath }).list().map(record => record.runId), ['run-b'])
})
test('accepts bounded Unicode group identifiers without relaxing run identifiers', (t) => {
const { storagePath } = fixture(t)
const ledger = new RunLedger({ storagePath, now: () => 1000 })
const groupId = '历史群聊 1'
assert.equal(ledger.checkpoint(runRecord('run-unicode', groupId)).groupId, groupId)
assert.equal(ledger.list(groupId)[0].groupId, groupId)
assert.throws(
() => ledger.checkpoint(runRecord('运行-1', groupId)),
{ message: 'RUN_LEDGER_RECORD_INVALID' },
)
assert.throws(
() => ledger.checkpoint(runRecord('run-control', 'group\ninvalid')),
{ message: 'RUN_LEDGER_RECORD_INVALID' },
)
assert.equal(ledger.deleteGroup(groupId), 1)
})
test('corrupt or malformed stores stay read-only without deleting unrelated data', (t) => {
const { directory, storagePath } = fixture(t)
fs.mkdirSync(path.dirname(storagePath), { recursive: true })
const sibling = path.join(path.dirname(storagePath), 'keep.txt')
fs.writeFileSync(sibling, 'keep', 'utf8')
for (const [name, contents] of [
['corrupt', '{not-json'],
['malformed', JSON.stringify({ version: 1, runs: {} })],
['invalid-record', JSON.stringify({ version: 1, runs: [{ runId: 'run-bad' }] })],
['invalid-target-kinds', JSON.stringify({
version: 1,
runs: [{ ...runRecord('run-bad-kinds', 'group-bad'), targetKinds: {} }],
})],
['invalid-target-kind-element', JSON.stringify({
version: 1,
runs: [{ ...runRecord('run-bad-kind-element', 'group-bad'), targetKinds: [null] }],
})],
['invalid-target-kind-value', JSON.stringify({
version: 1,
runs: [{ ...runRecord('run-bad-kind-value', 'group-bad'), targetKinds: ['../codex'] }],
})],
['invalid-agent-runs', JSON.stringify({
version: 1,
runs: [{ ...runRecord('run-bad-agents', 'group-bad'), agentRuns: {} }],
})],
['invalid-agent-run-element', JSON.stringify({
version: 1,
runs: [{ ...runRecord('run-bad-agent-element', 'group-bad'), agentRuns: [null] }],
})],
['invalid-agent-run-record', JSON.stringify({
version: 1,
runs: [{ ...runRecord('run-bad-agent-record', 'group-bad'), agentRuns: [{}] }],
})],
['invalid-agent-events', JSON.stringify({
version: 1,
runs: [runRecord('run-bad-events', 'group-bad', 'running', [{
agentRunId: 'agent-bad-events',
kind: 'codex',
events: {},
}])],
})],
['invalid-agent-event-element', JSON.stringify({
version: 1,
runs: [runRecord('run-bad-event-element', 'group-bad', 'running', [{
agentRunId: 'agent-bad-event-element',
kind: 'codex',
events: [null],
}])],
})],
['invalid-agent-event-record', JSON.stringify({
version: 1,
runs: [runRecord('run-bad-event-record', 'group-bad', 'running', [{
agentRunId: 'agent-bad-event-record',
kind: 'codex',
events: [{}],
}])],
})],
['invalid-agent-source-ids', JSON.stringify({
version: 1,
runs: [runRecord('run-bad-source-ids', 'group-bad', 'running', [{
agentRunId: 'agent-bad-source-ids',
kind: 'codex',
sourceMessageIds: {},
}])],
})],
['invalid-agent-source-id-element', JSON.stringify({
version: 1,
runs: [runRecord('run-bad-source-id-element', 'group-bad', 'running', [{
agentRunId: 'agent-bad-source-id-element',
kind: 'codex',
sourceMessageIds: [null],
}])],
})],
['invalid-agent-source-id-value', JSON.stringify({
version: 1,
runs: [runRecord('run-bad-source-id-value', 'group-bad', 'running', [{
agentRunId: 'agent-bad-source-id-value',
kind: 'codex',
sourceMessageIds: ['../message'],
}])],
})],
['invalid-agent-context', JSON.stringify({
version: 1,
runs: [runRecord('run-bad-context', 'group-bad', 'running', [{
agentRunId: 'agent-bad-context',
kind: 'codex',
context: [],
}])],
})],
]) {
const candidatePath = name === 'corrupt'
? storagePath
: path.join(directory, `${name}.json`)
fs.writeFileSync(candidatePath, contents, 'utf8')
const ledger = new RunLedger({ storagePath: candidatePath, now: () => 1000 })
assert.deepEqual(ledger.list(), [])
assert.equal(ledger.loadError instanceof Error, true)
for (const mutation of [
() => ledger.checkpoint(runRecord('run-new', 'group-new')),
() => ledger.finish('missing', 'failed'),
() => ledger.recoverInterrupted(),
() => ledger.deleteGroup('missing'),
]) {
assert.throws(mutation, { message: 'RUN_LEDGER_LOAD_FAILED' })
}
assert.equal(fs.readFileSync(candidatePath, 'utf8'), contents)
assert.equal(fs.readFileSync(sibling, 'utf8'), 'keep')
}
})
test('rejects coercible persisted scalar containers at every nested level', (t) => {
const { directory, storagePath } = fixture(t)
fs.mkdirSync(path.dirname(storagePath), { recursive: true })
const baseline = {
...runRecord('run-scalars', 'group-scalars', 'completed', [{
agentRunId: 'agent-scalars',
kind: 'codex',
round: 1,
status: 'completed',
output: 'Done',
events: [{
runId: 'run-scalars',
agentRunId: 'agent-scalars',
groupId: 'group-scalars',
threadRootId: 'group-scalars-root',
agentKind: 'codex',
round: 1,
seq: 1,
timestamp: 1000,
status: 'completed',
type: 'warning',
id: 'event-scalars',
title: 'Warning',
summary: 'Summary',
detail: 'Result: 1 item',
delta: 'legacy delta',
}],
sourceMessageIds: ['message-scalars'],
startedAt: 1000,
lastActivityAt: 1000,
finishedAt: 1000,
silent: false,
truncated: false,
reason: 'done',
context: {
includedCount: 1,
omittedCount: 0,
charCount: 100,
sessionRotated: false,
},
}]),
mode: 'auto',
permissionMode: 'read-only',
createdAt: 1000,
startedAt: 1000,
updatedAt: 1000,
finishedAt: 1000,
currentRound: 1,
maxRounds: 2,
unlimitedRounds: false,
reason: 'done',
}
const scalarPaths = [
['runId'], ['taskId'], ['groupId'], ['threadRootId'], ['mode'], ['status'], ['reason'],
['permissionMode'], ['createdAt'], ['startedAt'], ['updatedAt'], ['finishedAt'],
['currentRound'], ['maxRounds'], ['unlimitedRounds'],
['agentRuns', 0, 'agentRunId'], ['agentRuns', 0, 'kind'],
['agentRuns', 0, 'round'], ['agentRuns', 0, 'status'],
['agentRuns', 0, 'output'], ['agentRuns', 0, 'startedAt'],
['agentRuns', 0, 'lastActivityAt'], ['agentRuns', 0, 'finishedAt'],
['agentRuns', 0, 'silent'], ['agentRuns', 0, 'truncated'],
['agentRuns', 0, 'reason'],
['agentRuns', 0, 'events', 0, 'runId'],
['agentRuns', 0, 'events', 0, 'agentRunId'],
['agentRuns', 0, 'events', 0, 'groupId'],
['agentRuns', 0, 'events', 0, 'threadRootId'],
['agentRuns', 0, 'events', 0, 'agentKind'],
['agentRuns', 0, 'events', 0, 'round'],
['agentRuns', 0, 'events', 0, 'seq'],
['agentRuns', 0, 'events', 0, 'timestamp'],
['agentRuns', 0, 'events', 0, 'status'],
['agentRuns', 0, 'events', 0, 'type'],
['agentRuns', 0, 'events', 0, 'id'],
['agentRuns', 0, 'events', 0, 'title'],
['agentRuns', 0, 'events', 0, 'summary'],
['agentRuns', 0, 'events', 0, 'detail'],
['agentRuns', 0, 'events', 0, 'delta'],
['agentRuns', 0, 'context', 'includedCount'],
['agentRuns', 0, 'context', 'omittedCount'],
['agentRuns', 0, 'context', 'charCount'],
['agentRuns', 0, 'context', 'sessionRotated'],
]
const scalarCases = [
...scalarPaths.map(scalarPath => [scalarPath, value => [value]]),
...[
['reason'],
['agentRuns', 0, 'output'],
['agentRuns', 0, 'events', 0, 'summary'],
['agentRuns', 0, 'context', 'includedCount'],
].map(scalarPath => [scalarPath, value => ({ value })]),
]
for (const [index, [scalarPath, corrupt]] of scalarCases.entries()) {
const record = JSON.parse(JSON.stringify(baseline))
let container = record
for (const field of scalarPath.slice(0, -1)) container = container[field]
const field = scalarPath.at(-1)
container[field] = corrupt(container[field])
const candidatePath = index === 0
? storagePath
: path.join(directory, `scalar-${index}.json`)
const contents = JSON.stringify({ version: 1, runs: [record] })
fs.writeFileSync(candidatePath, contents, 'utf8')
const ledger = new RunLedger({ storagePath: candidatePath, now: () => 2000 })
assert.equal(ledger.loadError instanceof Error, true, scalarPath.join('.'))
assert.throws(
() => ledger.checkpoint(runRecord('run-new', 'group-new')),
{ message: 'RUN_LEDGER_LOAD_FAILED' },
scalarPath.join('.'),
)
assert.equal(fs.readFileSync(candidatePath, 'utf8'), contents)
}
})
test('rejects persisted enums and bounded numbers that normalization would change', (t) => {
const { directory, storagePath } = fixture(t)
fs.mkdirSync(path.dirname(storagePath), { recursive: true })
const cases = [
['mode', record => { record.mode = 'bogus' }],
['run-status', record => { record.status = 'bogus' }],
['permission', record => { record.permissionMode = 'bogus' }],
['manual-rounds', record => { record.mode = 'manual' }],
['unlimited-max', record => { record.unlimitedRounds = true }],
['round-over-max', record => { record.maxRounds = 1; record.currentRound = 2 }],
['current-negative', record => { record.currentRound = -1 }],
['current-fractional', record => { record.currentRound = 1.5 }],
['current-overflow', record => { record.currentRound = 100001 }],
['max-negative', record => { record.maxRounds = -1 }],
['max-overflow', record => { record.maxRounds = 100001 }],
['remote-job-container', record => { record.remoteJob = [] }],
['remote-connector', record => {
record.remoteJob = { connectorId: '../remote', jobId: 'job-1' }
}],
['remote-job-id', record => {
record.remoteJob = { connectorId: 'mock.remote', jobId: 'job id' }
}],
['remote-cursor', record => {
record.remoteJob = { connectorId: 'mock.remote', jobId: 'job-1', cursor: 'bad cursor' }
}],
['remote-owner', record => {
record.remoteJob = {
connectorId: 'mock.remote', jobId: 'job-1', recoveryOwnerId: '../owner',
}
}],
['remote-unknown', record => {
record.remoteJob = { connectorId: 'mock.remote', jobId: 'job-1', secret: 'nope' }
}],
['agent-status', record => { record.agentRuns[0].status = 'bogus' }],
['agent-run-only-status', record => { record.agentRuns[0].status = 'round-limit' }],
['agent-preparing-status', record => { record.agentRuns[0].status = 'preparing' }],
['agent-round-negative', record => { record.agentRuns[0].round = -1 }],
['agent-round-overflow', record => { record.agentRuns[0].round = 100001 }],
['event-cursor-negative', record => { record.agentRuns[0].eventCursor = -1 }],
['event-cursor-fractional', record => { record.agentRuns[0].eventCursor = 1.5 }],
['event-cursor-overflow', record => { record.agentRuns[0].eventCursor = 1000000001 }],
['output-chars-negative', record => { record.agentRuns[0].outputChars = -1 }],
['output-chars-fractional', record => { record.agentRuns[0].outputChars = 1.5 }],
['output-chars-overflow', record => { record.agentRuns[0].outputChars = 1000001 }],
['event-status', record => { record.agentRuns[0].events[0].status = 'bogus' }],
['event-run-only-status', record => { record.agentRuns[0].events[0].status = 'round-limit' }],
['event-preparing-status', record => { record.agentRuns[0].events[0].status = 'preparing' }],
['event-round-negative', record => { record.agentRuns[0].events[0].round = -1 }],
['event-round-overflow', record => { record.agentRuns[0].events[0].round = 100001 }],
['event-seq-zero', record => { record.agentRuns[0].events[0].seq = 0 }],
['event-seq-overflow', record => { record.agentRuns[0].events[0].seq = 1000000001 }],
['included-negative', record => { record.agentRuns[0].context.includedCount = -1 }],
['included-overflow', record => { record.agentRuns[0].context.includedCount = 1001 }],
['omitted-negative', record => { record.agentRuns[0].context.omittedCount = -1 }],
['omitted-overflow', record => { record.agentRuns[0].context.omittedCount = 100001 }],
['chars-negative', record => { record.agentRuns[0].context.charCount = -1 }],
['chars-overflow', record => { record.agentRuns[0].context.charCount = 1000001 }],
['source-count-negative', record => { record.agentRuns[0].context.sourceCount = -1 }],
['prompt-bytes-overflow', record => { record.agentRuns[0].context.promptBytes = 10000001 }],
['wire-bytes-fractional', record => { record.agentRuns[0].context.wirePayloadBytes = 1.5 }],
['source-hash-invalid', record => { record.agentRuns[0].context.sourceHash = 'invalid' }],
['prompt-hash-invalid', record => { record.agentRuns[0].context.promptHash = 'invalid' }],
['wire-hash-invalid', record => { record.agentRuns[0].context.wirePayloadHash = 'invalid' }],
]
for (const [index, [name, mutate]] of cases.entries()) {
const record = storedTraceRecord()
mutate(record)
const candidatePath = index === 0 ? storagePath : path.join(directory, `${name}.json`)
const contents = JSON.stringify({ version: 1, runs: [record] })
fs.writeFileSync(candidatePath, contents, 'utf8')
const ledger = new RunLedger({ storagePath: candidatePath, now: () => 2000 })
assert.equal(ledger.loadError instanceof Error, true, name)
assert.throws(
() => ledger.checkpoint(runRecord('run-new', 'group-new')),
{ message: 'RUN_LEDGER_LOAD_FAILED' },
name,
)
assert.equal(fs.readFileSync(candidatePath, 'utf8'), contents, name)
}
})
test('rejects explicit event provenance that disagrees with its parent', (t) => {
const { directory, storagePath } = fixture(t)
fs.mkdirSync(path.dirname(storagePath), { recursive: true })
const cases = [
['runId', 'run-other'],
['agentRunId', 'agent-other'],
['groupId', 'group-other'],
['threadRootId', 'root-other'],
['agentKind', 'hermes'],
['round', 2],
]
for (const [index, [field, value]] of cases.entries()) {
const record = storedTraceRecord()
record.agentRuns[0].events[0][field] = value
const candidatePath = index === 0
? storagePath
: path.join(directory, `provenance-${field}.json`)
const contents = JSON.stringify({ version: 1, runs: [record] })
fs.writeFileSync(candidatePath, contents, 'utf8')
const ledger = new RunLedger({ storagePath: candidatePath, now: () => 2000 })
assert.equal(ledger.loadError instanceof Error, true, field)
assert.throws(
() => ledger.finish('run-stored', 'failed'),
{ message: 'RUN_LEDGER_LOAD_FAILED' },
field,
)
assert.equal(fs.readFileSync(candidatePath, 'utf8'), contents, field)
}
const legacyPath = path.join(directory, 'legacy-event.json')
const legacy = storedTraceRecord()
for (const field of [
'runId', 'agentRunId', 'groupId', 'threadRootId', 'agentKind', 'round',
]) delete legacy.agentRuns[0].events[0][field]
fs.writeFileSync(legacyPath, JSON.stringify({ version: 1, runs: [legacy] }), 'utf8')
const restored = new RunLedger({ storagePath: legacyPath })
assert.equal(restored.loadError, null)
assert.deepEqual(
Object.fromEntries([
'runId', 'agentRunId', 'groupId', 'threadRootId', 'agentKind', 'round',
].map(field => [field, restored.get('run-stored').agentRuns[0].events[0][field]])),
{
runId: 'run-stored',
agentRunId: 'agent-stored',
groupId: 'group-stored',
threadRootId: 'group-stored-root',
agentKind: 'codex',
round: 1,
},
)
})
test('rejects duplicate run and Agent run identities across the complete store', (t) => {
const { directory, storagePath } = fixture(t)
fs.mkdirSync(path.dirname(storagePath), { recursive: true })
const duplicateAgent = storedTraceRecord()
duplicateAgent.agentRuns.push(JSON.parse(JSON.stringify(duplicateAgent.agentRuns[0])))
const outsideWindow = [
storedTraceRecord('run-edge-first', 'group-edge', 'agent-global-duplicate'),
...Array.from({ length: 2047 }, (_, index) => (
runRecord(`run-unique-${index}`, 'group-edge')
)),
storedTraceRecord('run-edge-last', 'group-edge', 'agent-global-duplicate'),
]
const cases = [
['duplicate-run', [
storedTraceRecord('run-duplicate', 'group-first', 'agent-first'),
storedTraceRecord('run-duplicate', 'group-second', 'agent-second'),
]],
['duplicate-agent', [duplicateAgent]],
['duplicate-agent-across-runs', [
storedTraceRecord('run-first', 'group-first', 'agent-duplicate'),
storedTraceRecord('run-second', 'group-second', 'agent-duplicate'),
]],
['duplicate-agent-outside-window', outsideWindow],
]
for (const [index, [name, runs]] of cases.entries()) {
const candidatePath = index === 0 ? storagePath : path.join(directory, `${name}.json`)
const contents = JSON.stringify({ version: 1, runs })
fs.writeFileSync(candidatePath, contents, 'utf8')
const ledger = new RunLedger({ storagePath: candidatePath, now: () => 2000 })
assert.deepEqual(ledger.list(), [], name)
assert.equal(ledger.loadError instanceof Error, true, name)
assert.throws(
() => ledger.deleteGroup('group-first'),
{ message: 'RUN_LEDGER_LOAD_FAILED' },
name,
)
assert.equal(fs.readFileSync(candidatePath, 'utf8'), contents, name)
}
})
test('validates malformed records before applying the retention window', (t) => {
const { storagePath } = fixture(t)
fs.mkdirSync(path.dirname(storagePath), { recursive: true })
const contents = JSON.stringify({
version: 1,
runs: [
null,
...Array.from({ length: 2048 }, (_, index) => (
runRecord(`run-window-${index}`, 'group-window')
)),
],
})
fs.writeFileSync(storagePath, contents, 'utf8')
const ledger = new RunLedger({ storagePath, now: () => 1000 })
assert.deepEqual(ledger.list(), [])
assert.equal(ledger.loadError instanceof Error, true)
assert.throws(
() => ledger.checkpoint(runRecord('run-new', 'group-new')),
{ message: 'RUN_LEDGER_LOAD_FAILED' },
)
assert.equal(fs.readFileSync(storagePath, 'utf8'), contents)
})
test('loads legacy records with omitted nested arrays and remains writable', (t) => {
const { storagePath } = fixture(t)
fs.mkdirSync(path.dirname(storagePath), { recursive: true })
fs.writeFileSync(storagePath, JSON.stringify({
version: 1,
runs: [{
runId: 'run-legacy',
groupId: 'group-legacy',
status: 'running',
}, {
runId: 'run-legacy-agent',
groupId: 'group-legacy',
targetKinds: ['codex'],
status: 'running',
agentRuns: [{
agentRunId: 'agent-legacy',
kind: 'codex',
status: 'running',
}],
}],
}), 'utf8')
const ledger = new RunLedger({ storagePath, now: () => 1000 })
assert.equal(ledger.loadError, null)
assert.equal(ledger.get('run-legacy').taskId, '')
assert.equal(ledger.get('run-legacy').contextPackState, 'legacy-unavailable')
assert.deepEqual(ledger.get('run-legacy').targetKinds, [])
assert.deepEqual(ledger.get('run-legacy').agentRuns, [])
assert.deepEqual(ledger.get('run-legacy-agent').agentRuns[0].events, [])
assert.equal(ledger.get('run-legacy-agent').contextPackState, 'legacy-unavailable')
assert.deepEqual(ledger.get('run-legacy-agent').agentRuns[0].sourceMessageIds, [])
assert.deepEqual(ledger.get('run-legacy-agent').agentRuns[0].context, {})
const finished = ledger.finish('run-legacy', 'completed')
assert.equal(finished.status, 'completed')
assert.equal(finished.contextPackState, 'legacy-unavailable')
const restarted = new RunLedger({ storagePath, now: () => 2000 })
assert.equal(restarted.get('run-legacy').contextPackState, 'legacy-unavailable')
})