211 lines
26 KiB
Markdown
211 lines
26 KiB
Markdown
# 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-012: chronogrid_core Crate — SyncEngine P2P Integration (Task 2.6)
|
||
|
||
- **Status**: Accepted
|
||
- **Context**: Task 2.6 required wiring the P2P sync layer from `chronogrid_net` into `chronogrid_core` so that CRDT delta packets received from remote peers are automatically merged into the local SQLCipher database, and local CRDT mutations are broadcast to connected peers.
|
||
- **Decision**:
|
||
- `chronogrid_net` added as an **optional dependency** under the `p2p` feature flag in `chronogrid_core/Cargo.toml`; `tokio` (sync+rt) and `tracing` also gated behind `p2p`.
|
||
- New `core::sync` module with `SyncEngine` struct: owns `Arc<Mutex<Database>>`, `SwarmHandle`, and a `broadcast::Receiver<NetEvent>`.
|
||
- Background Tokio task loops on `event_rx.recv()`: `NetEvent::DeltasReceived` → `merge_remote_deltas()` → Automerge merge → SQL row update.
|
||
- `merge_remote_deltas(deltas: Vec<DeltaPacket>) -> Result<Vec<String>>` returns affected entity IDs for UI cache invalidation.
|
||
- `notify_local_change(entity_id)` calls `swarm_handle.trigger_sync()` to push local mutations to peers.
|
||
- `CoreError::Sync(String)` variant added for sync-specific errors.
|
||
- `SwarmHandle::new_for_test()` added to `chronogrid_net` for unit test isolation (no live swarm required).
|
||
- 4 unit tests gated behind `#[cfg(all(test, feature = "p2p"))]`: task delta merge, note delta merge, empty delta list, engine construction.
|
||
- Full ADR at `docs/ARCHITECTURE/ADR/0012-core-sync-engine-p2p-integration.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**.
|
||
|
||
## [2026-08-23] - ADL-007: chronogrid_daemon — Headless Peer Node Binary Scaffold
|
||
|
||
- **Status**: Accepted
|
||
- **Context**: Task 2.7 required scaffolding the `apps/chronogrid_daemon/` headless binary crate, composing all Phase 2 components (`chronogrid_crypto`, `chronogrid_core`, `chronogrid_net`) into a runnable daemon process.
|
||
- **Decision**:
|
||
- Added `apps/chronogrid_daemon` to the Cargo workspace `members` list.
|
||
- Created `apps/chronogrid_daemon/Cargo.toml` as a `[[bin]]` crate (`chronogrid-daemon`) with dependencies: `chronogrid_core` (feature `p2p`), `chronogrid_crypto`, `chronogrid_net`, `clap = "4"` (derive), `serde` (derive), `toml = "0.8"`, `tokio` (full), `tracing`, `tracing-subscriber` (env-filter + fmt), `anyhow`.
|
||
- `apps/chronogrid_daemon/src/main.rs` implements the full startup sequence in 8 annotated steps:
|
||
1. CLI argument parsing via `clap` (`--config`, `--db`, `--log-level`).
|
||
2. TOML config loading from platform-default path (`/etc/chronogrid/config.toml` or `%APPDATA%\ChronoGrid\config.toml`) with graceful fallback to built-in defaults.
|
||
3. Node identity (`NodeIdentity`) and swarm key (`SwarmKey`) load-or-create from configured paths.
|
||
4. Master key resolution: Argon2id KDF keyed from node+swarm fingerprint concatenation + random salt stored in OS keychain. On first boot the key is generated and persisted; on subsequent boots it is retrieved and re-derived.
|
||
5. Encrypted SQLCipher database opened via `chronogrid_core::Database::open()` with automatic parent directory creation.
|
||
6. `SwarmManager::start()` launched with parsed `Vec<Multiaddr>` listen addresses.
|
||
7. `SyncEngine::new()` bound to the swarm handle and started via `.run()`.
|
||
8. Graceful shutdown on SIGINT (all platforms) and SIGTERM (Unix via `tokio::signal::unix`).
|
||
- `chronogrid_net::lib.rs` extended to re-export `libp2p::Multiaddr` alongside `PeerId`, avoiding a direct `libp2p` dependency in the daemon crate.
|
||
- `apps/chronogrid_daemon/config.example.toml` — fully documented reference configuration.
|
||
- `docs/DEPLOYMENT/01-headless-daemon-setup.md` — comprehensive setup guide covering build, Linux systemd service, systemd unit hardening (NoNewPrivileges, ProtectSystem, etc.), cluster swarm-key distribution, CLI reference, key management, firewall rules, upgrade procedure, and troubleshooting table.
|
||
- Full ADR at `docs/ARCHITECTURE/ADR/0012-daemon-scaffold.md`.
|
||
|
||
## [2026-08-23] - ADL-008: Workspace-Wide Lint Cleanup & Test Determinism Fixes
|
||
|
||
- **Status**: Accepted
|
||
- **Context**: The VS Code Problems tab reported 51 diagnostics across the workspace. Investigation showed `flutter analyze` was already clean, so all reported problems originated from `cargo clippy --workspace --all-targets --all-features`. The dominant cluster (44 errors) was in `crates/chronogrid_core/src/sync/mod.rs`, whose `#[cfg(test)]` module had been written against a `TaskCrdt`/`NoteCrdt` API that no longer matched the implementation — meaning the `chronogrid_core` test target had never compiled since Task 2.6.
|
||
- **Decision**:
|
||
- **`chronogrid_core::sync` tests realigned to the real CRDT API**: `TaskCrdt::new(id, namespace_id, title, hlc) -> Result<Self>` and `NoteCrdt::new(id, namespace_id, title, content_markdown, hlc)`; `set_*(value, hlc)` argument order (previously reversed); `from_bytes()` instead of the non-existent `load()`; getters return `Result<Option<String>>` rather than `Option<&str>`. Added `make_task`/`make_note` helpers and switched delta helpers to `&mut` (Automerge `save()` requires `&mut self`).
|
||
- **`CrdtDoc`** gained an `impl Default` (clippy `new_without_default`); the `put_get_f64_round_trip` test constant changed from `3.14` to `12.345_678_9` (clippy `approx_constant` flagged the former as an approximate `PI`).
|
||
- **`chronogrid_daemon`**: `Config` now derives `Default`. The previously dead `logging`/`level` fields were resolved *functionally* rather than by suppression — configuration is now loaded **before** tracing initialisation so `[logging].level` genuinely drives the subscriber. Precedence is `RUST_LOG` > `--log-level` > `[logging].level` > `"info"`. A `LoadedConfig` struct carries load provenance so the "config not found" warning can still be emitted after the subscriber exists.
|
||
- **Minor lints**: `div_ceil` in `chronogrid_crypto::keychain`; needless borrow in `chronogrid_net::swarm_key`; collapsible `if let` folded into the `match` arm in `chronogrid_net::swarm_manager`; `assert!(true)` in `chronogrid_ai` replaced with a real `VERSION` constant and assertion.
|
||
- **Bug 1 — unreachable test contract in `SyncEngine`** (exposed once the crate compiled): `SyncEngine` owns a `SwarmHandle` holding a `broadcast::Sender` clone, and `run()` moves `self` into the spawned task. A live sender therefore always exists *inside* the loop, so `RecvError::Closed` can never fire when an external `event_tx` is dropped. The former `run_event_loop_stops_on_channel_close` test asserted an impossible condition and hung. Replaced with `JoinHandle::abort()`-based teardown, an explanatory comment block, and a new `run_event_loop_survives_sender_drop` regression guard.
|
||
- **Bug 2 — parallel-test race on the FFI `ENGINE` singleton**: `chronogrid_ffi` tests mutate a process-wide `static Mutex<Option<Engine>>` while `cargo test` runs them across threads, producing intermittent `engine not initialized` / `engine is already initialized` failures. This was failing the gating `scripts/verify_agent_work.ps1` pipeline. Added a poison-tolerant `engine_test_guard()` in `api/state.rs` (`#[cfg(test)]` only) and routed all 21 engine-touching tests in `engine.rs`, `database.rs`, `task_crdt.rs`, and `note_crdt.rs` through it. This restores determinism without forcing `--test-threads=1` on the whole suite.
|
||
- **IDE/toolchain alignment**: added `.vscode/settings.json` setting `rust-analyzer.cargo.features = "all"` plus a clippy-based check command. `chronogrid_core::sync` is gated behind the optional `p2p` feature; rust-analyzer defaults to the crate's *default* feature set, so every `tracing::`, `tokio::`, `broadcast::`, and `chronogrid_net::` path in `sync/mod.rs` appeared unresolved in the Problems tab even when `cargo clippy --all-features` was clean. Note that `.vscode/` is currently listed in `.gitignore`, so this setting is machine-local; making `p2p` a default feature remains an option if the phantom diagnostics recur for other contributors.
|
||
- **Verification**: `cargo clippy --workspace --all-targets --all-features` → 0 warnings / 0 errors; `cargo test --workspace --all-features` → 221 passed / 0 failed; `flutter analyze` → clean; `scripts/verify_agent_work.ps1` → ALL VERIFICATION CHECKS PASSED. FFI determinism confirmed over 5 consecutive parallel runs (25/25 each).
|
||
- **No schema changes** — `docs/API_CONTRACTS/schema.sql` untouched; no ADR required as no architectural boundary changed.
|