You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-up to PR #5. Zeitghost's landing page sells "hash-chained, tamper-evident traces," and spiritwriter.fabric.emitter.TraceEmitter provides exactly that — but it is currently unused in zeitghost. PR #5 made shards signed and the lineage load correct; this issue adds the trace chain on top so provenance is queryable and per-run auditable.
What's missing today
No trace events emitted anywhere in fetcher → bias → shards — no shard_created, no chain.
MemoryShard.trace_ref is always None (a shard can't point back to "which run/event produced me").
No per-ingest run_id, so an ingest cycle is anonymous — can't ask "show everything the 06:00 run created/changed."
The card flip-panel shows model/agent atoms but not the signer thumbprint or a trace link.
Scope
Construct one TraceEmitter per zeitghost ingest run (one run_id, persisted JSONL alongside the shard store).
Emit shard_created per write (article_to_*_shard); set trace_ref via emitter.current_trace_ref() before store.put.
Emit shard_superseded(old, new) when a write chains onto a parent_shard_id (uses the lineage index already built in cli.py ingest).
Surface signer thumbprint (created_by) + the trace link in the card flip-panel (_shard_to_article already loads created_by/parent_shard_id; thread them to the template).
Wire TraceEmitter into the ingest pipeline
Follow-up to PR #5. Zeitghost's landing page sells "hash-chained, tamper-evident traces," and
spiritwriter.fabric.emitter.TraceEmitterprovides exactly that — but it is currently unused in zeitghost. PR #5 made shards signed and the lineage load correct; this issue adds the trace chain on top so provenance is queryable and per-run auditable.What's missing today
fetcher → bias → shards— noshard_created, no chain.MemoryShard.trace_refis alwaysNone(a shard can't point back to "which run/event produced me").run_id, so an ingest cycle is anonymous — can't ask "show everything the 06:00 run created/changed."shard_supersededevents are never emitted. (PR shards: integrity & lineage-correctness (sign, collapse revisions, no default-fill bias) #5 landed the load-side latest-per-entity collapse, but not the corresponding trace event when a revision supersedes its parent.)Scope
TraceEmitterperzeitghost ingestrun (onerun_id, persisted JSONL alongside the shard store).shard_createdper write (article_to_*_shard); settrace_refviaemitter.current_trace_ref()beforestore.put.shard_superseded(old, new)when a write chains onto aparent_shard_id(uses the lineage index already built incli.py ingest).created_by) + the trace link in the card flip-panel (_shard_to_articlealready loadscreated_by/parent_shard_id; thread them to the template).Acceptance criteria
emitter.verify_chain().trace_refpointing at theirshard_createdevent.shard_supersededlinking new→old.python -c "import zeitghost"stays clean from an empty cwd (no import-time I/O).trace_refround-trips, chain verifies, supersede event on re-analysis.Notes
reanalyzecommand — that's the workflow that actually producesshard_supersededevents to test against.created_bythumbprint this would display) is already live in prod as of PR shards: integrity & lineage-correctness (sign, collapse revisions, no default-fill bias) #5.Related: #8 (
zeitghost reanalyze) is the workflow that produces theshard_supersededevents this issue emits — land them together or #7 first.