State Model
Panopticon keeps almost no irreplaceable state of its own. The durable record of your work lives in systems that already exist for their own reasons — GitHub and git — and the local database is a derived cache: a fast view over those sources that holds nothing which can’t be rebuilt from them.
If you remember one thing from this page: the database is a cache, not a source of truth.
The sources of truth
Everything Panopticon needs — to show you a board, run the pipeline, or recover after a crash — is a function of four things that live outside its database:
| Source of truth | What it holds | Where it lives |
|---|
| GitHub Issues | What work exists and its lifecycle (todo / in progress / done) | github.com |
| Git repo & workspaces | The plan, the code, the feature branch, the PR, the per-issue record, task state | Your repo + origin |
| Agent transcripts | The conversation history to resume an agent mid-task | On disk, per harness (kept while work is active) |
| tmux | Which agents are alive right now | Local tmux server |
The local database is a cache
Panopticon uses a local SQLite database (~/.panopticon/panopticon.db) for two jobs:
- A uniform read/write API for the dashboard, the deacon, and the CLI — so every component talks to state the same way instead of each parsing files.
- A materialized view of the pipeline — so the board renders instantly instead of re-scanning every workspace on each page load.
Both are performance conveniences. The database stores nothing that cannot be recomputed from the sources of truth above.
The design principle: the database holds no state that isn’t derivable from GitHub + your workspaces. It is a cache in front of those sources, not a system of record — which is what keeps recovery and cross-machine work straightforward. The durable record of your work lives in git, not in a local file you have to back up.
The pipeline is derived, not stored
An issue’s pipeline phase is a function of GitHub + its workspace, not a value the database owns:
| Workspace / GitHub state | Phase |
|---|
| Feature branch + open tasks, no PR | Work |
| PR open, not approved | Review |
| PR approved | Merge |
| Issue closed on GitHub | Done |
Panopticon computes this view by walking the workspaces and reading GitHub, then caches the result. The cache is for speed; the derivation is the truth.
What lives where
| State | Durable home | In the cache? |
|---|
| Issue status / lifecycle | GitHub | ✅ cached |
| Plan (vBRIEF spec) | git — .pan/specs/ | ✅ cached |
| Code & branch | git / origin | — |
| Task completion (beads) | git (dolt-backed) | ✅ cached |
| Per-issue record — decisions, progress, harness/model, verdicts | git — the feature branch | ✅ cached |
| Conversation transcripts | on disk, per harness (while active) | indexed |
| Cost | derived from transcripts | ✅ cached |
| Live activity (who’s thinking) | ephemeral | cache only |
| Retry counters, merge-queue position | ephemeral bookkeeping | cache only — safe to lose |
The only things that vanish on a database loss are ephemeral counters — retry counts, queue positions. They gate loop-prevention, not your work; on a rebuild they reset, and the worst case is an agent gets a fresh set of attempts.
Search indexes are the exception
Panopticon also keeps search indexes in SQLite — full-text and semantic search over conversations and docs. Unlike pipeline state, a search index is legitimately a database’s job and persists as one. These indexes are rebuildable from the transcripts on disk, but they are a search feature, not part of the “pipeline is a cache” model.
The rules that keep this true
- All state mutations go through the database API — never ad-hoc writes to scattered files. The database is the single write surface; the per-issue git record is its durable mirror. This is enforced mechanically by
scripts/lint-state-writes.sh, which runs in npm run lint and CI.
- Legacy continue/feedback/session writers are being routed to the record writer. A small set of pre-PAN-1919 writers (continue files, feedback files, session JSONL) remain while they are migrated; the guard allowlists them explicitly and fails on any new ad-hoc state write.
- The database is reconstructable. A rebuild path walks every workspace + GitHub and repopulates the cache from scratch.
- The few facts a workspace can’t derive — an agent’s chosen harness/model, pipeline verdicts, retry counters — live in the per-issue record committed to the feature branch, so they travel across machines and survive a database loss.
Why it matters
- Recovery is trivial. Lost database, corrupted file, fresh machine —
git clone, start Panopticon, and the board rebuilds. No backups to restore, no event logs to replay.
- Work is portable. Because the durable record travels in git, another machine — or another engineer — can pull a branch and continue exactly where the last agent left off.
- The system is simple to reason about. Every piece of state has a clear home: if it isn’t derivable from GitHub + the workspaces, it lives in the per-issue git record — never only in the local cache.