Records and Replay
Current responsibility: persist run manifests, ordered lifecycle events, sealed artifacts, mutable user annotations, and zip exports. The first version supports integrity checks, torn-tail recovery, SSE event waiting, and export of sealed records. Run metadata queries are served by verification and reused by the Records tab. Export is this module's own route and must not be conflated with the query endpoint. See the parent scope, repository rules, and the development task guide.
Change Guide
| Goal | Edit here | Conditional / reused path |
|---|---|---|
| Change provenance/hash display | RecordsView.tsx | Reuse verification's run query. Do not add a second metadata endpoint. |
| Change sealing, integrity or recovery | store.py and relevant verification/fuzzing tests | Writer services only if the store contract changes. |
| Change zip contents | RecordStore.export_zip in store.py, export coverage tests | routes.py for HTTP headers/errors only; api.ts or view only if URL/download interaction changes. |
| Add a scientific artifact | Producing worker/bridge plus verification artifact write path | Existing generic RecordStore may need no change. analysis is a reader, not the layers.json writer. |
| Change saved counterexample annotations | fuzzing service/schema/view | RecordStore annotation methods are reused unless persistence guarantees change. Annotations are not part of the artifact seal. |
| Add persisted command receipts | store.py annotation allowlist and the owning service | Fuzzing writes fuzz_control_requests.json for per-run request-ID deduplication; receipts are included in export and never replayed as commands. |
Run metadata belongs to verification's schema/query; an added metadata field requires its owning schema change and npm run gen:types. Import and scientific snapshot replay remain planned, not available export features.
Flow Diagram
The query, artifact producer and export routes have different owners. Arrows are labeled with the transferred data or operation.
| Block | Code and contract | Neighbor handoff |
|---|---|---|
| Run metadata | hooks.ts reuses the run query and RecordsView.tsx renders RunOut | verification |
| Artifact producer | verification/service.py writes worker results, layers and fuzzing artifacts and seals them | Run lifecycle |
| Annotation writer | fuzzing/service.py saves user annotations through RecordStore | Saving counterexamples |
| Artifact readers | analysis/service.py reads layers; fuzzing reads fuzzing data and annotations | analysis, fuzzing |
| Export | api.ts supplies exportUrl; routes.py checks existence/seal and calls store.py export_zip | Downloaded zip; no second metadata API |
Validation
Run the existing record and fuzzing coverage:
.venv/bin/python -m pytest visual_bench/verification/test_verification.py
.venv/bin/python -m pytest visual_bench/fuzzing/test_fuzzing.py
.venv/bin/python -m pytest visual_bench/fuzzing/test_control.py
npm testObservable behavior: the Records tab shows verification's selected run metadata, the export button requests the records route, incomplete runs report that export requires a sealed record, and the downloaded zip contains only relative run paths plus sealed artifacts, events, hashes, and available annotations. These are validation instructions, not a new test result.
REC-05 acceptance on 2026-11-06: the full default suite included 19 passing fake-provider fuzz control tests, including persisted receipt lookup after completion with no repeated command. Full-suite commands/counts are recorded in execution validation.
Reference and Backlog
Current files and routes
Current Files and Routes
| File | Responsibility |
|---|---|
| store.py | RecordStore: per-run directories, atomic writes, ordered events.jsonl, sealed artifact hashes, mutable annotations, event waiting, and zip export. |
| propagation.py | PropagationWriter: full propagation index, deduplicated float32 blobs, atomic live publication, and progress coalescing at 10 notifications/s plus the final flush. |
| bab.py | BabWriter: append-only BaB tree log (bab.jsonl), each line flushed before its notification; notifications coalesced to 2/s plus the terminal; a closing end line. RecordStore.bab_log reads complete lines from a byte offset. |
| ../analysis/test_propagation.py | Synthetic storage/index/API tests: deduplication, throttling without history loss, seal/blob corruption, export and explicit size limits. |
| routes.py | GET /api/runs/{run_id}/export, owned by records. Returns a zip only for a sealed run. |
| api.ts | Export URL helper. Run metadata query is imported from verification's client path. |
| hooks.ts | Run record query shared with verification and the export action. |
| RecordsView.tsx | Provenance, hashes, status, native reference, and export button. |
| ../verification/test_verification.py | Record integrity, recovery, and lifecycle coverage. |
| ../fuzzing/test_fuzzing.py | Fuzzing artifacts and saved annotation export coverage. |
Record layout under var/records/<run_id>/:
manifest.json immutable request and provenance
events.jsonl ordered lifecycle events
result.json final ACT result
layers.json recorded interval layer states
propagation.json full observation index, live atomic snapshots then sealed on completion
bab.jsonl BaB runs: one line per committed ACT wave, the terminal and an end line; sealed on completion
blobs/<sha256>.f32 propagation tensor envelopes, deduplicated within this run and sealed
fuzzing.json fuzzing result when applicable
artifacts.json sha256 seal for available artifacts
saved_counterexamples.json mutable user annotation, included in export
fuzz_control_requests.json queued command receipts keyed by request_id, included in export
diagnostics.json worker warnings/logs/error location and engine snapshot (verification VER-12)
act_stdout.log scrubbed tail of the worker's stdout (ACT's prints)
worker_stderr.log scrubbed tail of the worker's stderrDiagnostics files are written once when a run ends, for failed runs too, so they are not sealed; write_diagnostic accepts only these names. The export zip includes them; the reproduction snapshot is verification's GET /api/runs/{id}/snapshot, not this module's export.
Propagation is scientific evidence and joins the artifact seal. A blob contains canonical compact JSON {"dtype":"float32","shape":[...]}, a newline, and full little-endian float32 bytes; SHA256 covers the metadata and bytes together. Identical bytes with different shapes are different tensors. The filename is that digest; live reads verify it, and completed reads additionally require its artifacts.json entry. The worker writes blobs before atomically publishing the index, then notifies the supervisor. Progress can be coalesced; propagation.json retains every record. A failed/interrupted run may retain an explicitly partial, unsealed index (complete: false); this is accessible for inspection but never exported as a sealed completed result. The final index and all tensor blobs are included in export and reproduction snapshots. The legacy layer detail limit does not limit recording.
Focused storage and API check: .venv/bin/python -m pytest visual_bench/analysis/test_propagation.py.
Task acceptance, rationale, and planned capabilities
Tasks and Acceptance
| ID | Deliverable | Depends on | Completion evidence |
|---|---|---|---|
| REC-01 | Persist immutable run metadata and final result | BASE-03 | Save/read a manifest and artifact for the native ACT run recorded in BASE-03 with identical hashes; no private absolute paths are required in a bundle. |
| REC-02 | Persist commit batches and snapshot boundaries | REC-01, EVT-02 | A simulated interrupted write recovers the last committed prefix; incomplete references are never visible. |
| REC-03 | Implement replay, coverage, and resync reads | REC-02 | Snapshot S plus following events reproduces recorded state; missing/expired history is explicitly represented. |
| REC-04 | Export fixed research data and figure configuration | REC-03, ANA-02 | Export/import preserves numerical data, selected coordinates, guarantees, revision, and plot configuration; invalid bundle paths are rejected. |
| REC-05 | Persist fuzz control idempotency receipts | REC-01, VER-16 | The annotation allowlist includes fuzz_control_requests.json; fuzzing/test_control.py verifies that a duplicate after completion returns the original receipt without another pipe write. Receipt semantics remain fuzzing-owned. |
| REC-06 | Store the BaB tree log | REC-01, EVT-02 | bab.jsonl: one JSON line per committed ACT wave or terminal record, flushed by the worker before its notification; readers return complete lines from a byte offset and leave a torn final line; the completed log is sealed and exported with the record. Tree meaning stays bab-owned. |
Current status: REC-01 is implemented. REC-02 and REC-03 are partial through lifecycle events, recovery, and replay. REC-04 has sealed export only, not import. User annotations are written atomically, are not hashed into artifacts.json, and are included in exports.
Keep the first store local and small. Publish artifacts atomically, then write the seal. Event replay reconstructs recorded display state, never solver memory, and never executes imported commands. Missing history is not zero. Orphan artifacts may be cleaned only after committed references are checked.
Planned operations are createRunRecord, appendCommit, readSnapshot, readEvents, readArtifact, and exportBundle. Current equivalents live on RecordStore. Planned files such as repository.py, commit_log.py, and bundle.py are intentionally folded into store.py until later tasks require extraction.
Owner: B, with D and C integrating figure and data export. On completion, record exact commands and results, provenance, and limitations.