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

44 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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).