# Vocabulary

Canonical terms for mu. Use these exact words in code, docs, error
messages, and the skill. If a doc uses a term not defined here, fix the
doc. Add a new term here before the code lands.

Verbs and flags live in `mu --help` and `mu <verb> --help`. Env vars are
in [reference/env.md](reference/env.md). Naming rules for flags, ids,
agent names, and file paths are in [reference/naming.md](reference/naming.md).

## Terms

### Topology and multiplexer

| Term | Meaning | Don't use |
| --- | --- | --- |
| **workstream** | The unit of organization: one **mux session** and one DB partition. | "project", "session" (alone), "context" |
| **scratch workstream** | The reserved workstream `scratch` for helpers outside any task DAG. Auto-created by `task add` and `agent spawn`; any other unknown `-w` is exit 3 (only `agent spawn` still creates its workstream) (`mu workstream init <name>` creates it); `mu workstream init scratch` is rejected. Its session keeps a `_mu` window, so it stays open after the last agent closes; `teardown --empty` skips it. | "throwaway ws", "temp workstream" |
| **multiplexer** / **mux** | The terminal program that owns panes: **tmux**, **herdr** or **Rex**. "mux" in code (`src/mux/`, `MU_MUX`), "multiplexer" in prose; one is active per invocation. | "terminal", "emulator", "backend" (alone) |
| **mux backend** | An implementation of `MuxBackend` (`src/mux/tmux.ts`, `src/mux/herdr.ts`, `src/mux/rex.ts`). Owns everything backend-specific. | "driver", "provider", "adapter" |
| **mux detection** | Picks the mux backend: `MU_MUX` → `HERDR_ENV=1` → `$TMUX` → a Rex terminal (`$REX_SESSION`) → binary on `PATH` (tmux, herdr, rex in that order) → `NoMultiplexerError`. | "auto-detect", "probe" |
| **mux session** | The container for one workstream's panes: tmux session `mu-<workstream>`, or herdr workspace or Rex session labelled `mu-<workstream>`. | "mux workspace", "session" (alone) |
| **tmux session** | The tmux flavour of a **mux session**. | "session" (alone) |
| **window** | The grouping inside a mux session (tmux window, herdr tab, Rex window), named by **window name**. | "tab" (except the `--tab` flag) |
| **window name** | A window's name: the agent's `--tab` value, or the agent name. | "tab name" |
| **pane** | One shell view inside a **window**. | "terminal", "shell" |
| **pane id** | The mux's stable pane handle: tmux `%15`, herdr `w1:p1`, Rex `block:<uuid>`. Not the volatile tmux pane index, which is never stored. | "pane number", "pane index" |
| **substrate** | An external system mu depends on: tmux, herdr, Rex, git, SQLite. | "dependency" (an npm dep), "service" |
| **crew** | Informal: the agents in a workstream. Prose only. | |

### Agents

