spec-node-states¶
Provenance¶
- Source:
.spec/spexcode/spec-cli/source-of-truth/spec-node-states/spec.md - Source SHA-256:
e0a2a4e395b7305ab7e3e8fea3ebd0191d1f899e2f9ce75db98d45881baa5a82
A spec node's status is mostly derived in the backend from what git and the live worktrees
actually say — with one declared exception (pending todos, below). There are four states, evaluated
in this precedence (deriveStatus in [[source-of-truth]]'s specs.ts):
- pending (declared todo) — a node whose frontmatter says
status: pendingand has no implementing code yet (empty/absentcode:, no drift) is a written-but-unbuilt spec. It reads pending regardless of how manyspec.mdcommits it has, and even while a worktree is merely adding its text (overlay). This is checked first so authoring a roadmap spec doesn't flip it toactive/mergedjust by existing in git. The arrival of governed code (a non-emptycode:list, or drift) graduates it — from then on it derives like any coded node. - active — an unmerged managed worktree has pending ops on a coded node (it carries a board overlay). Live, in-flight work. Only the board assembler knows the overlay, so this state can only be produced there.
- drift — the governed code has moved ahead of the spec's latest version (
drift > 0, by git ancestry). The spec may be stale. - merged — the node has committed version(s) and is in sync (no drift, no in-flight work).
A node with no committed version and no declared status also reads pending (the empty default).
Frontmatter status otherwise survives only as a fallback: if git is unreadable every node would
collapse to version 0, so a node that declared active/merged/drift still shows that intent.
For coded nodes the declared value is otherwise ignored — the derivation is authoritative.
Because active needs the overlay, the derivation runs in two places over one shared helper:
loadSpecs derives from git alone (so /api/specs reports pending/drift/merged), and buildBoard
([[sessions]]) re-derives with the overlay so a node a worktree is touching reads active. A
ghost node (one a worktree is adding, not yet on main) therefore reads active, never pending.
The same end-to-end tracing covers the two other state vocabularies the board shows:
- overlay op-types —
added·edited·deleted·moved, computed per worktree vs main and stamped on the node as glyphs (+ ~ ✕ →) in the colour of the authoring session (see [[node-graph]] for the surfacing). - session states — the worktree state machine, traced HARD (explicit writes, then one liveness
check + one guarded inference — no text-sniffing the TUI). The agent's own writes are authoritative and
reconcilereturns each directly:awaiting's proposalsreview·done·close-pending, plusparked·error·asking.askingis captured deterministically the instant the agent invokes the AskUserQuestion tool — the singlePreToolUsemark-active hook readstool_namefrom the payload and writesasking(the question → note), elseactive— and is also self-declarable viaspex session ask. The only LIVE-derived values areworking·idle·offline, from one liveness check (a dead tmux or a bare-shell pane = offline) plus one guarded inference (idle, written active-only by theidle_promptNotification hook so it never clobbers a declaration). Each is carried straight to the session window's status dot (see [[sessions]] and [[session-console]]).
The dashboard surfaces the derived node status as the row's dot colour and label (green=merged, orange=active, yellow=drift, grey=pending), with the drift count still shown as its own badge — see [[node-graph]].