chronogrid/docs/ARCHITECTURE/ADR/0007-schema-extension-phase...

6.1 KiB

ADR-0007: Schema Extension — Phase 2 Missing Entities (Task 2.1)

Date: 2026-08-22
Status: Accepted
Task: 2.1 — Extend Schema with Missing Entities
Author: Autonomous Agent Cycle


Context

The Phase 2 completion criteria for ChronoGrid P2P require 13 total database tables (7 original Phase 1 tables + 6 new Phase 2 extension tables). The architecture review (ADL-006) identified 6 missing schema tables as a critical gap blocking Phase 3 (Flutter UI requires calendar/time-block entities) and Phase 4 (AI/automation requires ingestion and rule entities).

The canonical schema at docs/API_CONTRACTS/schema.sql is embedded at compile time via include_str! in crates/chronogrid_core/src/db.rs, making it the single source of truth for all database structure.


Decision

New Tables Added

Six new tables were added to docs/API_CONTRACTS/schema.sql:

1. time_blocks

  • Purpose: Calendar time-blocking entries linked to tasks. Represents a scheduled block of time on the calendar grid.
  • Key fields: id, namespace_id, task_id (nullable FK → tasks, SET NULL), title, start_time, end_time, color_hex, is_all_day, recurrence_rule, hlc_timestamp
  • CRDT: Supports entity_type = 'time_block' in crdt_docs table (documented in comment and tested).
  • Indexes: idx_time_blocks_namespace, idx_time_blocks_task, idx_time_blocks_start

2. attachments

  • Purpose: Binary/file attachments linked to notes or ingestion logs.
  • Key fields: id, note_id (nullable FK → notes, CASCADE), ingestion_log_id (nullable FK → ingestion_logs, CASCADE), file_name, mime_type, file_size_bytes, storage_path, checksum_sha256, hlc_timestamp
  • Design note: storage_path is a relative path inside the encrypted vault directory; actual file bytes are stored outside SQLite to avoid BLOB size limits.
  • Indexes: idx_attachments_note, idx_attachments_ingestion

3. calendar_feeds

  • Purpose: CalDAV external feed configuration per namespace.
  • Key fields: id, namespace_id (FK → namespaces, CASCADE), display_name, feed_url, username, encrypted_password_blob (AES-GCM encrypted, key from keychain), sync_interval_seconds, last_synced_at, is_enabled, hlc_timestamp
  • Security: Credentials are stored as encrypted blobs — never plaintext.
  • Indexes: idx_calendar_feeds_namespace

4. node_peers

  • Purpose: P2P peer registry — NodeId, public key, last seen, swarm membership.
  • Key fields: id (NodeId: Ed25519 public key hash hex), public_key_bytes (BLOB, 32 bytes), swarm_key_fingerprint, last_seen_at, last_known_addrs (JSON array of multiaddr strings), is_trusted
  • Design note: No FK to namespaces — peers are global to the node, not namespace-scoped.
  • No hlc_timestamp: Peer registry entries are managed by the networking layer, not the CRDT engine.
  • Indexes: idx_node_peers_swarm, idx_node_peers_last_seen

5. automation_rules

  • Purpose: Local trigger/action rule definitions for the automation engine.
  • Key fields: id, namespace_id (FK → namespaces, CASCADE), name, trigger_type (task_created | task_status_changed | ingestion_received | schedule), trigger_config (JSON), action_type (set_field | create_task | send_notification | run_script), action_config (JSON), is_enabled, execution_count, last_executed_at, hlc_timestamp
  • Indexes: idx_automation_rules_namespace, idx_automation_rules_trigger

6. ingestion_configs

  • Purpose: IMAP account and file-watcher configuration per namespace.
  • Key fields: id, namespace_id (FK → namespaces, CASCADE), config_type (imap | file_watcher | hotkey), IMAP-specific fields (imap_host, imap_port, imap_username, encrypted_imap_password_blob, imap_mailbox, imap_use_tls), file-watcher fields (watch_path, watch_extensions JSON array), poll_interval_seconds, last_polled_at, hlc_timestamp
  • Design note: Single table covers multiple ingestion types via config_type discriminator; type-specific fields are nullable for non-applicable types.
  • Indexes: idx_ingestion_configs_namespace, idx_ingestion_configs_type

CRDT Compatibility

All 6 new tables include hlc_timestamp TEXT NOT NULL (except node_peers, which is managed by the networking layer and does not participate in CRDT sync). The crdt_docs table comment was updated to document time_block as a valid entity_type value alongside task and note.


Code Changes

File Change
docs/API_CONTRACTS/schema.sql Added 6 new tables + 12 new indexes
crates/chronogrid_core/src/db.rs Updated EXPECTED_TABLES from 7 to 13 entries
crates/chronogrid_core/src/db.rs Added 8 new unit tests covering all Phase 2 tables

Test Coverage Added

  • schema_has_exactly_13_tables — asserts total table count = 13
  • open_in_memory_creates_indexes — expanded to cover all 17 indexes (5 Phase 1 + 12 Phase 2)
  • crud_time_blocks — insert, read, FK SET NULL on task deletion
  • crud_attachments — insert, read, CASCADE delete with note
  • crud_calendar_feeds — insert, read, CASCADE delete with namespace
  • crud_node_peers — insert, read, update last_seen_at
  • crud_automation_rules — insert, read, CASCADE delete with namespace
  • crud_ingestion_configs — IMAP + file-watcher configs, CASCADE delete
  • crdt_doc_time_block_entity_type — verifies time_block entity_type in crdt_docs

Consequences

  • Positive: All Phase 2 completion criteria for schema are met (13 tables confirmed by verification script).
  • Positive: EXPECTED_TABLES validation in db.rs now enforces all 13 tables on every database open, preventing schema drift.
  • Positive: Existing Phase 1 tests are unaffected — all DDL uses CREATE TABLE IF NOT EXISTS.
  • Neutral: node_peers intentionally omits hlc_timestamp as it is managed by chronogrid_net, not the CRDT engine.
  • Neutral: attachments.storage_path stores relative paths; the vault directory root is resolved at runtime by the application layer.