chronogrid/DECISION_LOG.md

26 KiB
Raw Permalink Blame History

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.