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.
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
- Create crate directory +
Cargo.tomlwithedition = "2021" - Add to root
Cargo.tomlworkspace members array cargo build -p colibri-store— verifycargo 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_PATHenv 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.