R3: immutable append-only event log sealed into the run manifest

Adds the tamper-evident event stream that makes every DV auditable. Each
measurable moment is appended as one JSON object per line to an append-only
output/sor-runs/<ts>/events.jsonl over the closed vocabulary {consent_request,
consent_accept, consent_reject, circuit_build, hop_add, bridge_forward,
churn_kill, churn_spawn, rebuild_start, rebuild_done}; on close the file is
SHA-256'd and the digest is sealed exactly once into manifest.json (the single
sanctioned None->hash completion of the manifest).

Records carry only metadata (fingerprints, byte counts, latencies, decisions),
never message plaintext, so the zero-knowledge relay property is preserved. The
deterministic replay_fixture_circuit emits a seed-driven event stream with no
engine, socket, or traffic (containment-safe) and is the basis of
instrument-validation gate item 6.

- cmd_chat/sor/events.py: EventLog (append-only, per-record schema),
  replay_fixture_circuit, replay_and_seal.
- cmd_chat/sor/provenance.py: seal_manifest (seal-once events SHA + stop ts).
- tests: replay -> schema-valid JSONL whose SHA matches the manifest;
  append-only (original prefix byte-identical after further appends);
  seed-determinism modulo timestamps; seal-once immutability.

Live emit wiring into the _sor handlers lands with R4/R5.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
leetcrypt
2026-07-19 16:18:38 -07:00
parent 7ed23704ea
commit a9308edc5d
3 changed files with 355 additions and 0 deletions
+21
View File
@@ -172,6 +172,27 @@ class RunManifest:
}
def seal_manifest(
run_dir: Path, events_sha256: str, stop_utc: Optional[str] = None
) -> Dict[str, Any]:
"""R3 close step: seal the closed events.jsonl SHA-256 (and stop timestamp)
into an existing manifest.json exactly once.
This is the one sanctioned mutation of a manifest — a single None -> hash
transition completing the document. Re-sealing an already-sealed manifest is
refused, preserving artifact immutability."""
run_dir = Path(run_dir)
path = run_dir / "manifest.json"
doc = json.loads(path.read_text())
if doc.get("events", {}).get("sha256") is not None:
raise ValueError("events already sealed (manifest is immutable)")
doc["events"]["sha256"] = events_sha256
doc["timestamps"]["stop_utc"] = stop_utc or _utc_now_iso()
validate_manifest(doc)
path.write_text(json.dumps(doc, indent=2, sort_keys=True) + "\n")
return doc
def write_manifest(run_dir: Path, manifest: RunManifest) -> Dict[str, Any]:
"""Render `manifest` to `<run_dir>/manifest.json` and return the dict.