chronogrid/docs/ARCHITECTURE/ADR/0004-core-automerge-crdt-in...

3.6 KiB
Raw Blame History

ADR-0004: chronogrid_core – Automerge CRDT Integration for Task & Note Registers

  • Status: Accepted
  • Date: 2026-08-20
  • Deciders: Michael Kell

Context & Problem Statement

Task 1.4 requires integrating the Automerge CRDT engine into chronogrid_core so that task and note entities can be modified concurrently by different peers and merged deterministically without centralized coordination. The architecture spec (03-crdt-sync-protocol.md) mandates LWW-Element-Set registers for primitive fields and Hybrid Logical Clocks (HLC) for causal ordering.

Options Considered

  1. Manual LWW register implementation — track per-field HLC timestamps in a custom data structure and resolve conflicts manually.
  2. Automerge integration with HLC companion stamps — use Automerge's built-in CRDT merge semantics for structural conflict resolution, augmented with HLC timestamps stored inside each document for causal ordering context.
  3. Yrs (Yjs Rust port) — alternative CRDT library focused on text collaboration.

Decision Outcome

Chosen Option: Option 2 — Automerge + HLC because:

  • Automerge provides battle-tested CRDT merge, fork, save/load, and delta-sync primitives out of the box.
  • Storing HLC timestamps as companion fields inside the Automerge document aligns with the spec's requirement for HLC-based LWW conflict resolution while leveraging Automerge's deterministic merge for structural integrity.
  • The automerge crate (v0.5.12) has a stable API with AutoCommit, ReadDoc, and Transactable traits that map cleanly to our per-entity document model.
  • Yrs is optimized for text sequences, not key-value LWW registers.

Implementation Details

  • automerge = "0.5" added to chronogrid_core/Cargo.toml (resolved to v0.5.12).
  • uuid = "1" with v4 feature added for entity ID generation.
  • Schema extension: crdt_docs table added to schema.sql with columns entity_id TEXT PK, entity_type TEXT, doc_bytes BLOB, updated_at INTEGER, plus idx_crdt_docs_type index.
  • Module structure: crdt/ sub-module with hlc.rs, doc.rs, task.rs, note.rs.
  • HLC: Hlc struct with tick(), update(), Display/FromStr (format: {wall_ms:016}-{counter:08x}-{node_id}), and full Ord implementation ensuring lexicographic string order matches logical order.
  • CrdtDoc: Generic wrapper over AutoCommit providing typed put_*/get_* accessors for String, f64, i64, u64, bool scalars at the document root.
  • TaskCrdt / NoteCrdt: Entity-specific wrappers with field-level setters that automatically advance the HLC and record it in the document.
  • Database persistence: save_crdt_doc(), load_crdt_doc(), delete_crdt_doc() methods on Database for upsert/load/delete of serialized Automerge bytes.
  • 45 unit tests covering: HLC ordering, tick/update/parse, all scalar types, save/load round-trips, fork/merge conflict resolution, CRDT doc DB persistence, and schema validation.

Consequences & Trade-offs

  • Positive: Full offline-first CRDT capability; entities can be forked, edited independently, and merged without data loss. HLC provides deterministic causal ordering for the SQL layer. Zero external network dependencies (local-first mandate satisfied).
  • Positive: Automerge's compact binary format minimizes storage overhead for CRDT doc blobs.
  • Negative: Automerge 0.5.x adds ~40 transitive dependencies; binary size increases moderately.
  • Negative: Each entity requires both a SQL row and a CRDT doc blob, introducing a dual-write pattern that must be kept in sync (future work: transactional wrappers).