layered-soul/skills/colibri-development/references/store-integration.md
Sam & Claude 4d8ce07fa7 docs: apply Prettier to current markdown (Sam & Codex)
Normalize markdown formatting after the latest main updates.\n\nChecks: python3 scripts/layered_soul.py validate .; npx --yes prettier@3 --check '**/*.md'; git diff --check.
2026-06-14 01:48:32 +02:00

2.6 KiB

colibri-store Integration Pattern

How the colibri-store crate was integrated into the daemon (2026-05-27), serving as a template for future crate additions to the Colibri workspace.

Crate Structure

crates/colibri-store/
  Cargo.toml          — rusqlite (bundled), serde, chrono, thiserror, uuid
  src/
    lib.rs            — Store, Task, Agent, Skill types + public API
    schema.rs         — CREATE TABLE IF NOT EXISTS statements (idempotent)

Adding to Workspace

  1. Create crate directory + Cargo.toml with edition = "2021"
  2. Add to root Cargo.toml workspace members array
  3. cargo build -p colibri-store — verify
  4. cargo test -p colibri-store — 7 tests (in-memory SQLite)

Integrating with DaemonState

Step 1: Add dep to daemon Cargo.toml

colibri-store = { path = "../colibri-store" }

Step 2: Extend DaemonConfig

pub struct DaemonConfig {
    // ...
    pub db_path: PathBuf,
}

Initialize from colibri_store::default_db_path() which handles:

  • COLIBRI_DB_PATH env override
  • FreeBSD: /var/db/colibri/colibri.sqlite
  • Linux: $XDG_DATA_HOME/colibri/colibri.sqlite

Step 3: Add to DaemonState

pub struct DaemonState {
    // ...
    pub store: std::sync::Mutex<Store>,  // NOT Store — Connection is !Sync
}

Step 4: Open on startup

let store = Store::open(&config.db_path)
    .unwrap_or_else(|e| panic!("..."));
// Wrap in Mutex for Send + Sync

Step 5: Update all config constructors

Every DaemonConfig { ... } literal needs db_path added. Affected files in this integration:

  • crates/colibri-daemon/src/socket.rs — test_config()
  • crates/colibri-client/tests/live_socket_smoke.rs — smoke_config()

Step 6: Add socket commands

Extend HerdrCommand enum with coordination variants (list-tasks, create-task, transition-task, claim-task, list-agents, register-agent, list-skills, register-skill). Add dispatch arms + handler functions in socket.rs.

All store access through state.store.lock().unwrap() — short-lived locks, no hold-across-await.

Key pitfall: rusqlite::Connection is !Sync

Connection contains RefCell<LruCache<Arc<str>, RawStatement>> making it not Sync. Wrapping in std::sync::Mutex provides Sync while keeping the single-writer model (which is appropriate for SQLite WAL — one writer, many readers through snapshots).

Key pitfall: TaskStatus::from_str naming

Clippy's should_implement_trait lint flags from_str as confusing with std::str::FromStr::from_str. Use parse_status() instead, then #[allow(...)] is not needed.