Skip to content
docs
Arxo ↗

Save and replay: archive a capture, reproduce the answer

For LLMs10 sections
  • Goal: freeze a computed answer into a file that can be stored, handed to a reviewer, and replayed later — and tell replaying the capture apart from merely recomputing the case, including exactly what the checksum covers and what it does not.

  • You need: Show grounds: the capture freezes the answer whose grounds that chapter presents.

  • Run:

    Terminal
    node --import tsx --test test/capture-replay.test.ts
  • Files:

    Output
    src/capture.ts CAPTURE_FORMAT deadline-app.capture/1
    src/replay.ts replayCapture, verifyCaptureIntegrity
    src/server.ts POST /api/deadline/replay
    src/cli.ts replay command

Calculate the worked case and write a capture file. The CLI writes a capture only when you pass --out:

Terminal
node --import tsx src/cli.ts evaluate --event 2026-03-06 --days 14 --out /tmp/case.capture.json
Output
The period ends on 2026-03-20
Computed from the event date and the duration by the pinned canon.
rules: TagesfristEnde
sources: No sources anchored in this canon build — the rule names above carry the provenance.
program: sha256:468e3fe17c6a28371c6ce8258ae7928c496593673d4b73a52c2a9c40cb50ee33
semantic: sha256:90e53b453107dfee9422a5fe24774823430b5c82c3577b9321a0838a6e54c695
result: sha256:ae9a7b19361154c868eb9b28d5ac6395b1d2844053259a1abfd6f75e300de812
capture: /tmp/case.capture.json

Hand the file to replay:

Terminal
node --import tsx src/cli.ts replay /tmp/case.capture.json
Output
integrity: ok — stored document bytes match the recorded document sha256 (input, query, versions, and other metadata are not covered)
replay: match — replayed answer matches the captured result hash
expected: sha256:ae9a7b19361154c868eb9b28d5ac6395b1d2844053259a1abfd6f75e300de812
actual: sha256:ae9a7b19361154c868eb9b28d5ac6395b1d2844053259a1abfd6f75e300de812

The command exits 0 only when both checks pass.

The two messages report two different checks:

  • integrity checks the stored bytes. It decodes the stored answer document, hashes it, and compares the hash with the checksum recorded next to it. It says nothing about the input, the query, or the versions in the file.
  • replay checks the result. It runs the frozen query against the frozen case again and compares the fresh result hash with the recorded one. It never reads the stored document bytes.

Replay is not recompute. Recomputing runs the current case through the current canon and shows today’s answer. Replaying takes a frozen capture, checks the stored document bytes, re-evaluates its frozen case input, and compares the fresh answer with the frozen result hash. The comparison is the point: it tells the reviewer whether the same case still answers the same way.

A capture is the whole evaluated case plus the answer document, under a versioned format name so a future application never misreads an old file:

src/capture.ts
export interface CapturedEvaluation {
format: string;
capturedAt: string;
model: string;
adapter: string;
input: FormInput;
caseInput: CaseInput;
query: AppQuery;
evaluationStatus: string;
truthStatus?: string;
hashes: Record<string, string>;
// sha256 over the raw evaluation-document bytes.
documentSha256: string | null;
// The real document bytes, base64; null when the answer came from a host.
documentBase64: string | null;
documentNote: string | null;
versions: CaptureVersions;
}

Two groups of fields make the two checks possible. documentBase64 and documentSha256 feed integrity: documentSha256 covers the stored document bytes — the same bytes documentBase64 carries — and nothing else. caseInput, query, and hashes.result feed replay: the frozen case, the frozen question, and the result to compare with.

