---
kind: knowledge
when-and-why-to-read: When you are operating the canvas — spawning or steering nodes, deciding how work reports up, recovering a dormant or crashed node, or reasoning about the daemon — this reference should be read so lifecycle and reporting operations preserve one live engine, deliver results reliably, and recover nodes without corrupting state.
short-form: Operational model of the agent runtime — nodes on the canvas graph, spawn/delegate, the push/feed spine, lifecycle states, and revive (manual + daemon auto-revive).
system-prompt-visibility: name
file-read-visibility: none
---

# How nodes and the canvas work (operational)

Every agent is a **node** in one directed graph (the **canvas**). Each node has a canvas row, its own context dir, and one detached headless broker engine; tmux panes are only viewer surfaces that attach to a broker. The graph's edges are `subscribes_to` — the **spine** — and they decide who-wakes-whom.

The **daemon** (`crtrd`) is the sole owner of canvas persistent state: it is the only process that opens `canvas.db`, launches brokers, and writes runtime/model-auth, all behind an HTTP+WS API on a unix socket (dockerd model). Every `crtr` command, the web server, and the viewers are pure clients of that `/v1` API — they never open the store. A local interactive viewer still streams a node's broker socket (`view.sock`) directly, because that is broker IPC, not canvas state.

## Spawn & delegate

`crtr node new "<task>" --kind <kind>` launches a managed child broker and returns its id immediately; you **auto-subscribe** to it, so its finish wakes you. Delegating is the default move, not an optimization: a child's reading and tokens land in a fresh context window and only the conclusion comes back, keeping your own context (the scarce resource) free for steering.

- Match `--kind` to the work (`explore spec design plan developer review general`, plus any custom persona). See `node new -h`.
- Fan **independent** units out as concurrent children — a wake with idle workers is wasted. Serialize only true dependencies; never let two live children edit the same files.
- `--root` spawns an independent node you neither manage nor are woken by (e.g. one a human will drive).
- Once you delegate a unit, don't also run it yourself — you'll be woken when it finishes.

Navigate/steer: `surface node focus` (put an attach viewer for a node in your pane), `surface attach` (connect to a node's existing broker), `surface node cycle` (DFS-walk neighbors), `surface node id` (copy the current node id), `node message send` (direct-message any node at a wake tier, reviving a dormant target), `node subscription add`/`node subscription remove` (wire edges spawn didn't create). Survey with `canvas dashboard` (ASCII tree), `canvas browse` (interactive navigator), `node inspect list/show`, `canvas attention` (who's blocked on a human).

## The push/feed spine

Nothing is reported automatically — the feed contains only what a node **pushes**.

- `push update` — routine progress; fans a lightweight pointer to subscribers, no forced wake.
- `push urgent` — force-wakes every subscriber (you're blocked, scope changed, an error derails the plan).
- `push final` — the ONLY way a terminal node finishes: writes the canonical result, marks the node done, and tears down its broker. Stopping without it is not finishing. Resident/user-facing nodes stay dormant instead of finishing unless explicitly forced.

A push fans a ~30-token **pointer** (a ref path), not the content; subscribers dereference lazily. When a subscriber push wakes you, **the wake message already IS the coalesced digest** — dereference the refs that matter rather than trying to re-read it. An empty feed while workers run is normal — a worker that hasn't pushed yet leaves no pointer; the wake it fires when it does push is automatic, so there's nothing to poll or check in the meantime.

## Lifecycle

Two orthogonal axes:

- **mode**: base (a hands-on worker — finishes its own job, yielding mode-preservingly when an unsplittable task needs another window) ↔ orchestrator (`node promote` — a long-lived, roadmap-holding coordinator that delegates phases and survives context refresh via `node yield`). Promote only when what remains needs decomposition across children, not merely another window.
- **lifecycle**: terminal (owes a final up the spine, reaps when done) ↔ resident (`node config --lifecycle <value>` / interactable — stays dormant, wakes on inbox/human, never forced to finish). `node lifecycle demote` is the friendly flip-to-terminal-in-place.

A dormant node wakes from an **inbox** message (a push, or `node message send`) or from a scheduled **cron** whose bash command performs the action (`node message send`, `node lifecycle revive`, or `node new`). A cron can gate its action on an external condition such as CI or a deploy; inspect or reap schedules with `cron list`/`cron cancel`. To monitor your own children you arm nothing — you auto-subscribe on spawn, so their finish/crash/close wakes you; a deadline to chase a delegate is a belt-and-suspenders the runtime makes redundant. A terminal node blocked on a one-off message from a parent or controller it holds no live subscription to declares that wait with `node wait controller` before going dormant, so the next inbox delivery from that sender clears the wait rather than the runtime treating the idle as finished. Use `node wait deadline` only for the atomic inbox-versus-deadline race.

Tear-down: `node lifecycle close` cascade-cancels a node + its exclusive subtree WITHOUT finishing (revivable, nothing deleted); `node lifecycle recycle` finishes the agent in your pane and reboots a fresh root in place; `canvas prune` deletes terminal nodes past a TTL.

## Revive & the daemon

A dormant node (done/idle/dead/canceled) is reopened with `node lifecycle revive <id>` (resumes the saved conversation, or `--fresh` to restart clean); mass-reconnecting EVERY disconnected node at once (a reboot/crash recovery) is `canvas revive --all` instead. `reviveNode()` is the **only** sanctioned launcher of a node's broker engine — it builds the pi invocation, sets `CRTR_NODE_ID` + canvas extensions, runs `transition('revive')`, and starts the headless broker host, keeping the db row and broker process in lockstep. Never spawn `pi --session` raw, and never open a node by spawning pi directly — UIs go through `surface node focus` / `node lifecycle revive`.

The daemon (`crtrd`, managed via `sys daemon start/stop/status`) is also the supervisor: alongside owning the store and serving the API, it polls broker liveness via `pi_pid` and auto-revives active/idle nodes whose broker exited. It does NOT host agents, open viewers, or auto-revive *canceled* nodes (reach for `node lifecycle revive` for those). When activating source changes, build, run `npm run install-runtime`, then restart the daemon because new daemon/brokers select the atomically switched, immutable generation while live brokers retain their own. Restarting is safe: it never signals running nodes.
