Spine is the ledger that agent sessions file into. A session is a process that appears, edits a shared tree and vanishes, and git never records that it existed. Spine records who is working, on what, since when, holding which files, and what happened. It records; it does not act.
How the pieces connect
Solid lines are calls, or writes to the store. Dashed lines are reads. Boxes with a gold edge ship from this repository. The gold line is where spine leads back to a session. The Spine extension opens an existing session's tab by its transcript id, whether that session runs in a window, in a terminal or headless under claude -p. It also opens a new tab under a launch profile through spine-qwen/.
A gold edge means a box ships from this repository, and the two at the bottom right are its extensions rather than the ledger: the VS Code extension and the launcher. A dashed gold edge is proposed and not built yet. The store is drawn twice for that reason. It is ~/.substrate/tasks.db today, named for the repository spine used to live in, and its home is ~/.spine/tasks.db. migrate-store.mjs moves it and refuses while any process still holds the old path, because SQLite names the write-ahead log after the path a connection was opened with, and two paths in use at once is two logs over one database.
Every write this repository makes goes through db.mjs. Two things outside it touch the store: substrate's server, which imports db.mjs itself, and the Rust engine, whose proxy reads the store and writes one column, a task's status.
A session, from open to receipt
Closing a session writes a receipt and changes nothing else. A task still active when its session closes stays active, because the ledger records what happened and decides nothing on anyone's behalf.
How a session row knows its transcript and its window
At open, identity.mjs walks the process tree from the MCP server up to the VS Code main process. It matches that pid against the IDE lock files, preferring the lock whose workspace contains the session's cwd. The presence file Claude Code wrote about itself gives the transcript id. A headless run has no window above it, so its row carries a transcript and no window. The board still lists it, by joining the presence files on that transcript id.
The five tables
table
one row is
the rule that shapes it
plans
named work with a cwd and a project slug
created → ready → active → complete → reviewed → archived, never deleted; review is written at reviewed; dormancy is computed, never stored
tasks
a tree address inside a plan
result is terminal, notes always mutable, abandon_reason visible to siblings
file_claims
a task saying it is editing a path
a declaration, not a lock: it makes a collision visible and stops no write
sessions
a process that opened the ledger
every id carries how it was known: self, presence, ppid-walk, lockfile
overlook
a posture reading against a session
refuses an unknown session rather than writing an orphan
The surfaces spine ships
MCP tools, seen by a session as mcp__spine__*, each dispatched by name to a db.mjs function of the same shape.
Commands, skills and hooks under .claude/, linked into ~/.claude so every window has them. None of the hooks reads or writes the store.
Sweeps: reap.mjs retires sessions whose process is gone, and snapshot.mjs copies the store and refuses to claim a copy it cannot reopen.
The board: board.mjs joins live sessions to the tasks they hold, with the evidence for each attribution.
The extension: vscode/ renders the board as its own tab and opens new session tabs under a chosen profile, without touching the default Claude settings.
Launch profiles: spine-qwen/ runs Claude Code or Codex against a local model in a config dir that belongs to that profile alone. The claude profile is a passthrough.
The site: built from this tree, with the tool reference generated from a live tools/list.
The Rust engine: evaluation and posture tooling that reads the same store through a proxy which fails closed when the store is absent.
Regenerating the figures
The SVGs are generated by docs/diagrams.mjs, never edited by hand. Run the first command after changing it, and the second to check that the committed files still match. The site build inlines the generator's output directly, so a stale committed file cannot reach the website.