Companion to Version Management and ADR-0012 Two histories. Descriptions are from public documentation; verify details before borrowing code.

systemmodelborrow
gitDAG of snapshot commits; diffs computed on demand; identity = paththe checkpoint idea; history-as-graph rendering; gix (pure Rust) / git2 bindings
Datomicimmutable facts (entity, attribute, value, tx, added?); database as of any transactionthe fold model exactly: our events are Datomic facts at entity granularity; “as-of” queries
XTDBbitemporal: valid-time and transaction-time per factkeep both timestamps — “when was it true” vs “when did we learn it” matters for imported database rows
TerminusDBgit-like branches/merges over an RDF graph, stored as deltasbranching a graph; delta storage; merge as set operations on triples
Doltgit semantics (branch/diff/merge) for SQL tables, cell-level diffsproves table history is tractable; a Dolt source could map its history to the log directly
Unisoncode is content-addressed by hash; names are metadata; no diffs — definitions are added, never changedthe “append, never mutate” stance; renames as metadata; strong reason to treat a Unison codebase as a first-class source
Automerge / Loro (CRDTs)per-replica operation logs with causal ordering; text, lists, maps merge automaticallythe content layer under Content events; logical clocks (loro 1.x is Rust-native)
Event sourcing (general)append-only events, state by projection, snapshots for speedvocabulary and the snapshot/compaction discipline
FossilSCM with an append-only artifact store; “unversioned” vs versioned contenttombstones (“shunning”) as a first-class, auditable operation

Take-aways for the design

  1. Append-only + fold is well trodden (Datomic, event sourcing); the risks are storage growth and slow folds, both solved by snapshots.
  2. Two timestamps (XTDB) cost little now and are painful to retrofit; include valid_at alongside recorded_at for imported facts.
  3. Structural merge is easy, content merge is hard — every system above separates the two; so do we (entity log vs text patch/CRDT).
  4. Content addressing (Unison, git objects) is the right key for content, UUIDs the right key for identity; Moonkale uses both: NodeId for the thing, a content hash for its text at a point in time.