| Term | Meaning | Don't use |
| --- | --- | --- |
| **agent** | A named process in a pane with a row in `agents`. Identity is `$MU_AGENT_NAME`; mu never reads or writes pane titles. | "subagent", "worker" (a role) |
| **worker** | An agent in its role as task claimer. Prefer **agent** when ambiguous. | |
| **remote worker** | An agent spawned with `--host <profile>`: its pane is local and is the transport, or the lease on it (a `session` profile's `shell` template runs there and forwards the remote pi's control socket; a `lease` profile's runs a **carrier** that owns a detached remote job), its process is remote, and its host, remote cwd, base and (with `--worktree`) remote repo are `agents.host/cwd/base/repo`; the repo is chosen per spawn (`--root`, else the profile's default). The pane dying is the agent dying (for a lease worker, the job's death is a separate stop: see **stop template**). | "REMOTE: note", "remote-env" | "remote agent record", "host registry" |
| **command gate** | A remote spawn of a non-pi command (`spawn --host dev --command 'npm test'`): a foreground pane, not a worker. Its `agents.gate_token` marks it and keys its remote completion record; `mu wait` returns the command's own numeric exit (event `exited`, `data.exit`). The pane is retained after the command ends until `agent close`. | "remote job", "gate worker" |
| **collect** | `mu agent collect <agent>`: run a remote worker's `--fetch` template locally and pin the fetched commit as `refs/mu/collect/<ws>/<agent>`; the commit is recorded as `agents.collected_head`, and `agent close` accepts a remote worktree at its base or that HEAD. mu prints the merge, never runs it. | "pull", "sync" (that is `mu sync`) |
| **delivery unknown** | A send/fresh/interrupt/command request was written to a live agent's control socket but no reply arrived (`AgentSendIndeterminateError`). pi may have acted; read the pane before resending. | "refused" (the request never went out) |
| **actor** | Whoever caused a state change (`ops.actor`); need not be a worker. | "caller", "author" (notes only) |
| **role** | Advisory label, `full-access` or `read-only`: recorded and shown, not enforced and not a security boundary. A read-only auditor simply gets no worktree. | "permission", "tier", "capability" |
| **persistent** | An agent that stays alive across tasks. | "long-lived" (prose only) |
| **one-shot** | An agent that exists for one task. | "ephemeral", "transient" |
| **nudge** | A pi extension continuation at settle. The keep-driving one is the **settle wait**: every completed settle of an orchestrator runs `mu wait` as its callers and continues the turn with the event, until nothing is owed or the user cancels (under a `mu human-needed` stop it still collects owned work, led by the drain rule, and starts nothing). The close nudge tells a worker once per prompt to close the task it still owns (never a delegate's own `kind=delegate` task: its call closes it). | "goal", "loop" |
| **delegate** | A scratch agent started for one check. Its call is a `delegate`-kind task; its answer is that task's report, and the settle wait shows it to pi (a wave's as one **wave summary**). The `mu_delegate` tool does it in one call; the pane stays attachable, and its pi session is kept under `<DB dir>/delegate-sessions/`, out of the caller's `/resume` list. With no multiplexer the tool runs it as a **delegate process** instead (no pane). | "subagent", "child agent" |
| **wake** | The one pi message the delegate side sends for a recorded answer when no settle wait will show it (pi idle, `MU_NUDGE=0`): a pointer per answer (`mu task notes <task> -w <ws>`), no answer text, that starts a turn whose settle shows the report. Losing or repeating one loses or repeats nothing. | "echo", "follow-up answer" |
| **wave summary** | What the settle wait shows for a delegate of a wave: it waits while siblings run (never on refused ones), then ONE entry with counts per verdict, a line per call and the answers, instead of one per call. | "wave echo" |
| **envelope** | The one JSON document a `--json` invocation prints on stdout: `{ok:true,data,next}`, or on failure `{ok:false,error,message,next,exit}` with the real non-zero exit (`src/output-contract.ts`). A failed verb that already produced evidence (a spawn whose `--send` failed, a `doctor` report) carries it in `data`. `mu log --tail` is always NDJSON (one item per line), never enveloped. | "wrapper", "result object" |
| **delegate call** | Using a delegate for one check (a refuter, judge, claim checker): one `mu_delegate` call, or `mu agent spawn -w scratch` + `send --fresh` + `wait --json` without the tool. A call is a task (see **task kind**) with a `call_id` and a state (`queued starting running recording done failed refused cancelled`, `mu delegates`). Its verdict is recorded on the task it judged, then the call's own report and close. Admission control (`MU_DELEGATE_MAX` running, a bounded queue) stays; a call past it is **refused**: durable, parked, reissuable, never dropped. `mu next --refute` makes one per closed attempt (call id `refute:<ws>:<task>:<attempt key>`, wave `refute-<ws>`), launched and recorded by the executor itself. | "subtask" |
| **delegate prune** | `mu delegates prune`: delete closed **delegate call** tasks older than an age (default 30 days) that this machine's calls made, with their notes, call rows and puts in `ops` (with sync on, only peers' puts: this machine's own rebuild its segment); the task tombstones (one `delegate.prune` op group) sync, so peers drop the rows too. Keeps a task with edges and a `refute:` check of a task with `mu task auto` enabled. The pruned calls' pi session files go too (`delegate-sessions/<ws>/<task>/`, `delegate-process/<hash>/`, and loose first-layout `delegate-sessions/<ws>/*.jsonl` past the age). Dry run by default; `--yes` backs up, VACUUMs and checks drift. `mu doctor` housekeeping reports the candidates as `delegates-old`. | "gc", "cleanup" |
| **delegate process** | The mux-free way to run a **delegate call** (`mu delegates run <call-id>`, `src/delegate-process.ts`): one child `pi --mode rpc` with its own ctl socket, log and session, no pane and no agents row (`transport: "process"`, `attach: null`). The call and its task stay the authority. Its **attempt receipt** (`<state dir>/delegate-process/<hash>/receipt.json`, machine-local) marks that a child may have run: a call with one left behind finishes `unknown`, never respawned or resent. | "headless agent", "background agent" |
| **hidden subagent** | A child agent in other tools (pi-subagents, Claude Code and Codex task tools) with no pane to attach or steer. mu's counterpart is the **delegate**. | "subagent" (alone) |
| **agent state** | `busy`, `needs_input`, `needs_permission`, or `unknown`, from the **state source**. Not persisted; see [Agent state values](#agent-state-values). | "agent status", "lifecycle state" |
| **state source** | The **control socket** for pi agents; else the mux's `paneStatus()` (herdr's classifier, or on Rex the program's OSC 7501 report) or, on tmux, murmur. A silent pi socket gives `unknown` (`ctl missing`, `ctl refused`). | "scraper" |
| **control socket** / **ctl** | The per-agent unix socket (`$MU_CTL_SOCK`) the mu pi extension serves inside pi. **ctl state** is `ok`, `missing`, or `refused`. | "rpc", "daemon" |
| **session command** | `/new`, `/reload`, or `/compact [instructions]` sent to a pi agent. mu runs it inside pi through the ctl `command` op, never as a paste. Any other slash command to a pi agent is refused. | "slash paste" |
| **outdated extension** | A running pi whose loaded extension is older than mu (exit 4 on a missing op). pi's `/reload` fixes it. | |
| **lastText** | A pi run's final assistant message, returned verbatim by `wait`. `mu_delegate` `record` finds its first `VERDICT:` line (also behind one role word such as `REFUTER:`) and writes everything from it, uncapped, as a `verdict` task note, then the call's report; mu applies no close or status change to the judged task from it. A worker's own run is different: a valid **CLOSE block** at its end closes the worker's task. | "result", "output" (alone) |
| **ghost** | An `agents` row whose `pane_id` no longer exists. Pruned by **reconcile**. | "dead agent", "stale row" |
| **reaper** | The part of reconcile that releases a ghost's tasks to `OPEN`. Makes `mu wait` exit 6 (a `dead` event). | "garbage collector", "janitor" |
| **wait event** | What ends a `mu wait`: `closed`, `settled`, `input`, `dead`, `stall`, `delegate`, `exited` (a **command gate** ended: `data.exit` is its numeric status, also the process exit), `pane-text` (a non-pi pane's tail matched `--until-text`: no run finished, never `settled`), `heartbeat` (the timeout, `ok:true`, `timedOut`), `interrupted` (SIGINT/SIGTERM). It is `data.firing` of the one JSON result, beside `exit`, `snapshots`, `cursor` and `resume`. | "wait result" (alone), "firing task" |
| **typed ref** | A `mu wait` argument that names its kind: `task:[<ws>/]<id>`, `agent:[<ws>/]<name>[@<runs>][#<incarnation>]`, `delegate:<call id>`. A bare word is refused. | "bare ref" |
| **resume** | `data.resume` of a wait result: the one-line handle `mu wait --resume <id>` that continues it. The condition set (refs with the run baseline and pi incarnation last seen, mode, filters, deadline, callers) is stored in `wait_resumes`; null (with `resumeWhy`) when nothing is left to wait for. | "retry command" |
| **needs attention** | An `IN_PROGRESS` task whose owner has been `needs_input` for `--stuck-after` seconds. Check with `mu agent read <owner>`. | "stuck", "stalled" (kept only in the `stuck` JSON field and exit-7 `STALL_DETECTED`) |
| **reconcile** | Re-derive registry rows from the mux. Runs in `mu agent list` and `mu doctor`. | "sync", "refresh" |
| **extension** | The pi extension shipped in the mu package. | "plugin" |
| **skill** | The bundled `SKILL.md` that teaches the LLM. | "system prompt" |

### Tasks

| Term | Meaning | Don't use |
| --- | --- | --- |
| **task** | A node in the DAG with mandatory `impact` and `effort_days`, a **task status**, and a **substate**. | "issue", "ticket", "item" |
| **task status** | `OPEN`, `IN_PROGRESS`, or `CLOSED`. Any `CLOSED/*` satisfies a blocker. | "state" |
| **substate** | What the status means, shown as `STATUS/substate`; never null. `OPEN`: `todo`, `parked`, `triage`. `IN_PROGRESS`: `active`. `CLOSED`: `done`, `rejected`, `wontfix`, `duplicate`, `superseded`. | "resolution", "reason" |
| **park** / **unpark** | `OPEN/todo` ↔ `OPEN/parked` (park needs `--why`). Parked tasks leave **ready**. | "defer", "snooze" |
| **triage** / **accept** | `OPEN/triage` is a proposed task, usually a review **finding**, not yet accepted as work: out of **ready**, refused by `claim`, but it blocks its dependents. `mu task add --triage` creates one; `mu task accept` moves it to `OPEN/todo`; declining is `close --as rejected` or `--as duplicate`. | "inbox", "pending" |
| **human-needed** | `mu human-needed <reason>`: the caller records a `human` task, `OPEN/parked`, whose brief is the reason; the loop's only legitimate stop. Scoped to the caller (`human_needs`, machine-local: managed `<ws>/<agent>` or `pi:<session>`, never a note author; a rebuild keeps the task but loses the caller). Same reason is a no-op; a new reason appends a brief. A `human` task resolves only by an explicit `mu task close`: unpark, claim (even `--force`) and a CLOSE block are refused. Never created on an error or a queue refusal. Under an open stop the settle still collects owned work (continues each event under the drain rule); it starts nothing. `--no-stop` records the `human` task without the `human_needs` row: a human to-do, never a stop, one task per reason. | "blocker", "escalation" |
| **finding** | One problem a reviewer, checker, or auditor reports. Recorded as an `OPEN/triage` task (the mu way), or as an answer line when a **delegate** reviews something nobody will track. | "issue", "comment" |
| **task DAG** / **graph** | The directed acyclic graph of tasks. | "task list", "tree" |
| **edge** | The single edge type: `A blocks B` means A must close before B starts. | "dependency" (prose only) |
| **subtree** / **scope** | Tasks reachable from a root via blocks-edges. | "subgraph" |
| **explicit scope** / **injected scope** | The workstream a verb acts on: `-w <name>` or a qualified `<ws>/<name>` ref (explicit), else the `MU_WORKSTREAM` spawn gives a managed worker (injected, only with `MU_MANAGED_AGENT=1`). Nothing else routes a verb: not `MU_SESSION`, not the mux session name, which only appears as a suggestion in the one-line error. | "ambient workstream", "current workstream", "default workstream" |
| **track** | An independent subtree found by parallel-track detection. | "branch", "lane" |
| **diamond merge** | Merging two tracks that share a prerequisite into one. | "join" |
| **ready** | An `OPEN/todo` task with no open blockers (the `ready` view). | "unblocked", "available" |
| **goals** | Tasks with no outgoing edges (the `goals` view). | "leaves", "targets" |
| **sort key** | `--sort` value: `roi` (impact ÷ effort), `recency`, `age`, or `id`. | "order by" |
| **note** | An append-only piece of context on a task. | "comment", "log" |
| **claim** | Set `tasks.owner` to an agent. Atomic compare-and-set. | "assign" (prose only), "lock" |
| **owner** | The worker in `tasks.owner`; NULL after an **anonymous claim**. Never syncs. | "claimer", "assignee" |
| **anonymous claim** | A `--self` claim by an actor that is not a worker. `owner` stays NULL; `ops.actor` records who. | "unowned claim" |
| **release** | Clear `tasks.owner`; an `IN_PROGRESS` task returns to `OPEN` (`--reopen` also reopens a `CLOSED` one). | "unclaim", "unassign" |

### Recipe gates

| Term | Meaning | Don't use |
| --- | --- | --- |
| **fast gate** | Repo-defined iteration checks: format, lint, unit tests and cheap integration tests. Cost is measured, not guaranteed seconds. | "gate command" (alone) |
| **slow gate** | The remaining merge checks: real-environment e2e, release and packaging checks. | "gate command" (alone) |
| **gate budget** | The slow gate's target wall time; crossing it prioritises test upkeep. | "timeout" |
| **gate record** | A note identifying commit/tree, exact command/scope, result, duration, environment/toolchain and skips; reuse needs equivalent conditions. | "tests pass" (alone) |
| **test upkeep** | Ordinary DAG work to profile, speed up, move down, merge or remove tests while preserving coverage; see [test-health](../skills/mu/recipes/test-health.md). | "coverage reduction" |
| **test level** | Unit < integration < e2e; use the lowest level that catches the guarded failure. | |
| **review gate** | A review task blocked by a work task, deciding whether its commits count. | "gate command" |
| **course check** | A periodic look at whether a long job is still on course ([course-correct](../skills/mu/recipes/course-correct.md)), recorded as a task note starting `COURSE CHECK`. `courseCheckHint` reports one due after 10 CLOSED/done work tasks, or 24 h with at least one, since the last such note; `mu state` and `mu wait` show it in `data.notices`. | "retro" |
| **merge batch** | Accepted commits combined into one pinned tree for full merge verification, without a crew-wide barrier. | "wave" (when only verification is batched) |

### Worktrees and housekeeping

| Term | Meaning | Don't use |
| --- | --- | --- |
| **worktree** | A git worktree for one agent (`spawn --worktree`): `git worktree add --detach`, the agent's cwd. Closed by the one rule: dirty refuses, merged is removed, unmerged commits keep a branch. Git only. | "workspace", "checkout" |
| **worktree registry** | The machine-local `worktrees` table (path, agent, workstream, root, base, last collected head). Outlives the agent row; only closing the worktree removes it. | "workspace row" |
| **backend** | An implementation of `MuxBackend` (tmux, herdr, rex). | "driver", "provider" |
| **missing worktree dir** | A `worktrees` row whose path is gone. Read surfaces still call it healthy. | "dangling row" |
| **dormant** | An idle workstream with no agents or worktrees: **finished** (all closed, 14 days) or **abandoned** (open tasks, 60 days). | "stale", "dead", "empty" |
| **residue** | Bytes in `<state-dir>` that no row references and no code reads. Reported by `mu doctor`, never removed. | "garbage", "junk" |
| **disk↔DB reconciliation** | `mu doctor`'s report-only `disk` section. Not **reconcile** (rows vs mux) or **drift** (log vs tables). | "gc", "cleanup" |
| **doctor** | The diagnostic command and its report. | "health check" |

### Ops log and sync

| Term | Meaning | Don't use |
| --- | --- | --- |
| **op** | One `ops` row, written by a trigger in the mutation's transaction. Holds only changed columns. | "event", "delta", "change" |
| **ops log** | The `ops` table: the append-only record that sync and history read. | "event log", "journal", "WAL" |
| **log entry** | An op as `mu log` prints it: one escaped line of at most 400 characters (or one JSON item keyed by the `ops` column names: `key`, `actor`, `entity`, `group_id`, `created_at`, `rendered`), through one formatter (`src/log-render.ts`). Parent-row touch ops are not entries. A cut says `more=N`/`cut=N` and names the read that recovers it by seq (`mu log --seq A..B`, `mu log --after S`), never by filter. An intent this build does not know prints as `op put|del <key> intent=… kind=… patch=…`, never as a verb. | "message", "event" |
| **kind** | The operator's channel tag on a log entry (`mu log --kind`), stored as the op's `entity`. `workstream`, `task`, `edge`, and `note` are reserved. | "category", "type" |
| **log ledger** | A convention: a custom `--kind` used as a watcher loop's durable dedupe record. | "state file" |
| **intent** | The semantic label on an op (`task.close`), set once per SDK function via **op context**. | "verb", "action" |
| **group** | The ops of one user action (`group_id`), read as a unit (`mu log --group`). Any unique id prefix works. | "transaction", "batch" |
| **op context** | The one-row `op_ctx` table that stamps intent, actor, and group onto ops. Written only by `src/op-context.ts`, inside a transaction the wrapper owns, so it holds defaults at rest. | "thread local" |
| **scoped views** | The `TEMP` views `ready`, `blocked`, `blockers` that `mu db query -w <ws>` (or `mu db --shell -w <ws>`) installs on its own connection, cutting the global views to one workstream (id as a literal; same columns). `main.ready` stays the global one. | "filtered view", "session views" |
| **apply** | Land one op under the **merge rules** (`src/apply.ts`). Idempotent; mints no op. | "merge", "import", "replay" |
| **merge rules** | Notes: **grow-only set**. Tasks, workstreams: **per-field LWW**. Edges: **LWW-element-set**. Unknown entities are ignored. | "conflict resolution", "CRDT" |
| **per-field LWW** | Each field keeps the value from the newest **HLC** that wrote it. | "row LWW" |
| **grow-only set** | Insert-only entities (notes, log messages). Identity is `(task, author, content, created_at)`. | "append-only log" |
| **LWW-element-set** | Add and remove both carry an HLC, so re-adding converges in any order. | "2P-set" |
| **tombstone** | An `op='del'` row. There is no tombstone table. | "deletion marker" |
| **resurrection** | A put newer than a seen tombstone, which legitimately recreates the row. | "undelete", "revive" |
| **provenance** | Which HLC last wrote a field, derived from the ops log by query, never stored. | "version vector" |
| **teardown** | Soft: kill a workstream's mux session, delete its machine-local agent rows whose pane is dead (a live pane keeps its row, orphaned; worktrees are never removed), set `torn_down_at`. Tasks, edges and notes stay, hidden. Previewed first, applied with `--if <rev>`. Panes do not come back. Reusing the name (`init --if`) hard-deletes the old rows. | "destroy", "nuke", "purge" |
| **agent incarnation** | `AgentRow.incarnation`: the registry row's AUTOINCREMENT id (`id` in `--json` agent records, with `pane_id`, `workstream_id`; the SDK keeps camelCase). Never reused, so a name deleted and created again is a new one even on the same pane id. Every destructive agent write compares it (`deleteAgent`'s `expected`, `agent close --if`); not pi's **incarnation**. | "agent id", "pane id" |
| **incarnation** | A pi process's id on its control socket: minted once per process, kept across `/new` and `/reload`, new after a restart. `send` returns it; `wait --inc` refuses another one. | "session id", "pid" |
| **msgId / receipt** | The id a ctl `send`, `--fresh` or `--interrupt` carries. `status {msgId}` (`mu agent check`) says whether the extension accepted it: the receipt for a send that got no reply. Never a reason to resend. | "ack", "nonce" |
| **drift** | The ops log and live tables disagree. Always a bug. | "inconsistency", "corruption" |
| **shallow / deep check** | Drift checks in `mu doctor` and `mu doctor --deep` (full rebuild). | "fsck" |
| **scored** | `tasks.scored`: impact/effort were given (`-i`/`-e`, `update`), not defaulted (50 / 1). `ready` order: scored by impact/effort desc, then unscored oldest first. | "prioritised" |
| **dispatch host** | `tasks.dispatch_host` (synced): the `machine_identity` allowed to dispatch the task; `mu task auto` sets it to this machine; `next` on another machine skips it. Not the remote **host profile** that `--host` selects. | "target machine" |
| **local-write guard** | A persistent trigger gated on `applying = 0`: claim CAS, close needs evidence, close to `done` refuses open blockers. Ingest and import are never blocked; `mu doctor` warns about the states they accept. | "constraint", "validation" |
| **task record** | One task as a read prints it: a single escaped TAB-separated line `task id STATE key=value … title` (<= 400 characters), or one JSON item whose keys are the SQL column names (`id` is the `local_id`; `workstream_id` and `owner_id` are the real foreign keys; `owner` appears only while IN_PROGRESS). A cut says `more=N`/`cut=N` and names one way back: the same command with `--all` (`--full` on `owned-by`), uncapped. | "row", "card", "name" (as a key) |
| **open frontier** | The part of the task DAG `task tree` and the dashboard read (`loadOpenTaskFrontier`): open tasks expand, CLOSED neighbours are leaves counted past a few, depth and node counts are capped, and every cap says how much it left out. Not `loadFullDag`, which is the whole workstream. | "subtree" |
| **note kind** | `task_notes.kind`: `brief`, `report`, `verdict`, `close` or `note`. Set once at insert; a local change raises, as does a local change to a note's author, content or created_at (append-only). | "note type", "tag" |
| **torn down** | `workstreams.torn_down_at` set (soft teardown). Views and reads skip it (exit 3 "is torn down"); ingest still sees the row. Teardowns an earlier mu wrote as deletes stay final and are listed from the ops log. | "archived", "deleted" |
| **revision** | `rev=<n>`: a number a preview prints and `--if <n>` must repeat. `workstreamRevision` hashes the newest op touching the workstream plus its agents (name, pane) and worktrees, so any change in those since the preview makes the destructive verb refuse (exit 4). `taskRevision` (src/revision.ts) is the task-side revision: a hash derived from current state (task row, owner, incoming and outgoing edges, note count; never a counter, so sync apply and cascades move it too), shown as `revision` by `task show`/`list` and as `rev` by a `task delete` preview; `--if <rev>` on `task delete`/`block`/`unblock`/`reparent`/`claim`/`release` refuses a changed task (exit 4, `expected_rev`/`current_rev` in `--json`). `agentRevision` is the agent-side one: a hash of the registry **agent incarnation**, the row's fields, every owned task (any workstream), the registered worktree rows (an orphan worktree has its own) and the name's lease **reservation** (its handle or its absence); bare `agent close` previews it and `--if <rev>` applies. Also called **preview revision**. | "etag", "version" |
| **portable** | Syncable: `workstreams`, `tasks`, `task_edges`, `task_notes`. The rest is **machine-local** (`src/db.ts`). | "shared", "global" |
| **HLC** | Hybrid logical clock `(wall_ms, counter, machine_id)`; orders every op. Never regresses. | "timestamp", "version" |
| **machine_id** | Per-state-dir uuid naming this DB's **peer** and **segment**. | "device id", "host id" |
| **segment** | `<sync-dir>/<machine_id>.jsonl`: one machine's ops, single writer. | "replica", "shard" |
| **peer** | Another machine, found by its segment; named by `machine_id` prefix. **Stale** after 24h unchanged. | "node", "remote" |
| **watermark** | `sync_peers.last_applied_seq`: how many lines of a peer's segment file have been applied. A conflict copy has its own, keyed by its file stem. | "offset", "cursor" |
| **flush** / **ingest** | Write my ops to my segment; apply peers' from their watermarks. Every invocation, when `MU_SYNC_DIR` is set. | "push", "pull" |
| **fold** | The pure function from the ops of one key (and its tombstone horizon) to that row's state (`src/fold.ts`). Live apply and rebuild both use it, so live == rebuild for any arrival order. | "merge", "reprojection", "retry queue" |
| **mixed fleet** | Machines sharing state across different OSes or filesystems. | "cluster" |

### CLI and TUI

| Term | Meaning | Don't use |
| --- | --- | --- |
| **CLI** | The `mu` binary. | "tool" |
| **operation** | A canonical mu verb, each a thin wrapper over a typed SDK function. | "command", "action" |
| **qualified ref** | `<workstream>/<name>`, used instead of `-w`. | "prefixed name" |
| **DB** / **registry** | `<state-dir>/mu.db` and its tables. | "store" |
| **TUI** | The read-only ink dashboard. The 3.x card TUI (cards, popups, toasts, tick, help overlay) is removed; bare `mu` on a TTY opens the dashboard (help when piped or `--json`). | "GUI" |

### Terms added in 4.0

Terms the [4.0 contract](reference/surface-contract.md) added.

<!-- doc-cli-drift:skip-start -->

| Term | Meaning | Don't use |
| --- | --- | --- |
| **task kind** | `tasks.kind`: `work` (default), `delegate` or `human`. Not a **note kind** (`task_notes.kind`) and not a log **kind** (`mu log --kind`): three columns, three vocabularies. | "task type" |
| **wave** | `tasks.wave`: a label grouping the delegate calls of one fan-out so one wait returns one verdict tally. Not a **merge batch**. | "batch" |
| **call_id** | The durable id of one `mu_delegate` call (`delegate_calls.call_id`), session-qualified: `pi:<session>:<n>`. A repeat of the same call is deduplicated by it; a changed request under it is refused. Not a **msgId**. | "request id" |
| **attempt key** | Names one task-bound spawn attempt with its task: `agent spawn --task <t> [--attempt <k>]` is the key `manual:<k>` (default `manual:<task id>`). The same key replays its saved outcome and never sends a second prompt; a changed request under it conflicts (exit 4); a delivered attempt whose agent is gone (closed, reaped) or whose name another agent now holds has ended, and its replay exits 4 naming the next free key (`<key>-2`, `-3`, …); reopened work takes a new key. The executor's authorised attempts are `auto:<n>` (n = the run it charged) with their latest record in `task_auto.last_spawn`: a different namespace. An `auto` attempt is reserved (and its **run** charged) before any effect, and a failed one is never reset: the next attempt is a new key. The record stores the exact prompt: a replay sends those bytes, never a rebuild. An attempt that cannot be reconciled (task closed, agent gone, UNKNOWN the one probe cannot confirm) is ended on the first `mu next` that sees it: `abandoned` (nothing sent) or `needs_human` (it may have landed: never resent, a human need is recorded); an ended attempt is never replayed or counted again. | "nonce" |
| **remote recovery** | `remote_recovery`: one machine-local row per ORIGINAL registry incarnation (`agents.id`) of a finalized remote worker (host, worktree path and base all recorded), written by an AFTER DELETE trigger on `agents` (and a BEFORE DELETE one on `workstreams` for the cascade; `src/db-recovery.ts`) in the transaction of ANY delete of its row: the reaper (`reap`, also raw SQL), workstream teardown (`teardown`) and a workstream deletion cascade (`workstream-delete`). It keeps the host, path, base, last collected HEAD, the exact aside path (`asideOf(cwd, agents.id)`, never derived from a later id), the profile's exec when it existed and the worktree's own repo (`agents.repo`), and why. No foreign key (it outlives the agent, its workstream and its host profile), no op, no sync; `mu rebuild` reports it lost. A record of a resource, not a live channel or an agent. `agent close` that itself removed the tree writes none, nor does a failed spawn whose rollback removed it (or found it gone). `agent close <name>` / `agent collect <name>` read it back when no agent holds the name (`--recovery <id>` = the original incarnation, picks one of several; the preview's `kind` is `recovery`, its `recovery_id` is never the registry `id`); a current agent, a shared path (a live agent, or a NEWER record of the same host and path), or a changed profile refuses; a `--recovery <id>` that is gone is never read as the live agent, and only that record is deleted, only once the tree is removed, kept on a branch or gone. | "tombstone", "graveyard" |
| **dead staging** | A `--worktree` spawn's placeholder row (pane `%pending-<name>`) left for over 60 s by a spawn killed before its pane (SIGKILL, crash): no pane ever ran in its worktree. `mu doctor` lists it (`ws-staging`); the next spawn in the workstream removes the row and its unused worktree. SIGINT/SIGTERM roll the spawn back and leave none. | "orphan", "zombie spawn" |
| **spawn receipt** | `spawn_receipts`: one machine-local row per (task, attempt key), written BEFORE each effect: `reserved`, `pane` (agent row + pane exist), `claimed`, `sending` (the prompt may be written: never resend), then `sent` or `unknown`; `failed` is definitely unsent and rolled back. It stores the registry id (`agents.id`) and pane with no foreign key, so a replay after the agent was deleted or the name reused never acts on the replacement (a delivered manual attempt then exits 4 naming the next key; an unknown one says `stale`); the send re-checks that id + pane and the claim just before the write. | "journal row", "ticket" |
| **delivery unknown** | A prompt that may or may not have landed (written, no reply; a pane that died after the write; a pane paste). Never resent and never reaped: the pane and the claim stay, `mu agent check` asks the extension, and an id the extension no longer remembers (it keeps the last 64) is still unknown, not unsent. | "failed send" |
| **spawn replay** | Returning the saved `{msgId, incarnation, runs}` for a repeated spawn attempt. A **receipt** (msgId) answers "did pi accept this send"; replay answers "was this attempt already made". | "retry" |
| **spawner** | `agents.spawner`: who spawned an agent: `<workstream>/<agent>` for a managed caller, else `pi:<PI_SESSION_ID>`. Never guessed from task ownership; null on older rows. | "parent" (alone), "owner" |
| **wait ref** | One thing a wait watches: a task, an agent (pinned by `@<runs>` and `#<incarnation>`) or a delegate; written as a **typed ref**. | "target" |
| **first / all** | A wait returns on the **first** event of any owned ref (default) or, with the all-of alias, once every ref has closed. | "any", "race" |
| **heartbeat** | A wait's own cap (`--timeout`, default and maximum 1500 s): it ends with `timedOut` true and `ok:true`, never an error. | "keepalive" |
| **embedded exit** | The process exit number repeated inside the JSON (`exit`), so a pipe that hides the status keeps the reason: 0 fired or timed out, 6 dead, 7 stall, 130 interrupted. | "status code" |
| **wait cursor** | `wait_cursors`: per caller and ref, the last consumed incarnation and runs, so a settled run is never reported twice and a restart is not mistaken for one. Machine-local. | "offset", **watermark** (that is sync) |
| **completion token** | What a **wait cursor** stores as `(incarnation, runs)` once an event is delivered: an agent's ctl incarnation and settled run count; `closed` + the task's newest note id (a re-close is news); `delegate` + 1. Written only for delivered refs, in the transaction that ends the wait. | "ack" |
| **owned event** | An event on a ref the callers own: their registered refs and their own delegate calls. The wait observes them while listed refs are waited on (`eventScope: owned`, the default; `refs` sees only the listed ones); another caller's are never seen. | "global event" |
| **callers** | The SET a wait owns for: the managed `<ws>/<agent>` (spawned workers, human stops) and `pi:<session>` (delegate calls), whichever are present. Owned refs, delegates, human needs and the tally are the union. An empty set owns nothing; never "every caller". | "identity", "user" |
| **unconsumed baseline** | `wait_registrations.baselines_json`: per registered agent ref, the ctl run count, pi incarnation and registry id a producer saw BEFORE its effect, so a run that settles before any wait starts is still delivered. Not a **cursor** (which is what a wait already delivered). | "watermark" |
| **stall** | A wait event: an IN_PROGRESS work task whose owner sat needs_input (or behind a dead control socket) for `stuckAfterMs`. The engine detects and reports it (exit 7); `handleStall` owns any action. | "stuck" (the legacy warning) |
| **action receipt** | `wait_action_receipts`: one machine-local row per stall action a wait took, keyed by task id, owner registry id, pi's ctl incarnation and action (never the wait generation, the revision or the caller). It is the exclusion that keeps a nudge or a release to ONE per incarnation across callers, restarts and generations, and the record of a release. A steer row walks `reserved -> sending -> sent | unknown | refused`: `sending` means the write MAY have happened (a crash there is `unknown`, only ever probed, never resent); a guard that vetoes before the write leaves the row `reserved`, so the budget is not spent. No foreign key: it outlives the task and the agent. A rebuild cannot restore it (reported as lost). Not a **spawn receipt**. | "nudge log" |
| **guarded release** | The `release` stall action (`releaseStall`): free a stalled task in ONE immediate transaction that rechecks everything: the caller is in the callers set, the task is an IN_PROGRESS `work` task at the revision the stall saw, owned by the agent row the stall named (same id and pane) that a caller spawned, `autoPermitted`, and no caller has an open human stop. A failed guard writes nothing and returns the stall. Never reopens, closes, spawns or kills. | "auto-release", "unstick" |
| **urgent event** | In an **all** wait: a dead owned ref, an agent blocked on a dialog or an owned delegate report. Ends the wait early and does not count toward "all". | "interrupt" |
| **arming** | A producer (`spawn --send`, `send`, `send --interrupt`, `claim --for`) registering what it started in `wait_registrations` for the caller (managed `<ws>/<agent>`, else `pi:<session>`), from real state, never from a shell line. Only an effect that landed arms: not `claim --self`, a slash command, an undelivered or UNKNOWN send. A refused registration is a `warnings` entry; the effect stands. Direct SDK calls do not arm. An agent ref is **owed** only while the agent owns open work or a run it was sent is unsettled; when every task it owned is CLOSED and its last run settled it is **done**, so a lost pane is not a `dead` event. The engine decides this in one place (`src/wait-owed.ts`) and keeps a DONE mark per owned agent. A deliberate `agent close` is never a `dead` event: it drops the agent's ref from every registration (work it left unfinished stays owed through its task refs). | "subscribe", "auto-wait" |
| **wait registration** | `wait_registrations`: per caller, the typed refs it still owes a wait (each existed when registered), and whether a wait is `armed` (work owed) or `waiting` (inside one). `generation` counts the waits begun: a wait that ends compares it, so an older one cannot overwrite a newer. Ending a wait removes only the refs it delivered. A ref into a torn-down or deleted workstream is owed nothing: the next wait drops it (one stderr line), never fails on it. An optimisation and observable state, never the only test: the engine also reads the tasks and delegates directly. Not a **cursor**. | "subscription" |
| **agent observation** | `AgentObservation`: one typed read or watch result of an agent. `agentId` is the registry row id (a number); `incarnation` is pi's process string; `settled` is true only for a ctl wait that ended `settled`. | "snapshot" (that is the legacy `AgentStatusSnapshot`) |
| **close notice** | The one fixed note a wait sends a worker whose **CLOSE block** was refused as malformed: the parser's reason, the exact shape, and "no `CLOSE:` while not done" (`src/close-notice.ts`). Sent by a `mu wait` / `mu task wait` of a caller that spawned the worker (not gated on `mu task auto`; `mu agent wait` applies no CLOSE and never sends it), also when another caller's wait saw the run first, only while the worker's row owns its IN_PROGRESS `work` task; at most once per (worker row, pi incarnation, run) and 3 per incarnation, marked per incarnation before the write (never resent); a run that got one is not also steered. Other refusals stay with the orchestrator. Not a **steer**. | "nag", "retry" |
| **settle replay** | `agent_settles`: one row per (agent, incarnation, runs) already applied, so a **CLOSE block** takes effect once per settled run. | "dedupe table" |
| **CLOSE block** | The last lines of a pi worker's final message: `CLOSE: task=<id> evidence="..."` then `REPORT:` and the text. Counts only at the very end and for the worker's own task; applied as report plus close in one op group. `mu task close` stays. | "completion marker" |
| **report / verdict** | The pinned notes of a task: **report** is the closer's account (`kind=report`), **verdict** a judge's (`kind=verdict`). With the **brief** they are never truncated in the default notes view. | "result" |
| **pinned content** | A task's brief, last report and verdicts: exempt from output caps. Ordinary notes are capped and cut with `more=N`. | "important notes" |
| **more / cut** | `more=N`: N notes left out of a view; `cut=N`: N records dropped by a size cap. Each names the exact command that fetches the rest. | "truncated" |
| **message table** | `MESSAGES` in `src/messages.ts`: every error and hint line an agent reads, keyed `<domain>.<what>` and parameterised by facts; `say(key, ...facts)` renders one. The wording is free to change; the key, class, exit code and fields are not. | "error strings", "hint text" |
| **NextPlan** | The `next` of a result: `{mode, commands}`. `seq` is an ordered sequence, never truncated; `alt` is alternatives. Only state-advancing, executable-as-printed moves, computed only by `computeNext` and finalised once per result by `handle()` (JSON `next`, human `next:` line). Replaces the deleted `nextSteps` / `Next:` block. | "suggestion", "nextSteps", "hint block" |
| **notice** | A one-line fact beside `next`, not a command: `data.notices` on `mu state` and `mu wait`, from `computeNotices` (today only a due **course check**). Absent when there is none. | "warning" (that is `data.warning`), "hint" |
| **outcome** (of a verb, for `next`) | What the verb that just ran did that a follow-up depends on (`NextOutcome`: sent, send-unknown, gate-spawned, claimed-for, preview, waited). Declared by the verb with `setNextOutcome`; the only per-verb input to `next`. | "hint" |
| **auto** | A task authorised for the executor (`mu task auto`: a machine-local `task_auto` row, `enabled`, with a cli key, worktree mode and run ceiling, plus this machine as `dispatch_host`; `--off` keeps the counters). Ready is not permission: `next` acts only on `auto` tasks. | "autopilot" |
| **run** | One charge against a task's `task_auto.max_runs`: taken once, when the executor reserves a new `auto:<n>` attempt, in the same transaction as the reservation. A replay, a receipt check or an UNKNOWN probe never charges; a definitely-unsent failure keeps its charge. (pi's own `runs` in a receipt or wait is a different counter: settled runs of one pi.) | "try", "retry count" |
| **recovery (executor)** | What `mu next` does for a worker it dispatched. DEAD (pane gone) is reaped through `deleteAgent`'s id + pane compare-and-swap and its task retried as a new attempt; LIVE with an API-errored last run is retried IN PLACE (same pane, reserved msgId, `expectIncarnation`), never reaped or duplicated; a remote worker's tree is never touched; what cannot be acted on is an `attention` move. Each retry is one **run**. | "restart", "respawn" |
| **worker ceiling** | `mu next --workers <n>`: the most workers WORKING at once (an IN_PROGRESS task or an attempt in flight). An idle worker whose task is CLOSED does not count. Enforced where a pane would be created; never holds back an in-place retry. | "concurrency", "slots" |
| **hold** | `mu next --hold`: after dispatching, wait (bounded) for the first event on the in-flight workers; a death or an API error is recovered and the wait goes on, anything else is returned. | "watch mode", "daemon" |
| **brief (executor)** | `buildTaskBrief`: the prompt of one attempt, built from the task's first `brief` note and latest `report` note, stored verbatim with the reservation (a replay never rebuilds it). Never re-quotes a previous prompt. | "template" |
| **host profile** | A machine-local template (`shell`, `exec`, `fetch`, a `transport` and for a lease a `stop`, `max_sessions`, an optional default `repo`) for one remote machine (never one per repo: `max_sessions` caps the machine), filled by mu and run with `/bin/sh -c` (`exec`, `fetch`) or as the local pane's command (`shell`); mu never connects. Selected by `spawn --host`. Not the **dispatch host**. | "remote config" |
| **host template** | One field of a host profile (`shell`, `exec`, `fetch`, `stop`) with `{env}`, `{cmd}`, `{local_sock}`, `{remote_sock}`, `{cwd}` placeholders, plus `{job_file}` (a lease `shell`, exactly once) and `{job}` (`stop`, required; `{cwd}` is `.` there, `{env}` is refused). The lexer enforces which field takes which placeholder. Each value is one shell-quoted word (inside your own quotes, the value itself); `{name:sh}` adds a second-shell quoting layer; `{env}` is whitelisted `KEY='value'` words; `{{` `}}` are literal braces; anything else in braces is refused. | "ssh command" |
| **host channel** | One connection slot of a host (an attached pane of a `session` profile, `--exec`, `--fetch`, `--stop` or `host check`) counted against `max_sessions`: one `host_channels` row per holder. A `lease` pane holds none. **Transient** rides one invocation (reclaimed only when its pid is proven gone and its template's process group, recorded before it runs, is gone too); **attached** rides an agent's registry id and pane: `spawn --host` reserves it before the pane opens, binds it to the new id + pane, and `deleteAgent` frees it (reclaimed otherwise only when the pane is proven dead). A refusal is one line naming the holder. | "session" |
| **transport** | A host profile's `session` (default) or `lease`. `session`: the pane runs the `shell` template, a held ssh, and holds a host channel. `lease`: the pane runs a **carrier**, holds no channel, and a **stop template** ends the job. `--transport`, validated in `addHost` (`host_profiles.transport`, no CHECK). | "mode", "kind" |
| **carrier** | The local process a lease profile's `shell` template runs in the pane (e.g. `mule run --lease`): it owns one detached remote job, polls on its own connection, binds the control-socket forward, writes the **job handle** to `{job_file}` before dispatch, and stops the job when the pane dies (best effort). mu knows no carrier by name. | "wrapper", "agent process" |
| **job handle** | The opaque line (`^[A-Za-z0-9._:@-]{1,128}$`) a carrier writes to its job file (`<state dir>/carrier/<ws>/<agent>.job`, hashed past the ctl-socket bound). mu fills it into the name's **reservation** with the `stop` template that was admitted (`host_stop`). Only an ENOENT means "no file": an unreadable, empty or invalid file keeps the reservation and the tree. | "pid", "job id" |
| **reservation** | A lease spawn's `carrier_jobs` row for (workstream, name), inserted under the agent lock before any carrier effect (every spawn holds that lock from its name check to its finalized row, so none lands in between). It holds an immutable spawn token that guards every update, a `launch` state that turns `intent` just before the mux is asked for the pane, the pane and incarnation once known, and the handle after the **job handshake**. It is name-exclusive. While it exists, every spawn of the name refuses (exit 4), lease or not, and the agent-row insert checks it in the same statement. Agent deletion never touches it. It is released only after a successful stop plus cleanup of the job file and tree, by its own rollback when `launch` is still `none` and any remote worktree it prepared is removed (a failed removal keeps it, with the reason and WHICH tree: `tree_path` + `tree_agent_id`, released only by a close of that tree), or by `agent close --abandon-job`. `agent close` stops it after the agent's liveness proof and before the tree (the name's close does the same when its agent row is gone). An empty reservation with `launch` `intent` is never released otherwise: nothing mu can observe proves such a carrier will not publish. Machine-local, no FK, trigger, op or sync; `mu rebuild` reports it lost. | "lock", "claim", "job row" |
| **abandon a job** | `agent close <name> --abandon-job`: drop a lease **reservation** WITHOUT running its **stop template**, for a host that is gone for good. Logs an event naming the host and the handle (or the empty reservation's facts) and leaves the job file in place. The only deletion of a reservation that launched a carrier without a successful stop; `--force` never stands in for it. | "force close", "drop job" |
| **job handshake** | A lease spawn's wait for the **job handle** (`MU_SPAWN_JOB_MS`, default 30 s, never 0), before and independent of the ctl handshake (`--no-ctl` does not skip it). The handle is filled into the spawn's **reservation**, guarded by its token. If the pane dies first, the error is `AgentDiedOnSpawnError`. If no handle arrives by the deadline, the spawn is rolled back. Neither case releases the reservation. | "job wait", "lease probe" |
| **stop template** | A lease profile's `stop` (`--stop`): the template that ends a job given its handle (`{job}`), idempotent (exit 0 for a job already ended). `runStop` runs it under a transient channel only if it is byte-identical to the `host_stop` recorded with the handle; exit 3 is a **hand-back**; any other failure keeps the handle. | "kill template", "cleanup" |
| **hand-back** | A host `exec`, `fetch` or `stop` template that exits 3: a human must act (a hardware key, a prompt). `runLeasedTemplate` throws `HostHandbackError` (label `human needed`, exit 5, `data.handback`), never retried and never recorded as `human-needed` by mu: that record is always a decision. `host check` reports it as a `handback` step and skips the rest; a rollback or gate cleanup swallows it. | "auth failure", "ssh failed" |
| **collected HEAD** | A remote worktree's HEAD as last fetched home (`agents.collected_head`, set by `mu agent collect`; a **remote recovery** record keeps it after the row is gone). A remote close needs HEAD equal to the base or this. | "synced commit" |
| **identity trio** | Three unrelated identities: **spawner** (who started an agent), **dispatch host** (which machine may dispatch a task), **revision** (`workstreamRevision`, a preview fence). Do not substitute one for another. | |
| **dashboard** | The human-only read-only five-tab view (Overview, the default card grid; Tasks, DAG, Agents, Log) over a workstream row and the attention strip (on every view, drills included), opened by bare `mu` on a TTY. Enter drills a row (task notes, agent scrollback, one log op); `y` copies the row's command; only `a` on an agent runs anything (the mux's attach steps, falling back to the scrollback with the reason; from a client of another tmux server it nests on purpose and says `C-b C-b d`). Replaces the removed card TUI. Every view is live on one poll loop (3.x's tiers: a fast SQL tick, 1 s by default and set with `+`/`-`, and a 10 s slow tick for agent state and scrollback); `r` only nudges. Colours are the 3.x palette as named theme roles (`src/cli/tui/theme.ts`; NO_COLOR turns them off). Its read model is `loadDashboard` (`src/dashboard-data.ts`); its Next line is `computeNext` for the caller, "unavailable" (never "no authorised work") only when that fails. | "home screen" |
| **attention item** | One line of the dashboard's trouble list (`DashboardSnapshot.attention`), read over every live workstream regardless of filters: `dead` (pane gone), `permission`, `stalled` (one tool over 15 min), `input` (the agent's `idle` flag), `delegate` (a refused **delegate call**, which stays until reissued or cancelled; any caller; a failed call is final and is not one), `human` (an OPEN `kind=human` task, any recorder), `next_unavailable`, `sync` (the latest ingest's warnings, until an ingest comes back clean). An item a `mu` command clears carries that command as its `fix`, which `y` copies (dead: `agent close`, refused delegate: `delegates finish --outcome cancelled --no-record`, human: `task close`). Not **needs attention**, which is the `task wait` stall. | "alert", "notification" |

<!-- doc-cli-drift:skip-end -->

## Topology

```
  workstream  (one DB partition)
  ┌──────────────────────────────────────────────────────────────┐
  │  mux session: mu-auth-refactor  (tmux/Rex session, herdr ws) │
  │  ┌──────────────────────────┐  ┌──────────────────────────┐  │
  │  │ window: Backend          │  │ window: Review           │  │
  │  │ ┌──────────┐ ┌─────────┐ │  │ ┌──────────────────────┐ │  │
  │  │ │ worker-1 │ │worker-2 │ │  │ │ reviewer-1 (read-only)│ │  │
  │  │ │ pane     │ │ pane    │ │  │ │ pane                 │ │  │
  │  │ └──────────┘ └─────────┘ │  │ └──────────────────────┘ │  │
  │  └──────────────────────────┘  └──────────────────────────┘  │
  └──────────────────────────────────────────────────────────────┘
```

Actor resolution reads `$MU_AGENT_NAME` only; pane titles are not identity.

## Agent state values

Glyphs live in `src/glyphs.ts` (`AGENT_STATE_GLYPH`).

| Value | Glyph | Meaning |
| --- | --- | --- |
| `busy` | nf-fa-play | Working |
| `needs_input` | nf-fa-moon_o | Waiting for input |
| `needs_permission` | nf-fa-lock | Waiting for a human answer |
| `unknown` | nf-fa-question_circle | No usable reading; a reason accompanies it |

The extension's `busy` maps to `busy` and `idle` to `needs_input`; an
open extension dialog (its `blocked` field) maps to `needs_permission`. Murmur
`working` maps to `busy`, `blocked` to `needs_permission`, and `idle`,
`done`, and `crashed` to `needs_input`. The `agents.status` column is
deprecated, always `spawning`, and never read.

## Reserved and avoided terms

| Avoid | Why | Use instead |
| --- | --- | --- |
| "subagent" | Means a hidden child in other tools; mu has none | "agent", "delegate", or "hidden subagent" when contrasting |
| "session" | pi, tmux, herdr and Rex each have one | "workstream", "mux session", "tmux session" |
| "workspace" | The retired word for a worktree; in the mux sense only "herdr workspace", about herdr's CLI | "worktree"; "mux session" |
| "project" | Means a `.pi/` project root | "workstream" |
| "context" | LLM, project, and fork context | Be specific: "task context" |
| "tab" | tmux and Rex have windows (herdr calls them tabs; Rex.app shows windows as tabs) | "window"; `--tab` is the flag |
| "thread" | OS, chat, and git threads | Be specific |
| "message" | LLM message, log message, pane input | "log entry", "send" |
| "config" | mu has no config file | "settings", "options" |
| "manager", "instance" | Vague | The specific noun |
| "service", "broker" | Imply a daemon or middleware; mu has none | Be specific |
| "plugin" | pi has extensions | "extension" |
| "checkpoint", "snapshot" | mu has no savepoints | "group" (op group), "backup" (whole-DB copy) |
| "agent type", "agent definition", "agent template" | mu has no class system or templates; spawn flags and the prompt define an agent | "agent role", or describe the spawn |
| "worker" (general) | Names the task-claimer role | "agent" |
| "claimer" | | "owner" |

## Senses of "session"

| mu term | What it is |
| --- | --- |
| **workstream** | mu's unit of organization |
| **mux session** | The backend-agnostic container for a workstream's panes |
| **tmux session** | The tmux session `mu-<workstream>` |
| **herdr session** | herdr's server-level unit (one socket). Not a mux session: a workstream maps to a herdr workspace, one level down |
| **Rex session** | Rex's session, labelled `mu-<workstream>`: the Rex flavour of a **mux session** |
| **pi session** | pi's conversation |
| **agent session** | Colloquial for an agent's lifetime; avoid in code |

Columns and variables say `workstream_id`, never `session_id`.
