{{command_prefix}}state
The Phase 57 SQLite state backbone is opt-in (--migrate-state) and fully reversible.
Markdown .design/STATE.md is always the human-editable SoT; SQLite is a faster query
layer derived from it. This skill exposes three subcommands for operating on that layer.
Subcommands
| Subcommand | What it does |
|---|---|
{{command_prefix}}state query "<sql>" | Run a read-only SELECT against .design/state.sqlite. |
{{command_prefix}}state recover | Rotate the current sqlite to a .bak and rebuild from markdown. |
{{command_prefix}}state demigrate | Remove .design/state.sqlite; markdown becomes the SoT again. |
query
Execute a read-only SELECT against the state database.
The engine opens the file with readonly:true so all writes are rejected at the
engine level. A first-token denylist (Set membership, no regex) additionally blocks
DROP, DELETE, UPDATE, INSERT, ALTER, ATTACH, CREATE, PRAGMA, VACUUM, ANALYZE,
REINDEX, and REPLACE before the connection is opened.
bashnode -e ' const qs = require("./scripts/lib/state/query-surface.cjs"); const result = qs.query(process.argv[1], { projectRoot: process.cwd() }); console.log(JSON.stringify(result, null, 2)); ' "<sql>"
Outputs { rows: [...], backend: "sqlite" } on success, or
{ degraded: true, message: "..." } when SQLite is not active.
Degrades gracefully (no throw) when BACKEND==='markdown' or when
.design/state.sqlite has not been created yet (run --migrate-state first).
recover
Rotate the current (possibly corrupt) .design/state.sqlite to .bak.0, then
rebuild a fresh database from the markdown .design/STATE.md using
migrate-to-sqlite.cjs in force mode. Runs PRAGMA integrity_check on the
result and reports the outcome.
bashnode -e ' const qs = require("./scripts/lib/state/query-surface.cjs"); const result = qs.recover({ projectRoot: process.cwd() }); console.log(JSON.stringify(result, null, 2)); '
Outputs { recovered: true, integrity: true, message: "..." } on success.
Backup rotation keeps at most 10 files (.bak.0 through .bak.9). The oldest
backup is overwritten when the cap is reached.
demigrate
Remove .design/state.sqlite so the markdown STATE.md becomes the SoT again.
Idempotent: if the file does not exist, returns a clear no-op message without error.
A backup is taken in .bak.0 before removal.
bashnode -e ' const qs = require("./scripts/lib/state/query-surface.cjs"); const result = qs.demigrate({ projectRoot: process.cwd() }); console.log(JSON.stringify(result, null, 2)); '
Outputs { demigrated: true, message: "..." } when the file was removed, or
{ demigrated: false, message: "..." } when it was already absent (no-op).
To re-enable SQLite after a demigrate, run --migrate-state again.
Key design decisions
- Markdown is always the SoT. SQLite is opt-in via
--migrate-stateand reversible viademigrate. The markdown file is never silently overwritten. - Read-only queries only. The
querysubcommand enforces SELECT-only via both the engine (readonly:true) and a defense-in-depth denylist. No writes are possible through this skill. - Backup rotation cap.
rotateBakshifts.bak.0..8up by one index and caps at.bak.9(10 files total). The oldest backup is overwritten automatically. - Graceful degradation. All subcommands return a clear
{ degraded, message }object (no throw) whenbetter-sqlite3is not installed or whenGDD_STATE_BACKEND=markdownis set.


