Summary
Once dactyl is published with the public API dactyl::read(datastore, query, optimize) / dactyl::write(datastore, query, optimize) and dactyl::init(cfg), every direct read/write of .decapod/data/*.db (and .decapod/data/*.jsonl) inside src/decapod/ must be replaced with a dactyl call.
Goal
By the end of this issue, Decapod contains no direct SQLite or HTTP-to-Neon call sites at store boundaries. Every store interaction goes through dactyl, with a universal SQL-flavored query authored at the call site and a datastore value forwarded from dactyl::active_datastore().
Call shape (authoritative)
// Per-function call site in src/decapod/
let datastore = dactyl::active_datastore(); // "sqlite" | "neon"
let query = "select id, title, status from todos where assignee = $1"; // universal SQL-flavored
let optimize = true; // let dactyl rewrite for the active datastore
dactyl::read(datastore, query, optimize)
Notes:
datastore MUST come from dactyl::active_datastore(). Call sites must NOT re-derive datastore from repo.effective_backend() — that translation is owned exclusively by #1112 in src/decapod/dactyl_bridge.rs. Duplicating the translation at a call site re-introduces the leak this epic exists to remove.
- The query string is authored in Decapod, per function, in universal SQL.
dactyl parses it and (when optimize = true) rewrites it for the active datastore.
- For hard-coded queries, prefer
dactyl::query!("select ...") so dialect/datastore mismatches are caught at compile time. optimize defaults to true inside the macro; pass optimize = false to require verbatim forward.
Scope of files to migrate
Audit src/decapod/core/ for any direct .decapod/data/*.db / *.jsonl access:
core/db.rs
core/broker.rs (writes broker_dedupe.db, appends broker.events.jsonl)
core/knowledge.rs (writes knowledge.db)
core/governance_artifacts.rs (writes governance.db)
core/memory.rs (writes memory.db)
core/automation.rs (writes automation.db)
core/lcm.rs (writes lcm.db)
core/federation.rs (reads/writes federation/_graph.json, _index.md)
core/todo.rs (writes todo.db, appends todo.events.jsonl)
core/flight_recorder.rs (appends traces.jsonl, verification_events.jsonl, external_actions.events.jsonl)
core/repomap.rs if it touches data files
- any other module that opens a SQLite handle or appends to a
.events.jsonl
Acceptance criteria
Out of scope
Related
Summary
Once
dactylis published with the public APIdactyl::read(datastore, query, optimize)/dactyl::write(datastore, query, optimize)anddactyl::init(cfg), every direct read/write of.decapod/data/*.db(and.decapod/data/*.jsonl) insidesrc/decapod/must be replaced with adactylcall.Goal
By the end of this issue, Decapod contains no direct SQLite or HTTP-to-Neon call sites at store boundaries. Every store interaction goes through
dactyl, with a universal SQL-flavored query authored at the call site and adatastorevalue forwarded fromdactyl::active_datastore().Call shape (authoritative)
Notes:
datastoreMUST come fromdactyl::active_datastore(). Call sites must NOT re-derivedatastorefromrepo.effective_backend()— that translation is owned exclusively by#1112insrc/decapod/dactyl_bridge.rs. Duplicating the translation at a call site re-introduces the leak this epic exists to remove.dactylparses it and (whenoptimize = true) rewrites it for the active datastore.dactyl::query!("select ...")so dialect/datastore mismatches are caught at compile time.optimizedefaults totrueinside the macro; passoptimize = falseto require verbatim forward.Scope of files to migrate
Audit
src/decapod/core/for any direct.decapod/data/*.db/*.jsonlaccess:core/db.rscore/broker.rs(writesbroker_dedupe.db, appendsbroker.events.jsonl)core/knowledge.rs(writesknowledge.db)core/governance_artifacts.rs(writesgovernance.db)core/memory.rs(writesmemory.db)core/automation.rs(writesautomation.db)core/lcm.rs(writeslcm.db)core/federation.rs(reads/writesfederation/_graph.json,_index.md)core/todo.rs(writestodo.db, appendstodo.events.jsonl)core/flight_recorder.rs(appendstraces.jsonl,verification_events.jsonl,external_actions.events.jsonl)core/repomap.rsif it touches data files.events.jsonlAcceptance criteria
grep -RIn "rusqlite\|sqlx::\|Connection::open\|\\.events\\.jsonl" src/decapod/returns no matches outside ofsrc/decapod/dactyl_bridge.rs(the single allowed call site that initializes dactyl and forwardsbackend -> datastore).src/decapod/uses the formdactyl::read(datastore, query, optimize)ordactyl::write(datastore, query, optimize), withdatastoresourced fromdactyl::active_datastore().queryis authored in universal SQL-flavored syntax at the call site, with parameters passed alongside.effective_backend() -> datastoretranslation. That logic lives only in#1112.decapod validatepasses for bothbackend = "local"andbackend = "cloud".cargo test --libpasses.Out of scope
dactylcrate itself → tracked in [dactyl] Bootstrap crate: unified read/write facade with both adapters (sqlite, neon) in-tree, gated by (datastore, query, optimize) dactyl#1.backend -> datastoretranslation point +dactyl::init(cfg)call → tracked in [dactyl] Add single backend->datastore translation point and dactyl::init(cfg) call in Decapod #1112.Related
dactylcrate bootstrap (defines the public API this issue calls into): [dactyl] Bootstrap crate: unified read/write facade with both adapters (sqlite, neon) in-tree, gated by (datastore, query, optimize) dactyl#1dactylconformance harness (proves the call sites round-trip identically): [dactyl] Conformance harness: same universal query must round-trip identically across datastore = sqlite and datastore = neon, with optimize = true and optimize = false dactyl#2dactyl::init(cfg)(the one allowed place that derivesdatastorefrombackend): [dactyl] Add single backend->datastore translation point and dactyl::init(cfg) call in Decapod #1112