Local state — ~/.abc/db/local.db
The abc CLI keeps local state in a single SQLite database at
~/.abc/db/local.db. The driver is pure Go (modernc.org/sqlite), so the
binary remains CGO-free.
Moved 2026-06-02: the file now lives at
~/.abc/db/local.db(under adb/subdirectory) so the home directory stays tidy. On first invocation post-upgrade, the CLI moves a flat~/.abc/local.db(plus its WAL/SHM sidecars andlocal.db.backup-pre-*files) into~/.abc/db/in place.Renamed 2026-05-08: the file was previously
~/.abc/state.db. The CLI still auto-renames a legacystate.db(plus sidecars/backups) tolocal.dbbefore the move above. Both migrations run automatically on first invocation post-upgrade, print a one-line stderr note, and require no manual action.
Tables
| Table | Purpose |
|---|---|
projects | Top-level research project (slug + ULID, scoped per cluster context) |
investigations | Branchable, mergeable explorations under a project |
annotations | Free-form notes attached to investigations. Tags stored as a JSON array in tags_json; soft-delete via withdrawn_at / withdrawn_reason. The legacy singular tag column is preserved (additive migration) for cross-version readers. |
annotation_revisions | Audit trail for every annotation mutation. One row per body edit / tag change / move / withdraw / restore, capturing the previous state of whichever field changed. Walked oldest→newest to reconstruct history. |
runs | Every pipeline / job / module submission, auto-attached to active project + investigation. Carries cpu_hours, memory_gb_hours, walltime_seconds, gpu_count, scratch_gb (transient scratch reservation), exit_code, and exit_reason for cost / emissions reporting (abc report). The completion fields are written by the run-watcher (internal/runner/watcher.go) — best-effort, polled from Nomad every 5 s up to a 24 h ceiling. |
active_pointers | Per-context "active project" / "active investigation" pointers |
cli_audit | Per-invocation command log: verb, redacted argv, exit code, duration ms, user ULID. Set ABC_NO_AUDIT=1 to disable. Feeds abc report. |
citations | Cross-investigation citation edges (populated by abc project investigation annotate --cites=<inv>:<aid>). |
freezes, container_digests, pipeline_metadata, telemetry_queue | Forward-compatible substrate (no consumers in this release) |
Operational rules
- WAL mode +
busy_timeout=5000ms; safe under concurrent CLI invocations. - Foreign keys ON; cascading deletes on
abc project delete. - All write transactions use
BEGIN IMMEDIATEsemantics via the driver. - Every CLI invocation auto-applies pending migrations.
Admin commands
abc localdb status— binary version, DB path, schema version, applied/pending/future migrations, table row counts, WAL size, applied-migration history.abc localdb migrate— explicitly apply pending migrations.abc localdb vacuum— reclaim space (VACUUM).
The
abc cachegroup is the deprecated former name ofabc localdb. It remains as an alias for one release and prints a one-line deprecation note at invocation; new scripts should useabc localdb.
Schema versioning and migrations
The DB carries its schema version in the schema_migrations table. The
binary embeds a set of migration files under internal/state/migrations/.
On every state.Open() (which fires on every DB-backed command) the CLI:
- Compares applied migrations vs embedded migrations.
- DB ahead of binary (any applied row not in the embed) — refuse to
open with
ErrSchemaAheadand a clear "upgradeabc" message. Prevents an old binary from operating against a future schema. - Binary ahead of DB (embedded migrations not yet applied) — write a
pre-migration backup to
~/.abc/db/local.db.backup-pre-<version>-<unix>, then apply each pending migration in its own IMMEDIATE transaction. The most recent 5 backups are retained automatically; older ones pruned. - Equal — no-op.
Each schema_migrations row records the CLI version that applied it
(applied_by_cli_version), so abc localdb status shows the full provenance
chain.
Migration authoring rule
Migrations are forward-only. The rule has three parts:
- Never edit a shipped migration file. Once a
0NNN_*.sqlhas been applied to any user'slocal.db, its content is frozen. To change the schema, write a new migration that walks the schema forward. - New migration filename =
NNNN_<short_description>.sqlwhereNNNNis one greater than the current highest filename ininternal/state/migrations/. UseCREATE TABLE IF NOT EXISTS,ALTER TABLE … ADD COLUMN, andCREATE INDEX IF NOT EXISTSso the migration is idempotent against partial replays. - The numeric sequence may have gaps —
0003is intentionally absent (it was renumbered to0005historically). The migration driver applies files in lexicographic order from whatever set is embedded; gaps are harmless. Do NOT renumber existing files to "fill" the gap.
After writing the migration, add or update unit tests in
internal/state/migrations/migrations_test.go to exercise the new schema,
and bump the CLI version in the next release (-ldflags "-X cmd.version=…")
so the audit trail records which binary applied which migration.
Recovering from a failed migration
If a migration fails mid-apply, the IMMEDIATE transaction rolls back, the DB stays at the pre-migration version, and the error message points at the backup file written before the attempt:
~/.abc/db/local.db.backup-pre-NNNN_<name>-<unix>
To restore: stop all abc processes, replace ~/.abc/db/local.db with the
backup, then re-run abc localdb status to confirm the schema version.
Backup
~/.abc/db/local.db is a single file. Copy it (with the -wal and -shm
sidecars while idle) to back up. The CLI never writes anything cluster-side
that depends on this file. Pre-migration backups (above) are an automatic
form of this for the migration boundary specifically.