chronogrid/DECISION_LOG.md

160 lines
17 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.

# Architectural Decision Log (ADL)
## [2026-08-09] - ADL-001: Distributed Local-First System Baseline
- **Status**: Accepted
- **Context**: Design a multi-platform (Windows, Linux, Android, Headless) productivity engine for time, project, and note management without centralized servers.
- **Decision**: Adopted Rust core with SQLCipher local storage, Automerge CRDT delta sync over libp2p, and Flutter presentation layer bound via C-FFI.
## [2026-08-09] - ADL-002: chronogrid_crypto Crate – Argon2id KDF & OS Keychain
- **Status**: Accepted
- **Context**: Task 1.2 required implementing the `chronogrid_crypto` crate with Argon2id master-key derivation and OS-native keychain bindings per `docs/ARCHITECTURE/02-security-and-crypto.md`.
- **Decision**:
- Argon2id parameters pinned: 64 MiB memory, 3 iterations, 4 threads, 16-byte random salt, 32-byte output.
- `DerivedKey` struct implements `Zeroize + Drop` to scrub sensitive memory on scope exit.
- OS keychain abstracted via `keyring` crate v3 (Windows Credential Manager / DPAPI, Linux SecretService / libsecret). Android keychain is excluded (`cfg(not(target_os = "android"))`) and will be handled by Flutter KeyStore bridge in Task 1.5.
- Raw key material is Base64-encoded before credential-store insertion for UTF-8 backend compatibility.
- Inline Base64 encode/decode helpers avoid adding an external Base64 dependency.
## [2026-08-19] - ADL-003: chronogrid_core Crate – SQLCipher 4.x Database Initializer
- **Status**: Accepted
- **Context**: Task 1.3 required implementing the `chronogrid_core` crate with SQLCipher 4.x encrypted database initialization matching `docs/API_CONTRACTS/schema.sql`.
- **Decision**:
- `rusqlite` v0.31 with `bundled-sqlcipher-vendored-openssl` feature bundles both SQLCipher and OpenSSL, ensuring a hermetic build with no external C library dependencies.
- The 32-byte Argon2id-derived master key is passed as a **raw hex key** via `PRAGMA key = "x'…'"`, bypassing SQLCipher's internal PBKDF2 derivation since Argon2id already provides equivalent or stronger protection.
- `PRAGMA cipher_compatibility = 4` enforces SQLCipher 4.x defaults (AES-256-CBC, HMAC-SHA512 page auth, 64 KiB page size).
- `PRAGMA foreign_keys = ON` is set on every connection per the schema specification.
- The canonical schema SQL from `docs/API_CONTRACTS/schema.sql` is embedded via `include_str!` at compile time, ensuring single-source-of-truth alignment without runtime file I/O.
- Post-initialization validation confirms all 6 expected tables exist (`namespaces`, `projects`, `tasks`, `notes`, `task_note_links`, `ingestion_logs`) plus 4 indexes.
- `Database` struct provides `open(path, key)` for file-backed and `open_in_memory(key)` for test databases; key length is validated at 32 bytes.
- 11 unit tests cover: table/index creation, FK enforcement, cascade deletes, wrong-key rejection, key length validation, schema idempotency, and CRUD round-trips across all tables.
## [2026-08-20] - ADL-004: chronogrid_core – Automerge CRDT Integration for Task & Note Registers
- **Status**: Accepted
- **Context**: Task 1.4 required integrating the Automerge CRDT engine into `chronogrid_core` so task and note entities support offline-first LWW register mutations with HLC-based causal ordering.
- **Decision**:
- Added `automerge = "0.5"` (resolved to v0.5.12) and `uuid = "1"` with `v4` feature to `chronogrid_core/Cargo.toml`.
- Extended `schema.sql` with `crdt_docs` table (`entity_id TEXT PK`, `entity_type TEXT`, `doc_bytes BLOB`, `updated_at INTEGER`) and `idx_crdt_docs_type` index.
- Implemented `crdt/` sub-module: `Hlc` (Hybrid Logical Clock with `tick()`/`update()`/`Display`/`FromStr`), `CrdtDoc` (generic Automerge wrapper with typed scalar accessors), `TaskCrdt` (11 mutable fields), `NoteCrdt` (4 mutable fields).
- Each entity mutation stamps the document with an advancing HLC timestamp. Fork/merge delegates to Automerge's deterministic conflict resolution.
- `Database` gained `save_crdt_doc()`, `load_crdt_doc()`, `delete_crdt_doc()` for serialized Automerge blob persistence.
- Added `CoreError::Crdt` and `CoreError::HlcParse` error variants.
- 45 unit tests (all passing): HLC ordering/parse, all scalar types, save/load/fork/merge round-trips, CRDT doc DB persistence, schema validation with `crdt_docs` table.
- Full ADR at `docs/ARCHITECTURE/ADR/0004-core-automerge-crdt-integration.md`.
## [2026-08-20] - ADL-005: chronogrid_ffi Crate – Flutter Rust Bridge v2 FFI Layer
- **Status**: Accepted
- **Context**: Task 1.5 required generating `flutter_rust_bridge` FFI bindings to bridge all Rust storage, CRDT, and cryptographic methods to the Flutter/Dart presentation layer.
- **Decision**:
- Created `crates/chronogrid_ffi/` crate with `cdylib` + `staticlib` + `lib` targets, depending on `chronogrid_core`, `chronogrid_crypto`, `flutter_rust_bridge = "=2.7.0"`, and `anyhow = "1"`.
- API uses a **serialized-bytes pattern**: CRDT documents cross FFI as `Vec<u8>` (Automerge binary); read functions return DTO structs (`FfiTaskData`, `FfiNoteData`, `FfiDerivedKey`).
- Global `Engine` singleton (`Mutex<Option<Engine>>`) holds the `Database` connection + `Hlc` clock. Thread-safe `with_engine()` / `with_engine_mut()` helpers provide access from FRB's isolate thread pool.
- 38 public FFI functions across 5 API modules: `engine` (init/close/status), `database` (CRDT doc persistence), `crypto` (key derivation + platform-gated keychain), `task_crdt` (18 create/read/set/clear/merge ops), `note_crdt` (8 create/read/set/clear/merge ops).
- All fallible functions return `anyhow::Result<T>`, which FRB v2 maps to Dart exceptions.
- Keychain functions compile on all platforms but return an error on Android (delegated to Flutter KeyStore bridge).
- `flutter_rust_bridge.yaml` configures codegen: Rust input from `api/`, Dart output to `apps/chronogrid_flutter/lib/src/rust/`.
- Flutter `pubspec.yaml` updated with `flutter_rust_bridge: ^2.7.0`, `ffi: ^2.1.0`, `meta: ^1.11.0`.
- 25 unit tests (all passing): engine lifecycle, DB CRDT doc persistence, task/note CRDT create/read/mutate/merge round-trips, crypto derivation, stateless read validation, no-engine error paths.
- Full ADR at `docs/ARCHITECTURE/ADR/0005-ffi-bridge-flutter-rust-bridge.md`.
## [2026-08-22] - ADL-007: chronogrid_core Schema Extension — Phase 2 Missing Entities (Task 2.1)
- **Status**: Accepted
- **Context**: Task 2.1 required extending `docs/API_CONTRACTS/schema.sql` with 6 missing entity tables identified in the ADL-006 architecture review as a critical gap blocking Phase 3 and Phase 4 development.
- **Decision**:
- Added 6 new tables: `time_blocks`, `attachments`, `calendar_feeds`, `node_peers`, `automation_rules`, `ingestion_configs`.
- All new tables include `hlc_timestamp TEXT NOT NULL` for CRDT compatibility, except `node_peers` which is managed by `chronogrid_net` and does not participate in CRDT sync.
- `node_peers` has no FK to `namespaces` — it is a global peer registry scoped to the node, not to a namespace.
- `attachments.storage_path` stores relative paths inside the encrypted vault directory; file bytes are stored outside SQLite to avoid BLOB size limits.
- `calendar_feeds` and `ingestion_configs` store credentials as encrypted blobs (`encrypted_password_blob`, `encrypted_imap_password_blob`) — never plaintext.
- `ingestion_configs` uses a `config_type` discriminator (`imap` | `file_watcher` | `hotkey`) with nullable type-specific fields, avoiding a separate table per ingestion type.
- `crdt_docs.entity_type` now supports `'time_block'` in addition to `'task'` and `'note'`.
- `EXPECTED_TABLES` in `crates/chronogrid_core/src/db.rs` updated from 7 to 13 entries; schema validation enforces all 13 tables on every database open.
- 12 new indexes added for all new FK columns and common query patterns.
- 8 new unit tests added covering CRUD round-trips, FK cascade/SET NULL behavior, and `time_block` CRDT doc persistence.
- Verification script confirmed: all 13 tables present in schema.sql (PASS), Flutter analysis clean (PASS).
- Full ADR at `docs/ARCHITECTURE/ADR/0007-schema-extension-phase2-entities.md`.
## [2026-08-22] - ADL-008: chronogrid_net Crate — Node Identity & Swarm Key (Task 2.2)
- **Status**: Accepted
- **Context**: Task 2.2 required implementing the foundational identity and cluster-authentication primitives for `chronogrid_net`: Ed25519 node keypair (`NodeIdentity`), SHA-256-derived `NodeId`, and 256-bit pre-shared `SwarmKey`.
- **Decision**:
- `ed25519-dalek v2` chosen for Ed25519 keypair generation (audited, `rand_core`-compatible, libp2p-compatible for Task 2.3 integration).
- `NodeId` = SHA-256(public key bytes) → 32-byte / 64-hex-char stable identifier; maps directly to `node_peers.id` primary key in schema.
- `SwarmKey` = 32 bytes from `OsRng`; `fingerprint()` = first 16 hex chars of SHA-256(key) → maps to `node_peers.swarm_key_fingerprint`.
- Both `NodeIdentity` and `SwarmKey` implement `ZeroizeOnDrop`; `PersistedIdentity` also zeroized to prevent secret hex strings lingering in heap.
- `SwarmKey::eq` uses constant-time XOR accumulation with `black_box` to mitigate timing side-channels.
- Persistence: `NodeIdentity` → JSON (hex-encoded keys); `SwarmKey` → 64-char hex file. Both stored inside encrypted vault directory.
- `load_or_create_identity()` and `SwarmKey::load_or_create()` provide idempotent file-backed initialization.
- 27 unit tests: generation uniqueness, determinism, hex/bytes round-trips, file I/O, error paths, debug redaction, constant-time equality.
- Full ADR at `docs/ARCHITECTURE/ADR/0008-net-node-identity-swarm-key.md`.
## [2026-08-22] - ADL-009: chronogrid_net Crate — Transport & Discovery Layer (Task 2.3)
- **Status**: Accepted
- **Context**: Task 2.3 required implementing the libp2p transport stack and peer discovery layer in `chronogrid_net`, building on the Ed25519 `NodeIdentity` and `SwarmKey` from Task 2.2.
- **Decision**:
- Added 3 new modules: `peer_event` (`PeerEvent` enum), `transport` (TCP+Noise+Yamux builder, `TransportConfig`, keypair conversion), `discovery` (`MdnsDiscovery` and `KademliaDht` wrappers).
- **Transport:** `build_transport()` constructs a TCP + Noise XX + Yamux fallback transport. Primary QUIC transport is deferred to `SwarmBuilder::with_quic()` in Task 2.5. Noise XX (not XK) is used for TCP because XK requires pre-knowledge of the responder's static key, which is not guaranteed in a dynamic mesh.
- **SwarmKey authentication:** Enforced at the application layer via the `identify` protocol. The SwarmKey fingerprint (`SHA-256(key)[0..8]` as 16 hex chars) is embedded in the `UserAgent` string (`chronogrid/<version>/swarm:<fingerprint>`). Peers with mismatched fingerprints are disconnected by `SwarmManager` (Task 2.5) after identification.
- **Keypair conversion:** `dalek_to_libp2p_keypair()` extracts the 32-byte seed from `ed25519-dalek` and re-imports into `libp2p::identity::ed25519::Keypair`. Both use the same curve parameters; the derived `PeerId` is stable.
- **mDNS:** 5-minute TTL, 5-second re-query, IPv4-only, deduplication via `active_peers: HashSet<PeerId>`.
- **Kademlia:** In-memory routing store, 60s replication / 5min publication intervals, `add_bootstrap_peer()` / `bootstrap()` API.
- Added `tcp` and `yamux` libp2p feature flags (omitted from Task 2.2 declaration).
- 65 unit tests pass (10 peer_event, 12 transport, 13 discovery, plus 27 from Tasks 2.2 identity/swarm_key, plus 5 doc-tests).
- `KadEvent::RoutingUpdated` uses private libp2p-internal types; routing-table tracking tested via `#[cfg(test)] insert_peer_for_test()` helper; full coverage deferred to Task 2.5 integration test.
- Full ADR at `docs/ARCHITECTURE/ADR/0009-net-transport-discovery-layer.md`.
## [2026-08-22] - ADL-010: chronogrid_net Crate — CRDT Delta Sync Protocol (Task 2.4)
- **Status**: Accepted
- **Context**: Task 2.4 required implementing the CRDT delta exchange protocol over libp2p in `chronogrid_net`, building on the Ed25519 `NodeIdentity`, `SwarmKey`, and transport/discovery layers from Tasks 2.2 and 2.3.
- **Decision**:
- Added new `sync` module with 7 public types: `VectorClock`, `DeltaPacket`, `SyncCodec`, `SyncProtocol`, `GossipEvent`, `GossipsubBroadcast`, `NetEvent`.
- `VectorClock` uses `HashMap<entity_id, hlc_timestamp>` with lexicographic HLC comparison — no numeric parsing required since the `<ms>-<logical>-<node_prefix>` format preserves causal ordering under string comparison.
- `DeltaPacket` wire format: `bincode` → `lz4_flex` compress → `ChaCha20-Poly1305` encrypt with 12-byte random nonce prepended. AAD = `b"chronogrid-delta"` binds ciphertext to protocol context.
- `SyncProtocol` uses `#[derive(NetworkBehaviour)]` wrapping `libp2p::request_response::Behaviour<SyncCodec>`. `SyncCodec` implements the `Codec` trait via `#[async_trait]` (required by libp2p-request-response 0.27.0) using `futures::AsyncRead`/`AsyncWrite` (not `tokio::io`).
- `GossipsubBroadcast` subscribes to topic `chronogrid/state-changes/1` with SHA-256 content-addressed message deduplication and `ValidationMode::Strict`.
- `NetEvent` enum: `DeltasReceived { from_peer, deltas }`, `SyncComplete { peer_id }`, `SyncError { peer_id, reason }`, `GossipReceived { entity_type, entity_id, hlc_timestamp, origin_peer }`.
- Added dependencies: `bincode = "1"`, `lz4_flex = "0.11"`, `chacha20poly1305 = "0.10"`, `generic-array = "0.14"`, `async-trait = "0.1"`, libp2p `"request-response"` feature.
- 100 unit tests pass + 5 doc-tests pass (35 new sync tests + 65 from Tasks 2.2/2.3).
- Full ADR at `docs/ARCHITECTURE/ADR/0010-net-crdt-delta-sync-protocol.md`.
## [2026-08-22] - ADL-011: chronogrid_net Crate — P2P Swarm Manager (Task 2.5)
- **Status**: Accepted
- **Context**: Task 2.5 required implementing the top-level `SwarmManager` in `chronogrid_net`, composing all protocol behaviours from Tasks 2.2–2.4 into a single running libp2p `Swarm` with a clean public API.
- **Decision**:
- Added new `swarm_manager` module with 4 types: `ChronoGridBehaviour` (composite `#[derive(NetworkBehaviour)]`), `SwarmCommand` (internal control enum), `SwarmHandle` (cloneable public API), `SwarmManager` (event loop owner).
- `ChronoGridBehaviour` composes: `MdnsBehaviour` + `KadBehaviour<MemoryStore>` + `SyncProtocol` + `GossipBehaviour` + `IdentifyBehaviour` + `relay::client::Behaviour`.
- **Relay client**: In libp2p 0.54, `relay::client::new()` returns `Output = Connection` (not `(PeerId, Muxer)`), so `with_other_transport()` cannot be used. The correct API is `with_relay_client()` in the SwarmBuilder chain, which provides the `relay::client::Behaviour` to the `with_behaviour()` closure.
- **`SyncProtocolEvent`**: The `#[derive(NetworkBehaviour)]` macro on `SyncProtocol` (single field `inner`) generates `SyncProtocolEvent::Inner(request_response::Event<...>)` — unwrapped via pattern match in the event handler.
- **Event loop**: `tokio::select!` multiplexes `swarm.next()` (requires `futures::StreamExt`) and `cmd_rx.recv()`.
- **SwarmKey auth**: After `IdentifyEvent::Received`, validates `agent_version` against expected swarm key fingerprint; disconnects mismatched peers.
- **CRDT sync flow**: `ConnectionEstablished` → `SyncRequest(local_clock)` → `SyncResponse(encrypted_deltas, remote_clock)` → `NetEvent::DeltasReceived` + `NetEvent::SyncComplete`.
- Channels: `mpsc(64)` for `SwarmHandle → SwarmManager` commands; `broadcast(256)` for `SwarmManager → subscribers` events.
- 111 unit tests pass + 5 doc-tests pass (11 new swarm_manager tests + 100 from prior tasks).
- Full ADR at `docs/ARCHITECTURE/ADR/0011-net-p2p-swarm-manager.md`.
## [2026-08-22] - ADL-006: Architecture Review Board Assessment & Master Development Plan v2
- **Status**: Accepted
- **Context**: A comprehensive senior architecture review was conducted covering all crates, documentation, task structure, and the development plan. Phase 1 is complete and production-quality. Phases 2–5 require a structured execution plan to enable automated agent development.
- **Decision**:
- Created `docs/ARCHITECTURE/05-architecture-review-assessment.md` — full ARB assessment with gap analysis, risk register, and recommendations.
- Created `docs/ARCHITECTURE/06-master-development-plan.md` — authoritative execution plan superseding OSAD Section 8, with 37 tasks across Phases 2–5, quality gates, and acceptance criteria traceability.
- Created `docs/TASKS/phase_2_networking_and_schema.md` — 9 tasks for libp2p networking, schema extension, daemon scaffold, and verification pipeline hardening.
- Created `docs/TASKS/phase_3_flutter_ui_shell.md` — 10 tasks for Flutter BLoC architecture, all 4 primary views, FRB codegen, and build scripts.
- Created `docs/TASKS/phase_4_ai_and_automation.md` — 9 tasks for NLP engine, IMAP ingestion, file watchers, CalDAV server, and automation rules engine.
- Created `docs/TASKS/phase_5_headless_and_release.md` — 9 tasks for daemon completion, integration testing, performance testing, and release packaging.
- Fixed documentation typos in `docs/UI_UX/02-calendar-timeblocking.md` and `docs/UI_UX/03-kanban-gantt-views.md`.
- Expanded `docs/WORKFLOWS/01-master-user-story.md` from a stub to a substantive workflow reference document.
- Identified critical gaps: 6 missing schema tables, FFI type mismatch (`due_date: i64` vs `u64`), incomplete verification script, missing build scripts, unscaffolded daemon crate.
- Confirmed all Phase 1 architectural decisions as correct — no reversals required.
- Next task for automated execution: **Task 2.1 — Extend Schema with Missing Entities**.