src/replay.ts
// Recompute the sha256 over the stored document bytes and compare it
// with the recorded checksum. This checks the document bytes alone:
// it says nothing about the input, query, versions, or metadata, and
// nothing about who wrote the file (bytes and checksum travel together).
export function verifyCaptureIntegrity(capture: CapturedEvaluation): IntegrityReport {

The application keeps the two reports separate:

src/replay.ts
export interface IntegrityReport {
ok: boolean;
expectedSha256: string | null;
actualSha256: string | null;
detail: string;
}
export interface ReplayReport {
match: boolean;
expectedHash: string | null;
actualHash: string | null;
evaluationStatus: string;
detail: string;
}

Because replayCapture never reads the document bytes, the two checks fail differently, and the tests pin each cell:

EditIntegrityReplay
One document byte flippedfailsskipped
Display input onlypassesmatches — the edit is not detected
Stored case factspassesmismatches
Stored querypassesmismatches
Recorded result hashpassesmismatches
Version stringspassesmatches — the edit is not detected
Document plus its checksum, replaced togetherpassesmatches — no forgery protection is claimed
No document bytes (host answer)failsskipped

“Passes undetected” is not a bug in the table — it is the documented boundary. The checksum proves the stored bytes match their recorded checksum; it does not prove who wrote the file, because bytes and checksum travel together. Version strings are advisory metadata with no external record to check them against. A reviewer who needs provenance must compare against a digest kept elsewhere.

The server exposes the same flow over HTTP: POST /api/deadline/replay takes a capture id, verifies the stored file, replays it, and returns the comparison. Export is an explicit user action in the form too: it downloads only on its buttons — Save summary for the {view, captureId} answer JSON, Download capture (GET /api/deadline/capture?captureId=…) for the full stored archive. Only the full archive is replayable.

Calculate in the form (or by POST), download the full archive, then carry the file to another machine:

Terminal
curl -s -X POST localhost:8787/api/deadline/evaluate \
-H 'content-type: application/json' \
-d '{"eventDate":"2026-03-06","durationDays":14,"legalTime":"2026-09-17"}' \
-o /tmp/answer.json
ID=$(node -e "console.log(JSON.parse(require('fs').readFileSync('/tmp/answer.json','utf8')).captureId)")
curl -s "localhost:8787/api/deadline/capture?captureId=$ID" -o /tmp/capture.json

Stop the first server. In a second checkout with the pinned dependencies installed — no access to the first server’s store — replay the carried file:

Terminal
node --import tsx src/cli.ts replay /tmp/capture.json
Output
integrity: ok — stored document bytes match the recorded document sha256 (input, query, versions, and other metadata are not covered)
replay: match — replayed answer matches the captured result hash

The suite pins this portability: the downloaded bytes are copied to a directory outside the server store and replayed from there, with integrity ok and replay match.

  • Comparison stays within one SDK. The TypeScript and Python SDKs return the same statuses and values, but their canonical bytes and result hashes differ. A capture saved by one SDK is replayed and compared by the same SDK only; cross-SDK comparison would report a mismatch on identical outcomes.
  • A document-checksum failure rejects the file before any evaluation: a capture whose bytes do not hash to its documentSha256 is not replayed, and no answer is shown from it.
  • The format string is checked first. A capture naming another format is refused with the expected name, not parsed hopefully.
  • Captures of host answers carry no document bytes and always fail integrity with the recorded note. Only local answers are replayable.

Run the matrix:

Terminal
node --import tsx --test test/capture-replay.test.ts
Output
ok 1 - capture round-trips: replay matches the recorded result hash
ok 2 - tampered document bytes fail integrity, replay is skipped
ok 3 - an input-only edit passes undetected
ok 4 - a case edit passes integrity and mismatches replay
ok 5 - a query edit passes integrity and mismatches replay
ok 6 - a versions edit passes undetected
ok 7 - an edited result hash mismatches replay
ok 8 - consistent document-plus-checksum replacement passes both checks
ok 9 - a foreign-model capture is refused, not mixed
ok 10 - a host-style capture without bytes fails integrity, replay skipped
# tests 10
# pass 10
# fail 0

Predict before you open each answer.

You change the duration inside the stored case facts and replay. What do the two lines say?

integrity: ok — the stored document bytes are untouched. replay: MISMATCH — the edited case computes a different answer, so its result hash no longer matches the recorded one.

You change only the display `input` block. Does either check notice?

No. Integrity covers only the document bytes, and replay runs the stored caseInput, not the display input. Both pass; this edit is a documented blind spot.

Which download do you keep to replay the calculation later?

Download capture — the full stored archive. Save summary holds only the {view, captureId} answer JSON, which has no frozen case and no document bytes, so it cannot be replayed.

Documentation for Arxo. Writings — blog.arxo.io.

Anonymous visit counts on stats.arxo.io, no cookies.