44 lines
3.6 KiB
Markdown
44 lines
3.6 KiB
Markdown
# 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).
|