ADR-036: Deterministic replay model + clock/identity seam
Date: 2026-06-18 Status: Accepted Author: Rômulo Febasi
SYN-266 (INV-4). The verify-first investigation the issue mandated: “ADR-027 assumes but never verified that audit replay reconstructs the store.” It does not. Companion to ADR-026 (audit), ADR-027/031 (bitemporal), ADR-035 (verify report). Grounds:
docs/researchs/2026-06-16-provable-non-corruption-syn-verify.md.
Context
The brief’s INV-4 read: “replaying every accepted audit payload into an empty workspace reconstructs the current store.” Before building it the issue required verifying that assumption. It is false as stated, for three reasons found in the code:
- The audit log is hash-only.
AuditEntrystorespayload_sha256, not the payload. The replayable payloads live in thependingtable: part of the disposable index, and prunable (syn audit prune). So the audit log is not an event journal you can replay. - The wall clock is baked in, not journaled.
applystampscreated/updated(viaFrontmatter::now_iso) and the belief axis (x-recorded-at) fromnow()/ the accept-time stamps, andaccepted_byfrom the environment. Two of these are freshnow()calls recorded nowhere a replay can read, so a replay cannot reproduce them byte-for-byte. - Direct CLI writes are not audited.
syn project add,tag,rm, and manual Markdown edits never enter the queue. The decision journal therefore covers only the governed (AI-proposed → human-accepted) path.
The deeper point (3) exposes: Synapse is state-based, not event-sourced. The Markdown is the user-editable source of truth; the decision journal records what was proposed and accepted (provenance), not the current content (which the human may have since edited: legitimately). “Replay the decision journal == current store” is the wrong model: it would flag every legitimate post-accept edit as corruption.
Decision
1. Reframe INV-4 to what is sound and provable. Two separable claims:
- Replay determinism (provable, shipped).
applyis a deterministic function of(payload, stamps, clock). Given the same journal and the same pinned clock, replay reproduces the store exactly. This is proven by a property test (core::replay, ≥48 generated cases incl. a supersession). - Store reconstruction is NOT journal replay. It is INV-1 (the index
is a reproducible build of the Markdown: ADR-035) and INV-7 (any past
state is reconstructible from git via
syn at: SYN-258). Those, not audit-replay, are Synapse’s reconstruction guarantees.
2. Introduce a clock/identity seam. Frontmatter::now_iso consults a
per-thread pinned value (pin_clock, returning an RAII guard); production
never pins, so behaviour is unchanged. Replay pins the recorded instant per op
so timestamps are reproducible. This is the seam the issue asked for.
3. syn verify --deep is a bounded journal-integrity check, not store
reconstruction. It replays the governed journal (accepted, still-present
pending rows) through the same mutation engine the accept path uses, in two
throwaway scratch workspaces, and confirms every op applies cleanly and the
two replays agree (determinism on the real data). It catches a tampered or
corrupt pending payload. It does not compare against the live store
(unsound for an editable-Markdown store). The bound (pruned payloads and
CLI-direct writes are outside the journal) is intrinsic and stated in the
report evidence.
Consequences
- Improves: an honest, sound INV-4 ships today; the clock seam makes the
governed write path reproducible; the deep check catches journal corruption
with no false positives on legitimate edits;
core::replayreusespending::applyso the replay and accept engines cannot drift. - Worsens / limits:
--deepdoes not prove “the store equals its decision history”: that is not a property of a state-based store and is deliberately not claimed. Exact store reconstruction from a complete, durable op-journal would need the journal to record the payloads and the wall-clock stamps (today they are hash-only / fresh-now()); that is a larger event-sourcing refactor, not pursued because Markdown-as-truth + git already reconstruct state (INV-1/INV-7). - Open: if a future “shared/governed team” edition wants audit-log-only reconstruction (no shared Markdown), it must durably journal payloads + a clock/identity seam in the log: pull this then.
Alternatives considered
- Replay the audit log into an empty workspace and compare to the store. Rejected: the audit log is hash-only (no payloads), and the comparison is unsound: a human editing a governed fact afterwards is legitimate, not corruption.
- Replay the
pendingjournal and compare a timestamp-normalised dump to the live store. Rejected: still false-positives on any legitimate post-accept Markdown edit (Markdown is the truth, not the journal); the normalisation needed to hide that is broad enough to risk fake-green. - Thread explicit
created/updated/accepted_atparameters through everycreate_*andapply. Heavier and more invasive than a single thread-local clock seam, for the same determinism. Rejected in favour ofpin_clock. - A full event-sourcing rewrite (the store is a fold over an op-log). Rejected: it contradicts Markdown-as-truth (ADR foundation) and git-native history; reconstruction is already INV-1/INV-7.