@mu-crew/mu 3.2.0 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +203 -599
- package/README.md +76 -327
- package/dist/cli.js.map +1 -1
- package/dist/extension/mu-pi.js +178 -45
- package/dist/extension/mu-pi.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/docs/ARCHITECTURE.md +175 -1072
- package/docs/ROADMAP.md +88 -169
- package/docs/VISION.md +205 -415
- package/docs/VOCABULARY.md +202 -466
- package/docs/architecture/control-socket.md +119 -0
- package/docs/architecture/dag.md +113 -0
- package/docs/architecture/mux.md +163 -0
- package/docs/architecture/ops-log.md +140 -0
- package/docs/architecture/sdk.md +185 -0
- package/docs/architecture/sync.md +129 -0
- package/docs/architecture/tui.md +146 -0
- package/docs/guide/README.md +25 -0
- package/docs/guide/backends.md +61 -0
- package/docs/guide/cleanup.md +74 -0
- package/docs/guide/delegate.md +87 -0
- package/docs/guide/dispatch.md +137 -0
- package/docs/guide/getting-started.md +177 -0
- package/docs/guide/recovery.md +133 -0
- package/docs/guide/remote.md +74 -0
- package/docs/guide/sql.md +76 -0
- package/docs/guide/stop-a-worker.md +64 -0
- package/docs/guide/sync.md +78 -0
- package/docs/guide/tui.md +98 -0
- package/docs/guide/upgrade.md +76 -0
- package/docs/history/CHANGELOG-pre-3.md +4866 -0
- package/docs/reference/env.md +87 -0
- package/docs/reference/naming.md +82 -0
- package/docs/specs/2026-09-28-agent-state-from-murmur.md +1 -1
- package/docs/specs/2026-09-29-task-substates.md +1 -1
- package/package.json +1 -1
- package/skills/mu/REMOTE_WORKERS.md +179 -504
- package/skills/mu/SKILL.md +116 -185
- package/docs/HANDOVER.md +0 -486
- package/docs/USAGE_GUIDE.md +0 -2738
package/docs/VOCABULARY.md
CHANGED
|
@@ -1,485 +1,221 @@
|
|
|
1
1
|
# Vocabulary
|
|
2
2
|
|
|
3
|
-
Canonical terms for mu.
|
|
4
|
-
messages, and the
|
|
5
|
-
|
|
6
|
-
defined here, fix the doc. If you need a new term, add it here first.
|
|
3
|
+
Canonical terms for mu. Use these exact words in code, docs, error
|
|
4
|
+
messages, and the skill. If a doc uses a term not defined here, fix the
|
|
5
|
+
doc. Add a new term here before the code lands.
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
Verbs and flags live in `mu --help` and `mu <verb> --help`. Env vars are
|
|
8
|
+
in [reference/env.md](reference/env.md). Naming rules for flags, ids,
|
|
9
|
+
agent names, and file paths are in [reference/naming.md](reference/naming.md).
|
|
9
10
|
|
|
10
|
-
##
|
|
11
|
+
## Terms
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
| --------------------- | ------------------------------------------------------------------------ | -------------------------------------------------- |
|
|
14
|
-
| **workstream** | The unit of organization. One workstream = one **mux session** = one DB partition | "project", "session" (ambiguous), "context" |
|
|
15
|
-
| **multiplexer** / **mux** | The terminal substrate that owns panes. Two supported: **tmux** and **herdr**. "mux" is the short form used in code (`src/mux/`, `MU_MUX`); "multiplexer" is the prose form. Exactly one is active per mu invocation, chosen by **mux detection** — mu never drives two at once. | "terminal", "emulator", "backend" (alone — say **mux backend**) |
|
|
16
|
-
| **mux backend** | An implementation of the `MuxBackend` interface (`src/mux/tmux.ts`, `src/mux/herdr.ts`). Owns everything backend-specific: session/window/pane topology, the send protocol, scrollback capture, **pane id** validation, and **actor** identity fallback. Sibling of **VCS backend**; same `src/vcs/` shape. | "driver", "provider", "adapter" |
|
|
17
|
-
| **mux session** | The mux-level container holding one workstream's panes. On tmux it is a **tmux session** named `mu-<workstream>`; on herdr it is a herdr *workspace* labelled `mu-<workstream>`. Use the generic term in cross-backend prose and the specific term when the backend matters. | "mux workspace" (collides with mu's VCS **workspace**), "session" alone |
|
|
18
|
-
| **mux detection** | The ladder that picks the active **mux backend**: `MU_MUX` override → `HERDR_ENV=1` → `$TMUX` → whichever binary is on `PATH` (tmux wins a tie as the incumbent) → `NoMultiplexerError`. `HERDR_ENV` outranks `$TMUX` because it is the narrower signal: only a herdr-managed pane sets it, and herdr may itself be running inside tmux. | "auto-detect" (vague), "probe" |
|
|
19
|
-
| **scratch workstream**| The reserved workstream named `scratch` for off-the-cuff agent use — spin up a helper you'll keep talking to without crew/DAG ceremony. Explicit `-w scratch` (no implicit fallback); auto-created on first spawn; `mu workstream init scratch` is rejected (loud). Agents in it are still **agents**; a **delegate** is a scratch agent started for one task. Task-less is fine; the DAG stays opt-in. | "throwaway ws", "temp workstream", "subagent" |
|
|
20
|
-
| **tmux session** | The literal tmux session a workstream lives in — the tmux flavour of a **mux session** | "session" alone (ambiguous) |
|
|
21
|
-
| **window** | The mid-level grouping inside a **mux session** (tmux window / herdr tab); identified by `window_name` | "tab" (except as the frontmatter field name) |
|
|
22
|
-
| **pane** | One shell view inside a **window**; identified by **pane id** | "terminal", "shell" |
|
|
23
|
-
| **pane id** | The mux's stable handle for a **pane**. Shape is backend-specific — tmux `%15`, herdr `w1:p1` — so validation lives on the **mux backend**, never as a global regex. Stable for the pane's lifetime; never reused after close. Distinct from a tmux pane *index* (`0`, `1`, …), which is volatile and must never be stored. | "pane number", "pane index" |
|
|
24
|
-
| **pane title** | The string identifying which **agent** occupies a pane. **Equals the agent's name.** Set via `select-pane -T` on tmux, and via `herdr pane rename` (the pane *label*) on herdr. A fallback for **actor** identity — `$MU_AGENT_NAME` is consulted first. | "pane name" |
|
|
25
|
-
| **window name** | The **window**'s name. **Equals the agent's `tab:` value** (groups one or more agents). | "tab name" (in code; `tab:` only in frontmatter) |
|
|
26
|
-
| **agent** | A named worker running in a pane; identity = `$MU_AGENT_NAME`, falling back to **pane title**; row in `agents` table | "subagent" (means a hidden child in other tools), "worker" (only the specific role) |
|
|
27
|
-
| **worker** | An **agent** in its role-as-task-claimer. Synonym for the registered side of identity — a row in `agents`, owns tasks via the FK. | (when ambiguous, prefer **agent**) |
|
|
28
|
-
| **remote worker** | A worker whose process and workspace are on another machine while its pane and orchestrator DB remain local. The durable inventory record is a task-note line in the exact `REMOTE: <host>:<path>` shape; pair it with `REMOTE_BASE: <agent>:<sha>` so `mu task claim --for <agent>` can print the one-shot poll-and-close step. `mu state` lists `REMOTE:` lines and `mu state --json` exposes them as `remoteWorkers`. | "remote agent record", "host registry" |
|
|
29
|
-
| **actor** | The party that *caused* a state change. May or may not be a registered worker. Recorded in `ops.actor` for every op. The orchestrator running mu from a top-level shell is an actor but not a worker. | "caller", "author" (only on notes) |
|
|
30
|
-
| **crew** | *Informal* collective noun for the agents in a workstream | (no API surface; prose only) |
|
|
31
|
-
| **task** | A node in the DAG. Has mandatory `impact` and `effort_days`. Has a **task status** and a **substate**, shown as a pair (`OPEN/parked`, `CLOSED/wontfix`). | "issue", "ticket", "item" |
|
|
32
|
-
| **task status** | The lifecycle phase, one of 3: **OPEN** = not started; **IN_PROGRESS** = claimed and active; **CLOSED** = ended. Status alone decides edge satisfaction: any `CLOSED/*` satisfies a `--blocked-by` edge. | "state" |
|
|
33
|
-
| **substate** | What the **task status** means for this task. Never null; each status has a default. `OPEN`: `todo` (default), `parked`. `IN_PROGRESS`: `active`. `CLOSED`: `done` (default), `rejected` (the proposal was declined), `wontfix` (valid, not worth doing), `duplicate`, `superseded`. Rendered as a pair `STATUS/substate`; a default substate renders as the bare status. Valid pairs live in the `task_substates` table. Never affects edges. Refer to a non-`done` close as a *closed substate*. | "resolution", "reason", "sub-status" |
|
|
34
|
-
| **park** | Verb: set an `OPEN` task to `OPEN/parked` with a required `--why` note. A parked task leaves **ready** and `next`, `claim` refuses it without `--force`, and it stays in **goals**. Its dependents stay blocked. Refuses `IN_PROGRESS` (release first) and `CLOSED` (open first). | "defer", "snooze", "shelve" |
|
|
35
|
-
| **unpark** | Verb: return an `OPEN/parked` task to `OPEN/todo`. | "resume", "reactivate" |
|
|
36
|
-
| **task DAG** / **graph** | The directed acyclic graph of tasks. | "task list", "todo", "tree" (it's a DAG, not a tree) |
|
|
37
|
-
| **edge** | A `blocks` relationship between two tasks. The single edge type. `A blocks B` = A must close before B can start. | "dependency" (use only in prose) |
|
|
38
|
-
| **track** | An independent subtree of the DAG identified by parallel-track detection | "branch", "lane" |
|
|
39
|
-
| **diamond merge** | When two tracks share a prerequisite, parallel-track detection collapses them into one track to prevent two agents from colliding on the shared dependency. | "join", "converge" |
|
|
40
|
-
| **ready** | An `OPEN/todo` task with no unresolved blockers (parked tasks are excluded). Exposed as the `ready` SQL view. | "unblocked", "available" |
|
|
41
|
-
| **goals** | Tasks with no outgoing blocks-edges (graph endpoints). Exposed as the `goals` SQL view. | "leaves", "targets" |
|
|
42
|
-
| **sort key** | Argument to `mu task list / next / ready --sort <key>`. One of `roi` (impact / effort_days, default for `next` / `ready`), `recency` (`updated_at` DESC — "what did I touch most recently"), `age` (`created_at` ASC — "what's gone stale"), `id` (`local_id` ASC, default for `task list`). The two time-based keys also render an `updated`/`created` relative-time column in the table view. | "order by", "sort by" |
|
|
43
|
-
| **subtree** / **scope** | The set of tasks reachable from a root via blocks-edges | "subgraph" (only for technical descriptions) |
|
|
44
|
-
| **note** | An append-only piece of context attached to a task | "comment" (reserved for VCS), "log" (reserved for the **ops log**) |
|
|
45
|
-
| **log entry** | A rendered **op**, as shown by `mu log`. Not a distinct table — the ops log is the only log. Prose comes from the op's **intent** via ONE formatter (`src/log-render.ts`), shared by `mu log`, `mu state`, and the TUI. Rows with no intent (operator writes / a **log ledger**) are shown verbatim. One hidden exception: a parent-row *touch* (payload is only `updated_at`) is a real op but not a log line. | "message" (overloaded), "event" (overloaded) |
|
|
46
|
-
| **kind** | The operator-chosen channel tag on a **log entry** — `mu log --kind <tag>` sets it on write and filters to it on read. Stored as the op's `entity`. A different axis from **intent**: **kind** is what the *operator* labels a hand-written line, **intent** is what *mu* records about a state change. Captured ops get their entity from the table they mutate (`task`, `note`, `edge`, `workstream`), and machine-local ops use `agent` / `workspace`; `message` is the default for a bare `mu log "text"`. | "category", "type" (overloaded) |
|
|
47
|
-
| **log ledger** | A doc-only *convention* (not a feature): use a custom `--kind` tag as a durable, append-only dedupe/memory ledger for a watcher loop. Each tick records last-seen external state (`mu log --kind pr-state 'pr=N sha=.. ci=..'`); the next tick reconstructs it with `mu log --kind pr-state -n 1 --json`. State lives in SQLite, so it survives `/loop` or `/watch` death and context compaction — no in-session bookkeeping. | "state file", "dedupe cache" |
|
|
48
|
-
| **claim** | Verb: set `tasks.owner` to an agent. Atomic CAS. | "assign" (use only in prose), "lock" |
|
|
49
|
-
| **owner** | The **worker** name in `tasks.owner`. Set by claim. NULL when the task is unowned OR was claimed via `--self` (anonymous, attributed via `ops.actor` instead). Owners are **machine-local** — they reference `agents`, so ownership never syncs. | "claimer", "assignee" |
|
|
50
|
-
| **anonymous claim** | A claim made via `--self` where the **actor** isn't a registered **worker**. `tasks.owner` stays NULL; the actor is recorded in `ops.actor` for the auto-emitted `task.claim` op. The orchestrator-doing-direct-work pattern. | "self-claim" (in code; "anonymous claim" in prose), "unowned claim" |
|
|
51
|
-
| **release** | Verb: clear `tasks.owner` | "unclaim", "unassign" |
|
|
52
|
-
| **agent state** | Runtime state reported by the **state source**: `busy`, `needs_input`, `needs_permission`, or `unknown`. It is not persisted in mu. | "agent status", "lifecycle state" |
|
|
53
|
-
| **role** | `full-access` or `read-only` capability flag | "permission" (avoid), "tier" |
|
|
54
|
-
| **persistent** | Agent that stays alive across tasks | "long-lived" (only in prose) |
|
|
55
|
-
| **one-shot** | Agent that exists for a single task and then terminates | "ephemeral", "transient" |
|
|
56
|
-
| **workspace** | A VCS-isolated checkout (jj workspace / sl worktree / git worktree / cp). **In mu, "workspace" is always the VCS sense** — never the mux sense. herdr calls its session-level container a "workspace" too; in mu docs and code that is a **mux session**. | "branch" (it has one but isn't one), "checkout" (only for `none` backend), the herdr sense |
|
|
57
|
-
| **ghost** | An `agents` row whose `pane_id` no longer exists in the **mux**. Pruned by **reconcile**, which runs in `mu agent list` and `mu doctor`. | "dead agent" (a dead agent may still have a pane), "stale row" |
|
|
58
|
-
| **reaper** | The part of reconcile that releases a **ghost**'s tasks: `IN_PROGRESS → OPEN`, owner cleared. Makes `mu task wait` exit 6. | "garbage collector", "janitor" |
|
|
59
|
-
| **needs attention** | The condition `mu task wait --stuck-after` detects: an IN_PROGRESS task whose **owner** has been in `needs_input` for at least N seconds according to murmur's `since`. `unknown` never counts. The predicate cannot distinguish a worker that finished without closing, one waiting on an answer, or one at an approval prompt, so every surface points at `mu agent read <owner>`. | "stuck" / "stalled" (the `stuck` JSON field and exit-7 `STALL_DETECTED` keep their names for compatibility), "close discipline", "negligent worker" |
|
|
60
|
-
| **workspace orphan** | A directory under `<state-dir>/workspaces/<workstream>/` with no row in `vcs_workspaces`. Blocks subsequent `--workspace` spawns. Surfaced by `mu workspace orphans -w X`, `mu state -w X`, and `mu doctor`'s `ws-dirs` row. | "stray dir", "leftover workspace" |
|
|
61
|
-
| **stranded** | A **workspace orphan** whose parent *workstream* row is also gone. Reported separately because no workstream-scoped verb can reach it — `mu workspace orphans --all` and `mu doctor` are the only surfaces. | "double orphan", "abandoned" |
|
|
62
|
-
| **missing workspace dir** | The INVERSE of a **workspace orphan**: a `vcs_workspaces` row whose `path` is not on disk (usually a hand-run `rm -rf` where `mu workspace free` was wanted). More dangerous than an orphan, because every read surface reports the row as a healthy workspace and the next send fails inside the VCS backend rather than at the row that lied. `mu doctor`'s `ws-rows` row. | "broken workspace", "dangling row" |
|
|
63
|
-
| **dormant** | Of a workstream: nothing running (no agents, no registered workspaces) and no task touched in a while, so it is a **teardown** candidate. Split into two buckets with DIFFERENT advice, because one list would invite sweeping away the record of outstanding work: **finished** = every task `CLOSED` and idle ≥ 14 days (safe to tear down), **abandoned** = idle ≥ 60 days but still holding unclosed tasks (look first — teardown removes the PLAN, not the code, and is undoable, so the cost is losing sight of what was left to do). Reported by `mu doctor`'s `housekeeping` section at `ok` severity — a tidy-up opportunity, not a fault — and never acted on. Excludes the current workstream, the **scratch workstream**, and task-less workstreams (those are `mu workstream teardown --empty`'s job). Thresholds are display cutoffs in `src/dormant.ts`, deliberately not env vars: nothing downstream reads them. | "stale" (reserved for **stale workspace**), "dead", "empty" (`--empty` means zero rows) |
|
|
64
|
-
| **residue** | Bytes in `<state-dir>` that no DB row references and no code path reads: stray `mu.db*` copies, the `exports/` dir left by the removed `mu workstream export` <!-- doc-cli-drift:skip -->, empty per-workstream dirs, lock dirs that outlived their process. Reported by `mu doctor`'s `disk` section, never removed by it. | "garbage", "junk" (both imply mu may collect it — it may not) |
|
|
65
|
-
| **stale workspace** | A workspace whose `parent_ref` is N commits behind the project's default branch HEAD (per the workspace's local refs cache). Rendered as a color-coded `behind` column (green ≤2, yellow 3–9, red ≥10) in `mu workspace list` and `mu state`; ≥10 triggers a one-line warn in `mu state`, `mu task claim --for`, and `mu agent send` (or refusal on the two dispatch verbs with `--strict-staleness`). Pure observation — mu never auto-fetches. | "out of date", "drifting" |
|
|
66
|
-
| **refresh** | `mu workspace refresh <agent>` — rebase the agent's workspace onto a fresh base (default = backend's tracked main; `--from <ref>` overrides) WITHOUT touching the agent or pane. Refuses on dirty WC; surfaces conflicts as exit 5 with a resolve-in-place hint. The `none` backend errors (no VCS to rebase). | "recycle", "reset" (overloaded) |
|
|
67
|
-
| **backend** | Implementation of `MuxBackend` or `VcsBackend`. Always qualify which (**mux backend** / **VCS backend**) when both are in scope. | "driver", "provider" |
|
|
68
|
-
| **state source** | The system that reports **agent state**: the **control socket** for pi agents; otherwise herdr's `paneStatus()` on herdr, or murmur on tmux. mu reads local murmur pane options in one tmux call and uses `murmur status --json` only for remote workers. No source yields `unknown` with a reason; a pi agent whose socket does not answer is `unknown` (`ctl missing` / `ctl refused`), never a murmur reading. | "scraper" |
|
|
69
|
-
| **op** | The atomic unit of change: one row in the `ops` table, written by a trigger inside the same transaction as the mutation it records. Carries `(hlc, machine_id, group_id, actor, intent, entity, key, op, payload)`. A **semantic partial update** — the payload holds only the columns that actually changed, which is what makes per-field merge free. | "event", "delta", "change" (all overloaded) |
|
|
70
|
-
| **ops log** | The `ops` table: the single append-only record of every change, and the substrate **sync**, **undo**, and history are all queries or replays over. Canonical and ACID because it lives in `mu.db` alongside the tables it records. | "event log", "journal", "WAL" (reserved by SQLite) |
|
|
71
|
-
| **intent** | The semantic label on an **op** (`task.close`, `task.reparent`, `agent.spawn`). Set once per public SDK function via **op context**, not per mutation. `mu log` renders prose from it through one formatter. | "verb" (overloaded by CLI verbs), "action" |
|
|
72
|
-
| **group** | A set of ops sharing a `group_id` — one user-visible action. A cascade close writes N ops in one group, so `mu undo <group>` reverts them as a unit. Ids are uuids, so every verb that takes one (`mu undo <group>`, `mu log --group`) accepts any unique PREFIX via the shared `groupIdFromPrefix` — the affordance git gives for shas. An ambiguous prefix is a conflict (exit 4), never a guess. | "transaction" (reserved by SQLite), "batch" |
|
|
73
|
-
| **op context** | The per-connection `_op_ctx` temp table holding `(group_id, actor, intent, applying)` for the current transaction. Triggers read it to stamp ops; `applying=1` suppresses capture while ingesting a peer's ops (echo-loop guard). Set through the scoped `withOpContext(db, {...}, fn)` / `withCaptureSuppressed(db, fn)` helpers in `src/op-context.ts`, never by writing the table directly — the scoping is what guarantees a throw cannot leak a stale intent onto later ops. Two sibling temp tables support it: `_op_clock` (holds "now" for one trigger firing so the HLC advance reads a single consistent value) and `_op_dying` (natural keys of rows mid-DELETE, so FK-cascaded children can still resolve a key after their parent row is gone). | "ambient context", "thread local" |
|
|
74
|
-
| **apply** | Verb: land one **op** into the portable tables, honouring the **merge rules**. `applyOp` / `applyOps` in `src/apply.ts`. Always runs capture-suppressed (`applying=1`), so applying never mints a new op — that is the echo guard. Idempotent: applying the same op twice changes nothing, which is what makes `mu sync --repair` merely "re-read that peer's **segment** from zero". Distinct from **ingest**, which is the sync-side loop that reads a segment and calls apply per op. | "merge" (that's the rule set), "import", "replay" (replay is undo) |
|
|
75
|
-
| **merge rules** | The per-entity conflict policy the **apply** path implements. note/message = **grow-only set**; task/workstream = **per-field LWW**; edge = **LWW-element-set**; KNOWN machine-local entities (`MACHINE_LOCAL_ENTITIES`: agent, workspace, event, broadcast) are rejected as a bad peer; an UNRECOGNISED entity is a forward-compatible no-op — recorded, projected nowhere, never a defect, because a reader may legitimately be older than a writer in a mixed fleet. One rule per entity, chosen so that convergence needs no coordination between machines. | "conflict resolution" (implies prompting a human), "CRDT" (only loosely true) |
|
|
76
|
-
| **per-field LWW** | Last-writer-wins applied PER FIELD, not per row: each field of a task independently keeps the value written by the newest **HLC** that touched THAT field. Free, because ops are **semantic partial updates**. Row-level LWW is explicitly rejected — with agent crews on two machines, a crew closing a task while the operator edits its impact is concurrent by construction, and row-level would silently discard one edit. | "row LWW", "column versioning" (implies version vectors we don't need) |
|
|
77
|
-
| **grow-only set** | An entity that is only ever inserted, never updated: **notes** and log messages. Two machines therefore cannot disagree about a note's content, so there is nothing to resolve — apply is insert-if-absent. Identity is `(task, author, content, created_at)`, since a note's surrogate id is assigned per machine and cannot identify it across machines. `created_at` is part of it because machine-written text repeats (the reaper's "previous owner gone", dispatch's "CLAIM: dispatch"), and each repeat is a separate note. | "append-only log" (that's the ops log), "set" |
|
|
78
|
-
| **LWW-element-set** | The rule for **edges**: set membership where both the add and the remove carry an **HLC**, so a remove and a later re-add converge in either arrival order. An edge has no fields worth merging — it is present or absent. | "add-wins set", "2P-set" (a 2P-set can't re-add) |
|
|
79
|
-
| **tombstone** | An `op='del'` row. There is NO tombstone table: a tombstone is an ordinary op carrying an **HLC**, so out-of-order arrival is just "compare HLCs", the same comparison the update path makes. A late **put** older than a seen tombstone loses; a tombstone older than a seen put loses. | "deletion marker", "grave" |
|
|
80
|
-
| **teardown** | Removing a workstream: `mu workstream teardown` kills its **mux session** and deletes its rows, writing **tombstone** ops so the history survives in the **ops log**. **Reversible** — `mu undo <group>` restores every row it removed, so no backup is warranted before running it. Dry-run by default; `--yes` commits. `mu workstream list --torn-down` lists past teardowns with the **group** to undo. NOT called "destroy": that word says irreversible, which is wrong, and it drove real operators to hoard pre-flight DB copies. What teardown does not restore is what was never in the DB: killed panes and freed **workspace** dirs. | "destroy", "delete workstream", "nuke", "purge" |
|
|
81
|
-
| **resurrection** | A **put** with an HLC NEWER than a seen **tombstone** for the same key, which legitimately recreates the row. Distinguishable from a stale put (older than the tombstone, and must lose) because **provenance** outlives the row: ops are never deleted when a table row is. | "undelete" (that's `mu undo`), "revive" |
|
|
82
|
-
| **provenance** | Which **HLC** last wrote a given field of a given key — what **per-field LWW** compares against. DERIVED from the **ops log** by query, never stored in a side table: `ops` already records which fields each HLC touched, so a side table would be a denormalisation that can disagree with the log with no procedure to decide which side is right. Costs zero extra storage and survives row deletion for free, which is what makes **resurrection** work. | "version vector", "clock table", "field metadata" |
|
|
83
|
-
| **inverse op** | The op that reverts another: `del` for a **put** that created a row, a `put` restoring prior field values for a put that changed them, a `put` restoring the row for a `del`. Written through the normal capture path, so it carries a fresh **HLC** and is an ordinary op — which is why an undo syncs and is itself undoable. | "rollback", "revert commit", "compensating transaction" |
|
|
84
|
-
| **superseded** | Of a **group**: a later group has written the same field(s) since, so emitting **inverse ops** would discard that newer work. `mu undo` refuses (exit 4) and names the conflict rather than clobbering or silently skipping; `--force` overrides. Detected PER FIELD, so a later edit to a different field of the same row does not block the undo. | "stale", "conflicted" (conflict is the exit-code label) |
|
|
85
|
-
| **drift** | Divergence between the **ops log** and the live tables. Means capture missed a mutation or **apply** is lossy — always a BUG, never operator error, and never transient. Detected by `mu doctor` (shallow: every live row must have at least one op naming its key) and `mu doctor --deep` (full rebuild + field-level diff). Exits 5. Remediation is NOT 'rebuild': if capture missed the mutation, the live tables hold work the log never saw. | "inconsistency", "corruption" (corruption is the SQLite file being damaged, which is different) |
|
|
86
|
-
| **shallow / deep check** | The two drift tiers. Shallow is ~3ms and runs in every `mu doctor`; deep rebuilds the whole log (~0.6ms per op) and runs only under `--deep`. Shallow is a smoke alarm, not a proof — it cannot see an uncaptured UPDATE because the row's key still has ops. | "quick/full scan", "fsck" |
|
|
87
|
-
| **mixed fleet** | More than one machine sharing state, where the machines differ in OS or filesystem semantics. The three hazards mu checks for: the DB living inside the sync dir, the DB on a network mount, and workstream names that differ only by case (fine on ext4, colliding on APFS/NTFS). | "cluster" (implies coordination mu does not have), "multi-device" |
|
|
88
|
-
| **disk↔DB reconciliation** | The `disk` section of `mu doctor`: comparing `<state-dir>` against the database in BOTH directions (**missing workspace dir** one way, **workspace orphan** the other) plus **residue**. Distinct from **reconcile**, which re-derives rows from the *mux*, and from **drift**, which compares the ops log to the live tables. Report-only: every finding names its cleanup command and mu runs none of them. | "gc", "fsck", "cleanup" (nothing is cleaned) |
|
|
89
|
-
| **portable** | Of a table or entity: syncable across machines. Portable = `workstreams`, `tasks`, `task_edges`, `task_notes`. **Machine-local** = `agents` (holds `pane_id`), `vcs_workspaces` (absolute paths differ across macOS/Linux), `machine_identity`, `schema_version`, `sync_peers` (local watermarks), and `ops` itself — the ops table is never wholesale-copied; individual op *rows* ship, filtered by synced entity and carried by **segments**. Machine-local ops are still recorded (so `mu log` shows them) but never leave the machine. Declared as code in `src/db.ts`: `PORTABLE_TABLES` / `MACHINE_LOCAL_TABLES` / `SYNCED_ENTITIES`, guarded so the two table lists must partition `EXPECTED_TABLES`. | "shared", "global" |
|
|
90
|
-
| **HLC** | Hybrid logical clock: `(wall_ms, counter, machine_id)` serialized to a sortable TEXT. The ordering key for every op. Monotonic — never regresses — so a laptop waking with a skewed clock cannot resurrect stale edits. Persisted in `machine_identity` because every mu invocation is a fresh process. | "timestamp", "version" (both imply wall clock) |
|
|
91
|
-
| **machine_id** | Per-state-directory uuid seeded on first `openDb` and stored in `machine_identity`. Identifies one mu DB as a **peer** and names its **segment**; users do not configure it. | "device id", "host id" |
|
|
92
|
-
| **segment** | An append-only JSONL file at `<sync-dir>/<machine_id>.jsonl` holding one machine's ops. **Single-writer-per-file**: a machine appends only to its own and read-onlys every other, so no file is ever contended and any file-mover (Syncthing, rsync, scp, git, USB) is adequate transport. Derived and regenerable from the `ops` table, so losing one costs a re-flush. | "replica", "shard", "stream" |
|
|
93
|
-
| **peer** | Another machine, discovered implicitly as a `<machine_id>.jsonl` **segment** in the sync dir. There is no membership list to configure or keep consistent — dropping a segment in the folder joins the cluster. Referred to by `machine_id`, or any unique PREFIX of one (the affordance git gives for shas); an ambiguous prefix is a conflict, never a guess. NOT by hostname: `machine_identity.hostname` is machine-local and never ships, so a hostname display would need the membership file `MU_SYNC_PEERS` was rejected for being. A peer is **stale** when its segment file has not moved in 24h — a display threshold, not an env var. | "node", "replica", "remote" |
|
|
94
|
-
| **watermark** | Per-peer integer in `sync_peers.last_applied_seq`: how far into that peer's **segment** we have applied, counted in LINES of that segment (not the peer's `ops.seq`, which is a local-only cursor on their machine and means nothing here). One integer suffices because segments are append-only and ordered. Advanced only as far as the last GOOD record, so damage is re-read rather than skipped. | "offset", "cursor" (cursor is reserved by `mu log`) |
|
|
95
|
-
| **flush** / **ingest** | The two halves of sync. Flush appends local ops to my own **segment**; ingest reads each peer segment from its **watermark** and applies. Both run ambiently on every mu invocation when `MU_SYNC_DIR` is set — there is no daemon. | "push"/"pull" (imply mu moves files; it does not) |
|
|
96
|
-
| **reprojection** | The pass that lands **ops** which could not be applied when they arrived because the row they hang off was not here yet — an `edge` or `note` whose task came from a DIFFERENT **peer**'s **segment**. Runs once after every **ingest** pass (never per peer: an edge in one segment may name a task in another, so only the union suffices) and asks the **ops log** which of them are resolvable NOW, rather than keeping a retry queue that could disagree with the log and would not survive a short-lived mu process. Excludes ops whose parent is gone and keys with a newer `del`, so it never resurrects a deleted edge. `reprojectDeferredOps` in `src/apply.ts`. | "retry queue", "pending ops", "deferred apply" |
|
|
97
|
-
| **qualified ref** | An entity-arg form `<workstream>/<name>` that targets a specific workstream's task / agent / workspace without `-w`. Bare `<name>` still resolves via the standard chain (`-w` / `$MU_SESSION` / current tmux session). Mixing a qualified ref with a non-matching `-w` is rejected (`UsageError`). When a bare name appears AND no workstream resolves AND ≥2 workstreams contain that name, mu raises `NameAmbiguousError` (exit 4) listing every candidate as a qualified-form one-paste fix. | "fully-qualified id" (in prose), "prefixed name" |
|
|
98
|
-
| **doctor** | The diagnostic command + report | "health check", "diagnose" |
|
|
99
|
-
| **CLI** | The `mu` command-line binary | "tool" (overloaded), "binary" (only when relevant) |
|
|
100
|
-
| **extension** | The pi extension shipped in the same package | "plugin" |
|
|
101
|
-
| **skill** | The bundled SKILL.md that teaches the LLM | "system prompt", "instruction" |
|
|
102
|
-
| **DB** / **registry** | `~/.local/state/mu/mu.db` and its tables | "store", "database" (full word OK in prose) |
|
|
103
|
-
| **substrate** | An external system mu depends on (tmux, herdr, jj, sl, git, sqlite) | "dependency" (means npm dep), "service" |
|
|
104
|
-
| **operation** | A canonical mu verb (e.g. `mu task add`). Each verb is a thin CLI wrapper over a typed function in `src/*.ts` — the SDK and the CLI share one surface. | "command" (overloaded), "action" |
|
|
105
|
-
| **reconcile** | Verb: re-derive registry rows from substrate reality (the **mux**). Always runs in `mu agent list` and `mu doctor`. | "sync", "refresh" |
|
|
106
|
-
| **adopt** | Verb (`mu agent adopt`): register an existing pane as a managed **agent**. The inverse of `mu agent list`'s 'orphan' state. Pane must be in the workstream's **mux session**. | "import", "absorb" |
|
|
107
|
-
| **control socket** / **ctl** | The per-agent unix socket the mu pi extension serves inside a pi agent's own interactive session (`$MU_CTL_SOCK`). mu derives its path from workstream and agent name, so nothing is stored. Carries `hello`, `status`, `send`, `wait` (resolves on pi's `agent_settled`), `abort` and `fresh` (new session + prompt) as JSON lines. The pane stays pi's normal TUI. **ctl state** is whether it answers: `ok`, `missing` or `refused`. A running pi keeps the extension it loaded at start; an **outdated extension** lacks an op this mu sends (`AgentExtensionOutdatedError`, exit 4) or reports an older `extVersion` in `hello` (doctor warns). pi's `/reload` re-imports it. | "rpc" (pi's `--mode rpc` replaces the TUI; mu does not use it), "daemon" |
|
|
108
|
-
| **delegate** | A **scratch workstream** agent started for one task: spawn, send the task, return at once, and hand its answer back when its control-socket `wait` resolves. The `mu_delegate` tool in the mu pi extension does this in one call; the CLI equivalent is `mu agent spawn -w scratch` + `mu agent send` + `mu agent wait --json`. The pane stays visible: attach, steer, abort, or keep talking to it. No agent types; the brief is ad-hoc text. | "subagent", "child agent", "worker" (a worker claims DAG tasks) |
|
|
109
|
-
| **lastText** | The text of the final assistant message of a pi agent run. The mu pi extension records it on `agent_end` and returns it verbatim in the control-socket `wait` reply; `mu agent wait --json` passes it through. mu never interprets it. | "result", "answer file", "output" (alone) |
|
|
110
|
-
| **hidden subagent** | A child agent in other tools (pi-subagents, pi's example subagent extension, Claude Code and Codex task tools) that runs out of sight: no pane to attach to, no way to steer it or keep talking to it, and its transcript collapses into a result. Usually comes with built-in agent types. mu's counterpart is the **delegate**. | "subagent" alone in mu prose |
|
|
111
|
-
| **TUI** | The interactive ink-based dashboard launched by bare `mu` in a TTY or explicitly by `mu state --tui`. Lives in `src/cli/tui/`. Read-only against SQLite (yanks, never executes). | "GUI", "interactive mode" |
|
|
112
|
-
| **dashboard** | The TUI's main screen — the grid of cards above the status bar. | "home screen", "main view" |
|
|
113
|
-
| **card** | A glanceable summary tile on the dashboard, identified by its toggle digit (0-9). Wrapped in a TitledBox. | "panel", "section" (overloaded) |
|
|
114
|
-
| **popup** | A fullscreen drill-down opened with `Shift+0`-`Shift+9` or a keybind-only shortcut such as `g` for DAG; single-popup invariant. Closed with `Esc`/`q`. | "modal", "dialog", "detail view" |
|
|
115
|
-
| **TitledBox** | The `<TitledBox>` component (`src/cli/tui/titled-box.tsx`) that renders a rounded border with the section header inset into the top border line. The visual primitive used by every card / popup / help overlay. | "header box", "box" (alone) |
|
|
116
|
-
| **tick** | The TUI's periodic data refresh (default 1s; `+/-/=` adjusts). Owned by a single `setInterval` in `<App>`. | "poll", "refresh" (verb sense), "frame" |
|
|
117
|
-
| **yank** | Copy the canonical `mu` command for the focused row to the clipboard. Bound to `y` in every popup. | "copy", "export command" |
|
|
118
|
-
| **footer** | The persistent bottom line on the dashboard showing the last yank. Cleared with `c`. | "status line" (reserved for status bar), "toast" |
|
|
119
|
-
| **toast** | Transient in-popup message (e.g. "tick floor 100ms" when `+` hits floor). | "notification", "banner" |
|
|
120
|
-
| **act-intent** | The conceptual action a `y` keypress would trigger. **Never executed by the TUI** — the user runs the yanked command in their shell. The R1 read-only contract: model drives the CLI. | "command intent", "action proposal" |
|
|
121
|
-
| **help overlay** | The `?` / `F1` modal showing the global + per-popup keymap. Same TitledBox family as cards/popups. | "keys", "cheat sheet" |
|
|
122
|
-
| **glanceable** | Design property of cards: readable at a glance, no cursor, no row interaction. The contract is "never exhaustive" — long lists clip with `+M more` hint pointing at the popup. | "compact", "summary" (use the noun form for the data, the adjective for the property) |
|
|
123
|
-
| **drill-down** | Design property of popups: full-screen, focused, scrollable, filterable. The exhaustive view a card promises in its `+M more` hint. | "detail view", "expansion" |
|
|
13
|
+
### Topology and multiplexer
|
|
124
14
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
15
|
+
| Term | Meaning | Don't use |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| **workstream** | The unit of organization: one **mux session** and one DB partition. | "project", "session" (alone), "context" |
|
|
18
|
+
| **scratch workstream** | The reserved workstream `scratch` for helpers outside any task DAG. Auto-created on first spawn with explicit `-w scratch`; `mu workstream init scratch` is rejected. | "throwaway ws", "temp workstream" |
|
|
19
|
+
| **multiplexer** / **mux** | The terminal program that owns panes: **tmux** or **herdr**. "mux" in code (`src/mux/`, `MU_MUX`), "multiplexer" in prose; one is active per invocation. | "terminal", "emulator", "backend" (alone) |
|
|
20
|
+
| **mux backend** | An implementation of `MuxBackend` (`src/mux/tmux.ts`, `src/mux/herdr.ts`). Owns everything backend-specific. | "driver", "provider", "adapter" |
|
|
21
|
+
| **mux detection** | Picks the mux backend: `MU_MUX` → `HERDR_ENV=1` → `$TMUX` → binary on `PATH` (tmux wins a tie) → `NoMultiplexerError`. | "auto-detect", "probe" |
|
|
22
|
+
| **mux session** | The container for one workstream's panes: tmux session `mu-<workstream>`, or herdr workspace labelled `mu-<workstream>`. | "mux workspace", "session" (alone) |
|
|
23
|
+
| **tmux session** | The tmux flavour of a **mux session**. | "session" (alone) |
|
|
24
|
+
| **window** | The grouping inside a mux session (tmux window, herdr tab), named by **window name**. | "tab" (except the `--tab` flag) |
|
|
25
|
+
| **window name** | A window's name: the agent's `--tab` value, or the agent name. | "tab name" |
|
|
26
|
+
| **pane** | One shell view inside a **window**. | "terminal", "shell" |
|
|
27
|
+
| **pane id** | The mux's stable pane handle: tmux `%15`, herdr `w1:p1`. Not the volatile tmux pane index, which is never stored. | "pane number", "pane index" |
|
|
28
|
+
| **pane title** | Equals the agent name. The fallback for **actor** identity when `$MU_AGENT_NAME` is absent. | "pane name" |
|
|
29
|
+
| **substrate** | An external system mu depends on: tmux, herdr, jj, sl, git, SQLite. | "dependency" (an npm dep), "service" |
|
|
30
|
+
| **crew** | Informal: the agents in a workstream. Prose only. | |
|
|
31
|
+
|
|
32
|
+
### Agents
|
|
33
|
+
|
|
34
|
+
| Term | Meaning | Don't use |
|
|
35
|
+
| --- | --- | --- |
|
|
36
|
+
| **agent** | A named process in a pane with a row in `agents`. Identity is `$MU_AGENT_NAME`, else the **pane title**. | "subagent", "worker" (a role) |
|
|
37
|
+
| **worker** | An agent in its role as task claimer. Prefer **agent** when ambiguous. | |
|
|
38
|
+
| **remote worker** | A worker running on another machine while its pane and the DB stay local. Recorded in task notes as `REMOTE: <host>:<path>` and `REMOTE_BASE: <agent>:<sha>`. | "remote agent record", "host registry" |
|
|
39
|
+
| **actor** | Whoever caused a state change (`ops.actor`); need not be a worker. | "caller", "author" (notes only) |
|
|
40
|
+
| **role** | The capability flag: `full-access` or `read-only`. | "permission", "tier" |
|
|
41
|
+
| **persistent** | An agent that stays alive across tasks. | "long-lived" (prose only) |
|
|
42
|
+
| **one-shot** | An agent that exists for one task. | "ephemeral", "transient" |
|
|
43
|
+
| **delegate** | A scratch agent started for one task; its answer returns when its control-socket `wait` resolves. The `mu_delegate` tool does it in one call; the pane stays attachable. | "subagent", "child agent" |
|
|
44
|
+
| **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) |
|
|
45
|
+
| **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" |
|
|
46
|
+
| **state source** | The **control socket** for pi agents; else herdr's `paneStatus()` or, on tmux, murmur. A silent pi socket gives `unknown` (`ctl missing`, `ctl refused`). | "scraper" |
|
|
47
|
+
| **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" |
|
|
48
|
+
| **outdated extension** | A running pi whose loaded extension is older than mu (exit 4 on a missing op). pi's `/reload` fixes it. | |
|
|
49
|
+
| **lastText** | A pi run's final assistant message, returned verbatim by `wait`. mu never interprets it. | "result", "output" (alone) |
|
|
50
|
+
| **ghost** | An `agents` row whose `pane_id` no longer exists. Pruned by **reconcile**. | "dead agent", "stale row" |
|
|
51
|
+
| **reaper** | The part of reconcile that releases a ghost's tasks to `OPEN`. Makes `mu task wait` exit 6. | "garbage collector", "janitor" |
|
|
52
|
+
| **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`) |
|
|
53
|
+
| **adopt** | Register an existing pane in the workstream's mux session as an agent (`mu agent adopt`). | "import", "absorb" |
|
|
54
|
+
| **reconcile** | Re-derive registry rows from the mux. Runs in `mu agent list` and `mu doctor`. | "sync", "refresh" |
|
|
55
|
+
| **extension** | The pi extension shipped in the mu package. | "plugin" |
|
|
56
|
+
| **skill** | The bundled `SKILL.md` that teaches the LLM. | "system prompt" |
|
|
57
|
+
|
|
58
|
+
### Tasks
|
|
59
|
+
|
|
60
|
+
| Term | Meaning | Don't use |
|
|
61
|
+
| --- | --- | --- |
|
|
62
|
+
| **task** | A node in the DAG with mandatory `impact` and `effort_days`, a **task status**, and a **substate**. | "issue", "ticket", "item" |
|
|
63
|
+
| **task status** | `OPEN`, `IN_PROGRESS`, or `CLOSED`. Any `CLOSED/*` satisfies a blocker. | "state" |
|
|
64
|
+
| **substate** | What the status means, shown as `STATUS/substate`; never null. `OPEN`: `todo`, `parked`. `IN_PROGRESS`: `active`. `CLOSED`: `done`, `rejected`, `wontfix`, `duplicate`, `superseded`. | "resolution", "reason" |
|
|
65
|
+
| **park** / **unpark** | `OPEN/todo` ↔ `OPEN/parked` (park needs `--why`). Parked tasks leave **ready**. | "defer", "snooze" |
|
|
66
|
+
| **task DAG** / **graph** | The directed acyclic graph of tasks. | "task list", "tree" |
|
|
67
|
+
| **edge** | The single edge type: `A blocks B` means A must close before B starts. | "dependency" (prose only) |
|
|
68
|
+
| **subtree** / **scope** | Tasks reachable from a root via blocks-edges. | "subgraph" |
|
|
69
|
+
| **track** | An independent subtree found by parallel-track detection. | "branch", "lane" |
|
|
70
|
+
| **diamond merge** | Merging two tracks that share a prerequisite into one. | "join" |
|
|
71
|
+
| **ready** | An `OPEN/todo` task with no open blockers (the `ready` view). | "unblocked", "available" |
|
|
72
|
+
| **goals** | Tasks with no outgoing edges (the `goals` view). | "leaves", "targets" |
|
|
73
|
+
| **sort key** | `--sort` value: `roi` (impact ÷ effort), `recency`, `age`, or `id`. | "order by" |
|
|
74
|
+
| **note** | An append-only piece of context on a task. | "comment", "log" |
|
|
75
|
+
| **claim** | Set `tasks.owner` to an agent. Atomic compare-and-set. | "assign" (prose only), "lock" |
|
|
76
|
+
| **owner** | The worker in `tasks.owner`; NULL after an **anonymous claim**. Never syncs. | "claimer", "assignee" |
|
|
77
|
+
| **anonymous claim** | A `--self` claim by an actor that is not a worker. `owner` stays NULL; `ops.actor` records who. | "unowned claim" |
|
|
78
|
+
| **release** | Clear `tasks.owner`. | "unclaim", "unassign" |
|
|
79
|
+
|
|
80
|
+
### Workspaces and housekeeping
|
|
81
|
+
|
|
82
|
+
| Term | Meaning | Don't use |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| **workspace** | A VCS-isolated checkout (jj workspace, sl or git worktree, or a copy). Never the herdr sense. | "branch", "checkout" |
|
|
85
|
+
| **VCS backend** / **backend** | An implementation of `VcsBackend` or `MuxBackend`. Qualify which when both are in scope. | "driver", "provider" |
|
|
86
|
+
| **stale workspace** | A workspace behind the default branch (the `behind` column). mu never fetches. | "out of date", "drifting" |
|
|
87
|
+
| **refresh** | Rebase an agent's workspace onto a fresh base without touching its pane. | "recycle", "reset" |
|
|
88
|
+
| **workspace orphan** | A dir under `<state-dir>/workspaces/<workstream>/` with no `vcs_workspaces` row. Blocks later `--workspace` spawns. | "stray dir" |
|
|
89
|
+
| **stranded** | A workspace orphan whose workstream row is gone too. Only `mu workspace orphans --all` and `mu doctor` reach it. | "abandoned" |
|
|
90
|
+
| **missing workspace dir** | A `vcs_workspaces` row whose path is gone. Worse than an orphan: read surfaces still call it healthy. | "dangling row" |
|
|
91
|
+
| **dormant** | An idle workstream with no agents or workspaces: **finished** (all closed, 14 days) or **abandoned** (open tasks, 60 days). | "stale", "dead", "empty" |
|
|
92
|
+
| **residue** | Bytes in `<state-dir>` that no row references and no code reads. Reported by `mu doctor`, never removed. | "garbage", "junk" |
|
|
93
|
+
| **disk↔DB reconciliation** | `mu doctor`'s report-only `disk` section. Not **reconcile** (rows vs mux) or **drift** (log vs tables). | "gc", "cleanup" |
|
|
94
|
+
| **doctor** | The diagnostic command and its report. | "health check" |
|
|
95
|
+
|
|
96
|
+
### Ops log, sync, and undo
|
|
97
|
+
|
|
98
|
+
| Term | Meaning | Don't use |
|
|
99
|
+
| --- | --- | --- |
|
|
100
|
+
| **op** | One `ops` row, written by a trigger in the mutation's transaction. Holds only changed columns. | "event", "delta", "change" |
|
|
101
|
+
| **ops log** | The `ops` table: the append-only record that sync, undo, and history read. | "event log", "journal", "WAL" |
|
|
102
|
+
| **log entry** | An op as rendered by `mu log`, through one formatter (`src/log-render.ts`). | "message", "event" |
|
|
103
|
+
| **kind** | The operator's channel tag on a log entry (`mu log --kind`), stored as the op's `entity`. | "category", "type" |
|
|
104
|
+
| **log ledger** | A convention: a custom `--kind` used as a watcher loop's durable dedupe record. | "state file" |
|
|
105
|
+
| **intent** | The semantic label on an op (`task.close`), set once per SDK function via **op context**. | "verb", "action" |
|
|
106
|
+
| **group** | The ops of one user action (`group_id`), undone as a unit. Any unique id prefix works. | "transaction", "batch" |
|
|
107
|
+
| **op context** | The `_op_ctx` temp table that stamps intent, actor, and group onto ops. Set only via `src/op-context.ts`. | "thread local" |
|
|
108
|
+
| **apply** | Land one op under the **merge rules** (`src/apply.ts`). Idempotent; mints no op. | "merge", "import", "replay" |
|
|
109
|
+
| **merge rules** | Notes: **grow-only set**. Tasks, workstreams: **per-field LWW**. Edges: **LWW-element-set**. Unknown entities are ignored. | "conflict resolution", "CRDT" |
|
|
110
|
+
| **per-field LWW** | Each field keeps the value from the newest **HLC** that wrote it. | "row LWW" |
|
|
111
|
+
| **grow-only set** | Insert-only entities (notes, log messages). Identity is `(task, author, content, created_at)`. | "append-only log" |
|
|
112
|
+
| **LWW-element-set** | Add and remove both carry an HLC, so re-adding converges in any order. | "2P-set" |
|
|
113
|
+
| **tombstone** | An `op='del'` row. There is no tombstone table. | "deletion marker" |
|
|
114
|
+
| **resurrection** | A put newer than a seen tombstone, which legitimately recreates the row. | "undelete", "revive" |
|
|
115
|
+
| **provenance** | Which HLC last wrote a field, derived from the ops log by query, never stored. | "version vector" |
|
|
116
|
+
| **inverse op** | The op that reverts another; an ordinary op, so undo syncs and is undoable. | "rollback" |
|
|
117
|
+
| **superseded** (group) | A later group wrote the same field, so `mu undo` refuses (exit 4) without `--force`. | "stale" |
|
|
118
|
+
| **teardown** | Kill a workstream's mux session and delete its rows. `mu undo` restores rows, not panes or dirs. | "destroy", "nuke", "purge" |
|
|
119
|
+
| **drift** | The ops log and live tables disagree. Always a bug. | "inconsistency", "corruption" |
|
|
120
|
+
| **shallow / deep check** | Drift checks in `mu doctor` and `mu doctor --deep` (full rebuild). | "fsck" |
|
|
121
|
+
| **portable** | Syncable: `workstreams`, `tasks`, `task_edges`, `task_notes`. The rest is **machine-local** (`src/db.ts`). | "shared", "global" |
|
|
122
|
+
| **HLC** | Hybrid logical clock `(wall_ms, counter, machine_id)`; orders every op. Never regresses. | "timestamp", "version" |
|
|
123
|
+
| **machine_id** | Per-state-dir uuid naming this DB's **peer** and **segment**. | "device id", "host id" |
|
|
124
|
+
| **segment** | `<sync-dir>/<machine_id>.jsonl`: one machine's ops, single writer. | "replica", "shard" |
|
|
125
|
+
| **peer** | Another machine, found by its segment; named by `machine_id` prefix. **Stale** after 24h unchanged. | "node", "remote" |
|
|
126
|
+
| **watermark** | `sync_peers.last_applied_seq`: how many lines of a peer's segment have been applied. | "offset", "cursor" |
|
|
127
|
+
| **flush** / **ingest** | Write my ops to my segment; apply peers' from their watermarks. Every invocation, when `MU_SYNC_DIR` is set. | "push", "pull" |
|
|
128
|
+
| **reprojection** | After ingest, apply deferred ops whose parent arrived from another peer. | "retry queue" |
|
|
129
|
+
| **mixed fleet** | Machines sharing state across different OSes or filesystems. | "cluster" |
|
|
130
|
+
|
|
131
|
+
### CLI and TUI
|
|
132
|
+
|
|
133
|
+
| Term | Meaning | Don't use |
|
|
134
|
+
| --- | --- | --- |
|
|
135
|
+
| **CLI** | The `mu` binary. | "tool" |
|
|
136
|
+
| **operation** | A canonical mu verb, each a thin wrapper over a typed SDK function. | "command", "action" |
|
|
137
|
+
| **qualified ref** | `<workstream>/<name>`, used instead of `-w`. | "prefixed name" |
|
|
138
|
+
| **DB** / **registry** | `<state-dir>/mu.db` and its tables. | "store" |
|
|
139
|
+
| **TUI** | The read-only ink dashboard: bare `mu` in a TTY, or `mu state --tui`. | "GUI" |
|
|
140
|
+
| **dashboard** | The TUI's grid of cards. | "home screen" |
|
|
141
|
+
| **card** | A dashboard tile with a toggle digit 0-9. | "panel", "section" |
|
|
142
|
+
| **popup** | A card's full-screen drill-down (`Shift+digit`). One at a time. | "modal", "dialog" |
|
|
143
|
+
| **TitledBox** | The bordered component every card and popup uses. | "box" (alone) |
|
|
144
|
+
| **tick** | The TUI's data refresh (default 1s; `+`, `-`, `=` adjust). | "poll", "frame" |
|
|
145
|
+
| **yank** | Copy the focused row's `mu` command to the clipboard (`y`). | "copy" |
|
|
146
|
+
| **act-intent** | The action a yank stands for. The TUI never runs it. | "action proposal" |
|
|
147
|
+
| **footer** | The bottom line showing the last yank. `c` clears it. | "status line" |
|
|
148
|
+
| **toast** | A transient in-popup message. | "notification" |
|
|
149
|
+
| **help overlay** | The `?` or `F1` keymap overlay. | "cheat sheet" |
|
|
150
|
+
| **glanceable** / **drill-down** | The two TUI properties: cards are never exhaustive; popups are scrollable and filterable. | "compact", "detail view" |
|
|
151
|
+
|
|
152
|
+
## Topology
|
|
128
153
|
|
|
129
154
|
```
|
|
130
|
-
workstream (one
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
│
|
|
134
|
-
│
|
|
135
|
-
│
|
|
136
|
-
│
|
|
137
|
-
│ │
|
|
138
|
-
│ │
|
|
139
|
-
│
|
|
140
|
-
|
|
141
|
-
│ │ │ pane │ │ pane ││ │ │ pane │ │ │
|
|
142
|
-
│ │ │ (pi) │ │ (pi) ││ │ │ (pi, role=read-only) │ │ │
|
|
143
|
-
│ │ │ agent │ │ agent ││ │ │ agent │ │ │
|
|
144
|
-
│ │ └──────────┘ └────────┘│ │ └────────────────────────┘ │ │
|
|
145
|
-
│ └──────────────────────────┘ └──────────────────────────────┘ │
|
|
146
|
-
│ │
|
|
147
|
-
│ the crew = { worker-1, worker-2, reviewer-1 } (informal) │
|
|
148
|
-
└───────────────────────────────────────────────────────────────────┘
|
|
149
|
-
|
|
150
|
-
partitioned by session_id in ~/.local/state/mu/mu.db
|
|
155
|
+
workstream (one DB partition)
|
|
156
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
157
|
+
│ mux session: mu-auth-refactor (tmux session / herdr ws) │
|
|
158
|
+
│ ┌──────────────────────────┐ ┌──────────────────────────┐ │
|
|
159
|
+
│ │ window: Backend │ │ window: Review │ │
|
|
160
|
+
│ │ ┌──────────┐ ┌─────────┐ │ │ ┌──────────────────────┐ │ │
|
|
161
|
+
│ │ │ worker-1 │ │worker-2 │ │ │ │ reviewer-1 (read-only)│ │ │
|
|
162
|
+
│ │ │ pane │ │ pane │ │ │ │ pane │ │ │
|
|
163
|
+
│ │ └──────────┘ └─────────┘ │ │ └──────────────────────┘ │ │
|
|
164
|
+
│ └──────────────────────────┘ └──────────────────────────┘ │
|
|
165
|
+
└──────────────────────────────────────────────────────────────┘
|
|
151
166
|
```
|
|
152
167
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
from the `tab:` frontmatter field and may group multiple agents in one
|
|
157
|
-
window.
|
|
158
|
-
|
|
159
|
-
This makes the claim protocol zero-config: an agent runs
|
|
160
|
-
`mu task claim foo` and mu resolves the **actor** by reading
|
|
161
|
-
`$MU_AGENT_NAME`, falling back to asking the **mux backend** for the
|
|
162
|
-
current pane's title. Env-first is backend-independent and more
|
|
163
|
-
reliable than scraping; the fallback exists because **adopted** panes
|
|
164
|
-
predate the env injection and carry only a title.
|
|
168
|
+
Actor resolution reads `$MU_AGENT_NAME`, then the **pane title**
|
|
169
|
+
(adopted panes have only a title). On tmux, read `#{pane_title}`, not
|
|
170
|
+
`#W`: they differ when agents share a window.
|
|
165
171
|
|
|
166
|
-
|
|
167
|
-
window name (`#W`)** — they differ when several agents share a
|
|
168
|
-
window.
|
|
172
|
+
## Agent state values
|
|
169
173
|
|
|
170
|
-
|
|
174
|
+
Glyphs live in `src/glyphs.ts` (`AGENT_STATE_GLYPH`).
|
|
171
175
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
### Agent state
|
|
175
|
-
|
|
176
|
-
Agent state is runtime data and is not persisted. The codepoints live in
|
|
177
|
-
`src/glyphs.ts` (`AGENT_STATE_GLYPH`).
|
|
178
|
-
|
|
179
|
-
| Value | Glyph slot | Meaning |
|
|
176
|
+
| Value | Glyph | Meaning |
|
|
180
177
|
| --- | --- | --- |
|
|
181
|
-
| `busy` | nf-fa-play |
|
|
178
|
+
| `busy` | nf-fa-play | Working |
|
|
182
179
|
| `needs_input` | nf-fa-moon_o | Waiting for input |
|
|
183
180
|
| `needs_permission` | nf-fa-lock | Waiting for a human answer |
|
|
184
|
-
| `unknown` | nf-fa-question_circle | No
|
|
185
|
-
|
|
186
|
-
The state source is the control socket for pi agents, else herdr on the
|
|
187
|
-
herdr backend and murmur on the tmux backend. The extension's `busy`
|
|
188
|
-
maps to `busy` and `idle` to `needs_input`. mu maps murmur `working` to `busy`, `blocked` to
|
|
189
|
-
`needs_permission`, and `idle`, `done`, and `crashed` to `needs_input`.
|
|
190
|
-
The retained `agents.status` column is deprecated and always contains
|
|
191
|
-
`spawning`; no runtime read uses it.
|
|
192
|
-
|
|
193
|
-
### Agent lifecycle verbs
|
|
194
|
-
|
|
195
|
-
| Verb | Effect |
|
|
196
|
-
| --------------------- | --------------------------------------------------------------------------- |
|
|
197
|
-
| `mu task release feature_a`| Clears the task owner for `feature_a`. The agent who claimed it is unaffected. |
|
|
198
|
-
| `mu agent close alice` | Terminates alice's pane and removes from registry. Destructive. |
|
|
199
|
-
| `mu agent send alice --fresh '…'` | New pi session + prompt as one control-socket operation; returns once the prompt's run started. Refused while busy unless `--force` (abandons the turn). pi only. |
|
|
200
|
-
| `mu agent abort alice` | Stops a pi agent's current turn through its control socket (pi's Esc) and waits for it to settle. No-op when idle. Queued follow-ups return to the pane's editor unsent. Non-pi agents: use kick. |
|
|
201
|
-
| `mu agent remote-env alice` | Prints (never runs) the ssh socket-forward args and identity env for a remote pi agent's `--command`, so its control socket answers at the local derived path. |
|
|
202
|
-
| `mu agent kick alice` | Signals (default SIGINT) the foreground process group of alice's pane TTY. For wedged tool subprocesses (`find /`, busy-wait); the wrapping CLI itself is untouched. Refuses when the foreground IS the wrapping CLI. |
|
|
203
|
-
| `mu agent wait alice bob --first` | Blocks until an agent finishes (busy → any other state). The task-less counterpart to `mu task wait` for scratch/off-the-cuff helpers that own no task. `--any`/`--first` fire on the first; default all. Exit 0 met, 5 timeout, 6 a watched pane died. |
|
|
204
|
-
| *(none)* | There is no detach verb. Use tmux detach to leave a workstream attached session without killing panes. |
|
|
205
|
-
|
|
206
|
-
### Verbs that move tasks through the lifecycle
|
|
207
|
-
|
|
208
|
-
| Verb | Effect |
|
|
209
|
-
| ------------------------------------- | ----------------------------------------------------- |
|
|
210
|
-
| `mu task add <id> ...` | Creates a new OPEN task. `--note <text>` appends an initial note in the same transaction; `--note-author <name>` overrides the note author. |
|
|
211
|
-
| `mu task close <id> [--as <substate> --why <text>]` | Sets `CLOSED` (default `CLOSED/done`). A non-`done` `--as` requires `--why` and prints the dependents it unblocked. |
|
|
212
|
-
| `mu task open <id>` | Reopens a task to `OPEN/todo`. |
|
|
213
|
-
| `mu task park <id> --why <text>` | `OPEN` → `OPEN/parked`. Leaves ready/next; stays in goals. |
|
|
214
|
-
| `mu task unpark <id>` | `OPEN/parked` → `OPEN/todo`. |
|
|
215
|
-
| `mu task claim <task> [--for <agent>] [--force]` | Atomic: sets `owner`, flips status to `IN_PROGRESS/active`. Refuses a parked task unless `--force`. |
|
|
216
|
-
| `mu task release <task>` | Clears `owner`. Auto-flips `IN_PROGRESS` → `OPEN` (so the task re-enters the ready set); `CLOSED` is preserved. `--reopen` forces `OPEN` from `CLOSED`. |
|
|
217
|
-
| `mu task note <task> "..."` | Appends to `task_notes`. Never edits prior notes. |
|
|
218
|
-
| `mu task notes <task> [--tail N \| --since <iso> \| --since-claim]` | List notes (oldest first). `--tail N` (alias `--last N`) prints last N; `--since <iso>` filters by `created_at`; `--since-claim` auto-resolves to the most recent `task claim` event timestamp. `--since` and `--since-claim` are mutually exclusive. |
|
|
219
|
-
|
|
220
|
-
---
|
|
221
|
-
|
|
222
|
-
## Mode of address — who is "you" in each surface?
|
|
223
|
-
|
|
224
|
-
When the docs/code say "you", it must be unambiguous which actor.
|
|
225
|
-
|
|
226
|
-
| Surface | "you" means |
|
|
227
|
-
| -------------------- | ---------------------------------------------------- |
|
|
228
|
-
| README.md | The human user installing/running mu |
|
|
229
|
-
| VISION.md | The human user |
|
|
230
|
-
| ARCHITECTURE.md | A developer working on mu's source |
|
|
231
|
-
| AGENTS.md (root) | An AI coding agent working on this repo |
|
|
232
|
-
| ROADMAP.md | A developer implementing one of the listed items |
|
|
233
|
-
| **SKILL.md** | **The LLM running inside an agent's pane** |
|
|
234
|
-
| Agent prompt bodies | The LLM running as that specific agent |
|
|
235
|
-
| `mu doctor` output | The human user running the diagnostic |
|
|
236
|
-
| Error messages | The caller (CLI user, script, or pi tool invocation) |
|
|
237
|
-
|
|
238
|
-
Avoid second-person across these surfaces unless the audience is
|
|
239
|
-
unambiguous.
|
|
240
|
-
|
|
241
|
-
---
|
|
242
|
-
|
|
243
|
-
## Reserved / avoided terms
|
|
244
|
-
|
|
245
|
-
These words show up in adjacent ecosystems and would confuse mu users.
|
|
246
|
-
Don't use them in mu code or docs:
|
|
247
|
-
|
|
248
|
-
| Avoided word | Why | Use instead |
|
|
249
|
-
| ---------------- | ---------------------------------------------------------------- | ---------------------------------------------------- |
|
|
250
|
-
| "subagent" | In other tools it means a hidden, opaque child agent; mu has none | "agent" (mu's unit), "delegate" (one-task scratch agent), or "hidden subagent" when contrasting |
|
|
251
|
-
| "session" | Pi has its own "session"; tmux has "session"; herdr has a third (server-level); ambiguous alone | "workstream" (mu's unit), "mux session" (cross-backend), or "tmux session" (literal) |
|
|
252
|
-
| "workspace" (mux sense) | mu already uses "workspace" for a VCS-isolated checkout. herdr's session-level container is also called a workspace, and letting both senses in would make `mu workspace list` ambiguous | "mux session"; say "herdr workspace" only when describing herdr's own CLI |
|
|
253
|
-
| "project" | Means a `.pi/` project root; conflict with mu's organizational unit | "workstream" |
|
|
254
|
-
| "context" | Overloaded (LLM context, project context, fork context) | Be specific: "task context", "forked context", etc. |
|
|
255
|
-
| "tab" | Tmux has windows, not tabs (herdr does call them tabs). Pi-subagents and dg use "tab" as a frontmatter field; we keep that field for compatibility but use "window" everywhere else | "window" (in prose); only `tab:` in frontmatter |
|
|
256
|
-
| "thread" | OS threads + chat threads + git threads; bad word | Be specific |
|
|
257
|
-
| "message" | Overloaded (LLM message, log message, send-keys input) | "log entry" (for the **ops log**), "send" (for input to a pane) |
|
|
258
|
-
| "config" | Already means the global mu config; don't reuse | Specific: "settings", "frontmatter", "options" |
|
|
259
|
-
| "manager" | Vague; everything could be a manager | The specific noun (e.g., "the registry", "the eval engine") |
|
|
260
|
-
| "service" | Implies long-running daemon; mu has none | Be specific |
|
|
261
|
-
| "plugin" | Pi has extensions, not plugins | "extension" |
|
|
262
|
-
| "instance" | Vague; could be agent / workstream / process | The specific thing |
|
|
263
|
-
| "broker" | Implies pub-sub middleware; we don't have one | "log entry" or be specific |
|
|
264
|
-
| "checkpoint" | Implies recoverable savepoints in the work | "group" (undo unit) |
|
|
265
|
-
| "snapshot" | mu has no whole-DB savepoints | "backup" for a whole-DB copy |
|
|
266
|
-
| "agent type" | "Type" implies a class hierarchy; mu has no class system | "agent role" (scout/reviewer/etc.) |
|
|
267
|
-
| "agent definition" / "agent template" / "agent role doc" | mu has no template/definition concept. Spawn flags + the orchestrator's prompt are the only "definition" | Just describe the spawn invocation directly |
|
|
268
|
-
| "worker" | "worker" is the name of one specific built-in agent | "agent" (general); "the worker" only when referring to that specific agent |
|
|
269
|
-
| "claimer" | Awkward; we have "owner" already | "owner" |
|
|
270
|
-
|
|
271
|
-
---
|
|
272
|
-
|
|
273
|
-
## Operations reference
|
|
274
|
-
|
|
275
|
-
The complete verb list lives in two places, both authoritative:
|
|
276
|
-
|
|
277
|
-
- **`mu --help`** and **`mu <verb> --help`** — the canonical CLI
|
|
278
|
-
reference. If anything below disagrees with `--help`, trust
|
|
279
|
-
`--help`.
|
|
280
|
-
- **[skills/mu/SKILL.md](../skills/mu/SKILL.md)** — the LLM-facing
|
|
281
|
-
one-pager: `## CLI overview` covers the gotchas per namespace.
|
|
282
|
-
|
|
283
|
-
For worked examples, see [USAGE_GUIDE.md](USAGE_GUIDE.md). Rows below
|
|
284
|
-
exist to keep names canonical, not to replace `--help`.
|
|
285
|
-
|
|
286
|
-
| Operation | Canonical meaning |
|
|
287
|
-
| --------- | ----------------- |
|
|
288
|
-
| `mu sync` | Report **peer** status (**watermark** + staleness). **Flush** and **ingest** happen on this and every other mu invocation when `MU_SYNC_DIR` is set, so the bare form is a status card whose sync is incidental — it exists mainly so `rsync ... && mu sync` reads correctly. |
|
|
289
|
-
| `mu sync --from <path>` | Ingest from a peer's `mu.db` directly (a different reader: SQLite `ops` table rather than a JSONL **segment**). For an sshfs mount or a copied file. |
|
|
290
|
-
| `mu sync --repair <peer>` | Reset a **watermark** to re-read that peer's **segment** from zero. The universal repair, safe because ingest is idempotent via `UNIQUE (machine_id, hlc)`. Takes a `machine_id` or any unique prefix. |
|
|
291
|
-
| `mu undo <group>` | Emit inverse ops for one **group**. Granular (touches only that action's rows), composable, and itself an op — so it syncs and is itself undoable. |
|
|
292
|
-
| `mu rebuild <file>` | Materialize a fresh DB by replaying the **ops log** in HLC order. The disaster-recovery path; prints the swap command rather than overwriting in place. Replays EVERYTHING the log knows, including machine-local ops — a rebuild is local recovery, not **ingest**, so it does not filter to synced entities. Carries `machine_identity` (and its HLC clock) across, so the rebuilt DB is the SAME peer. `agents` / `vcs_workspaces` are NOT rebuilt — they have no capture triggers so leave no ops, and `pane_id` / absolute paths would be lies after recovery; the summary reports the loss and says to re-spawn. |
|
|
293
|
-
| `mu link pi` | Install mu into pi: the **extension shim** (`~/.pi/agent/extensions/mu.ts`, a re-export of the installed `dist/extension/mu-pi.js`, marked `// mu:shim`) and a symlink `~/.agents/skills/mu` → the package's `skills/mu`. `--copy` inlines the extension (pinned). Never replaces a real directory; replaces a foreign symlink only with `--force` (`LinkConflictError`, exit 4). Root overridable via `MU_PI_HOME`. |
|
|
294
|
-
| `mu db backup <file>` | `VACUUM INTO` copy of the whole DB. The "one file I can scp" convenience; real DR is **segments** + `mu rebuild`. |
|
|
295
|
-
|
|
296
|
-
A one-off directory needs no flag — `MU_SYNC_DIR=/mnt/usb mu state`
|
|
297
|
-
ingests from a USB stick using the existing env-var idiom.
|
|
298
|
-
|
|
299
|
-
---
|
|
300
|
-
|
|
301
|
-
## Naming conventions
|
|
181
|
+
| `unknown` | nf-fa-question_circle | No usable reading; a reason accompanies it |
|
|
302
182
|
|
|
303
|
-
|
|
183
|
+
The extension's `busy` maps to `busy` and `idle` to `needs_input`. Murmur
|
|
184
|
+
`working` maps to `busy`, `blocked` to `needs_permission`, and `idle`,
|
|
185
|
+
`done`, and `crashed` to `needs_input`. The `agents.status` column is
|
|
186
|
+
deprecated, always `spawning`, and never read.
|
|
304
187
|
|
|
305
|
-
|
|
306
|
-
> else — scoping, modifiers, payload — is a flag.**
|
|
188
|
+
## Reserved and avoided terms
|
|
307
189
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
|
331
|
-
|
|
|
332
|
-
|
|
|
333
|
-
|
|
|
334
|
-
|
|
|
335
|
-
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
operator actually typed.
|
|
341
|
-
|
|
342
|
-
A blank single-value `-w ' '` is rejected in `resolveWorkstream` for
|
|
343
|
-
the same reason; otherwise it resolves to a workstream literally
|
|
344
|
-
named `" "`.
|
|
345
|
-
|
|
346
|
-
Whether zero surviving fragments is legal is then a per-verb
|
|
347
|
-
question, decided *after* this rule: `--by ''` needs at least one
|
|
348
|
-
blocker (exit 2), while `reparent --blocked-by ''` means "clear
|
|
349
|
-
them all".
|
|
350
|
-
|
|
351
|
-
Corollary for multi-value flags: the same CONCEPT gets the same
|
|
352
|
-
parsing in every verb that names it. `--blocked-by` and `--by` are
|
|
353
|
-
both blocker lists, so both accept repeat / comma / mixed form via
|
|
354
|
-
the one canonical `parseCsvFlag` helper.
|
|
355
|
-
|
|
356
|
-
### IDs
|
|
357
|
-
|
|
358
|
-
- **Agent name**: lowercase, `[a-z][a-z0-9_-]*`, ≤32 chars. Used as
|
|
359
|
-
tmux window name verbatim. Unique within a workstream.
|
|
360
|
-
- **Task local_id**: same shape and rules. Unique within the DB.
|
|
361
|
-
- **Workstream name**: same shape; tmux session is `mu-<name>`.
|
|
362
|
-
The name `scratch` is **reserved**: it can only be auto-created on
|
|
363
|
-
first spawn (`mu agent spawn <name> -w scratch`); `mu workstream
|
|
364
|
-
init scratch` is rejected loud. The `mu-` prefix is likewise
|
|
365
|
-
reserved (no double-prefix).
|
|
366
|
-
- **Tab name (frontmatter `tab:`)**: human-friendly,
|
|
367
|
-
`[A-Za-z][A-Za-z0-9 _-]*`, ≤32 chars. Used as tmux window name when
|
|
368
|
-
multiple agents share the window.
|
|
369
|
-
|
|
370
|
-
### Agent names: prefer `<role>-<n>`, not human names
|
|
371
|
-
|
|
372
|
-
Agents are workers with **roles**, not people. Pick names that
|
|
373
|
-
describe the role, with a numeric suffix when there are multiples:
|
|
374
|
-
|
|
375
|
-
Good: `worker-1`, `worker-2`, `reviewer-1`, `scout-1`, `auditor-1`,
|
|
376
|
-
`oracle-1`, `planner-1`
|
|
377
|
-
|
|
378
|
-
Avoid: `alice`, `bob`, `carol`, `revv`, `mallory`, `peon`, ...
|
|
379
|
-
|
|
380
|
-
Anthropomorphic (or status-loaded) names confuse the model when
|
|
381
|
-
reading commands ("alice claims design" sounds like a person;
|
|
382
|
-
"worker-1 claims design" is obviously a generic worker taking a
|
|
383
|
-
task). Role-based names also make `mu agent list` and tmux's window
|
|
384
|
-
list legible at a glance.
|
|
385
|
-
|
|
386
|
-
Common roles:
|
|
387
|
-
|
|
388
|
-
`worker` long-lived implementer; the default
|
|
389
|
-
`reviewer` reads diffs/code; usually `--role read-only`
|
|
390
|
-
`scout` fast recon; one-shot, returns context
|
|
391
|
-
`oracle` second opinion before action
|
|
392
|
-
`auditor` long-lived watcher; `--role read-only`
|
|
393
|
-
`planner` designs implementation plans
|
|
394
|
-
|
|
395
|
-
If you have multiple agents in the same role, suffix with `-1`,
|
|
396
|
-
`-2`, ... (`worker-1`, `worker-2`).
|
|
397
|
-
|
|
398
|
-
This is a convention, not enforcement: mu's regex accepts any
|
|
399
|
-
`[a-z][a-z0-9_-]{0,31}` string. Test fixtures may use `alice`/`bob`;
|
|
400
|
-
don't propagate that to user-facing examples or real workstreams.
|
|
401
|
-
|
|
402
|
-
### File paths
|
|
403
|
-
|
|
404
|
-
XDG-Base-Directory-Spec compliant. The state directory resolves as:
|
|
405
|
-
|
|
406
|
-
`MU_STATE_DIR` > `$XDG_STATE_HOME/mu` > `~/.local/state/mu`
|
|
407
|
-
|
|
408
|
-
- `<state-dir>/mu.db` — the canonical SQLite database (shared across
|
|
409
|
-
all workstreams; partitioned by `workstream` columns)
|
|
410
|
-
- `<state-dir>/workstreams/<workstream>/` — per-workstream artifact
|
|
411
|
-
directory (created lazily); reserved for tracing logs / forensic
|
|
412
|
-
pane captures.
|
|
413
|
-
- `<state-dir>/workspaces/<workstream>/<agent>/` — per-agent VCS
|
|
414
|
-
workspace (created by `mu agent spawn --workspace` — there is no
|
|
415
|
-
standalone create verb). Orphan dirs (no row in `vcs_workspaces`)
|
|
416
|
-
surfaced by `mu workspace orphans -w <workstream>` and
|
|
417
|
-
`mu state -w <workstream>`.
|
|
418
|
-
- `<sync-dir>/<machine_id>.jsonl` — one **segment** per machine,
|
|
419
|
-
where `<sync-dir>` is `MU_SYNC_DIR` (unset = sync off, zero cost).
|
|
420
|
-
**Single-writer-per-file**: this machine appends only to its own
|
|
421
|
-
and read-onlys every other, so no file is ever contended and any
|
|
422
|
-
file-mover works as transport. Derived from the `ops` table and
|
|
423
|
-
therefore regenerable — no fsync needed, and a torn transfer costs
|
|
424
|
-
one skipped line rather than the file.
|
|
425
|
-
- `<sync-dir>/<machine_id>.manifest` — `{count, last_hlc, sha256}`
|
|
426
|
-
for whole-segment verification.
|
|
427
|
-
- **Never** place `MU_DB_PATH` inside `MU_SYNC_DIR`. Syncing a live
|
|
428
|
-
SQLite file (and its `-wal`/`-shm` sidecars) corrupts it. `mu doctor`
|
|
429
|
-
checks for this.
|
|
430
|
-
- mu does NOT consult any agent-template directory (such as
|
|
431
|
-
`~/.pi/agent/agents/` or `.pi/agents/`). Roles are names and
|
|
432
|
-
briefs, never templates.
|
|
433
|
-
|
|
434
|
-
### Env vars (mu state location)
|
|
435
|
-
|
|
436
|
-
| Name | Effect | Precedence |
|
|
437
|
-
| ----------------- | ------------------------------------------------------------ | ---------- |
|
|
438
|
-
| `MU_DB_PATH` | Override the SQLite file path directly | wins over all |
|
|
439
|
-
| `MU_STATE_DIR` | Override the state directory | beats `XDG_STATE_HOME` |
|
|
440
|
-
| `XDG_STATE_HOME` | Standard XDG base-directory state path; `mu/` appended | default fallback chain |
|
|
441
|
-
| `MU_SESSION` | Override active workstream name (when not auto-detectable) | n/a |
|
|
442
|
-
| `MU_MUX` | Force the **mux backend**, bypassing **mux detection**. Load-bearing: every verb reaches its multiplexer through `activeMux()`, so an unknown value fails the invocation rather than silently running on tmux. | wins over all detection |
|
|
443
|
-
| `MU_SYNC_DIR` | Shared folder holding one **segment** per machine. Unset = sync off, zero cost. The WHOLE cluster configuration; there is no peer list. | n/a |
|
|
444
|
-
| `MU_DELEGATE` | `0` hides the `mu_delegate` tool in the mu pi extension (kill switch for benchmarks or a model it confuses). The `mu doctor` extension row says so. | n/a |
|
|
445
|
-
|
|
446
|
-
### Env vars passed to spawned children
|
|
447
|
-
|
|
448
|
-
| Name | Value |
|
|
449
|
-
| ---------------------------- | ---------------------------------------------------- |
|
|
450
|
-
| `MU_SESSION_ID` | Workstream identifier |
|
|
451
|
-
| `MU_AGENT_NAME` | This agent's name |
|
|
452
|
-
| `MU_PARENT_PANE` | **Pane id** of the spawning process (backend-specific shape) |
|
|
453
|
-
| `MU_DB_PATH` / `MU_STATE_DIR` | Inherited from parent unless overridden |
|
|
454
|
-
| `XDG_STATE_HOME` | Inherited; mu uses `<XDG_STATE_HOME>/mu` by default |
|
|
455
|
-
| `MU_SEND_DELAY_MS` | Delay between bracketed paste and Enter (default `500`) |
|
|
456
|
-
| `MU_SEND_READINESS_MS` | Budget for `mu agent send` to wait out a pane's modal / re-init before pasting, and to re-confirm the Enter landed (default `15000`; `0` restores fire-and-forget). A busy-but-working pane is not waited on. |
|
|
457
|
-
| `MU_TMUX_SOCKET` | Override tmux socket (`-L <name>`); default uses `$TMUX`. tmux **mux backend** only — ignored under herdr, which reaches its server over a unix socket it manages itself. |
|
|
458
|
-
| `MU_HERDR_SESSION` | Drive a NAMED herdr server (`--session <name>`, its own socket) instead of the user's default one. The herdr analogue of `MU_TMUX_SOCKET`, and the isolation seam any herdr integration test must use so the suite can never observe — or destroy — real panes. herdr **mux backend** only. |
|
|
459
|
-
| `MU_<UPPER_CLI>_COMMAND` | Override the executable launched for `--cli <cli>` (e.g. `MU_PI_COMMAND=pi-alt` makes `--cli pi` exec `pi-alt`; hyphens in the cli key become underscores in the env var name, so `--cli pi-meta` reads `MU_PI_META_COMMAND`). Accepts multi-word strings (`MU_PI_COMMAND="pi-alt --some-flag"`); tmux exec's via a shell. Reconcile also treats the resolved binary as agent-worthy when surfacing orphan panes. When this env var supplies the override (and `--command` did not), the spawn-success line surfaces the env-var name (`Spawned worker-1 (pi-meta via $MU_PI_META_COMMAND)`) so stale aliases are visible without `mu agent show`. |
|
|
460
|
-
| `MU_SPAWN_LIVENESS_MS` | **tmux-tier knob** (herdr's `agent start` blocks until the agent is ready, so there is nothing to poll for). After spawn, wait this many ms then verify the pane is still alive AND scan the tail of its scrollback for known startup-error patterns (provider auth failures — `No API key found for X`, `401 Unauthorized`, … — plus shell-level `command not found` / `No such file or directory` when the spawned binary vanished post-pre-flight). Default 1500. Set to 0 to disable (useful in CI). On detected death the DB row is rolled back and `AgentDiedOnSpawnError` is thrown with the captured scrollback; on a startup-error match (pane alive but parked at an error prompt) the row is rolled back and `AgentSpawnStartupError` is thrown with the matched line + remediation hints. The complementary pre-flight check (PATH lookup of `--cli`'s resolved binary BEFORE any side effect) is not env-tunable; on miss it throws `AgentSpawnCliNotFoundError` with no orphan workspace / pane / row. |
|
|
461
|
-
| `MU_CTL_SOCK` | Set by spawn in every agent pane: the agent's control socket path, derived from (state dir, workstream, agent). The mu pi extension serves it; mu connects to it. |
|
|
462
|
-
| `MU_SPAWN_CTL_MS` | Budget for spawn's control-socket handshake with a pi agent, polled every 250ms (default `30000`; `0` disables). On timeout spawn still exits 0, reports `ctl: missing\|refused`, and warns on stderr. `mu agent spawn --no-ctl` skips it per spawn. |
|
|
463
|
-
|
|
464
|
-
They use the `MU_` namespace so they never collide with env vars other
|
|
465
|
-
pi extensions set in the same session.
|
|
466
|
-
|
|
467
|
-
---
|
|
468
|
-
|
|
469
|
-
## Type of "session"
|
|
470
|
-
|
|
471
|
-
Because "session" is overloaded, here are the four senses we encounter
|
|
472
|
-
and the disambiguated terms:
|
|
473
|
-
|
|
474
|
-
| Generic word | mu term used in docs/code | What it actually is |
|
|
475
|
-
| ------------ | ------------------------------------- | ------------------------------------------------ |
|
|
476
|
-
| session | **workstream** | mu's unit of organization |
|
|
477
|
-
| session | **mux session** | The backend-agnostic container for a workstream's panes |
|
|
478
|
-
| session | **tmux session** | The tmux process group `mu-<workstream>` |
|
|
479
|
-
| session | **herdr session** | herdr's *server*-level unit (one socket). NOT a **mux session** — mu maps a workstream to a herdr *workspace*, one level down. |
|
|
480
|
-
| session | **pi session** | The thing pi calls a session (its conversation) |
|
|
481
|
-
| session | **agent session** (avoid in code) | Colloquial for "an agent's run/lifetime"; prefer "lifetime" or "the work alice has done" |
|
|
482
|
-
|
|
483
|
-
When writing code, say `workstream_id` not `session_id` in any new
|
|
484
|
-
column or variable name. The `agents.session_id` column keeps its
|
|
485
|
-
name for schema stability; document it as "workstream id".
|
|
190
|
+
| Avoid | Why | Use instead |
|
|
191
|
+
| --- | --- | --- |
|
|
192
|
+
| "subagent" | Means a hidden child in other tools; mu has none | "agent", "delegate", or "hidden subagent" when contrasting |
|
|
193
|
+
| "session" | pi, tmux, and herdr each have one | "workstream", "mux session", "tmux session" |
|
|
194
|
+
| "workspace" (mux sense) | Collides with the VCS workspace | "mux session"; "herdr workspace" only about herdr's CLI |
|
|
195
|
+
| "project" | Means a `.pi/` project root | "workstream" |
|
|
196
|
+
| "context" | LLM, project, and fork context | Be specific: "task context" |
|
|
197
|
+
| "tab" | tmux has windows (herdr calls them tabs) | "window"; `--tab` is the flag |
|
|
198
|
+
| "thread" | OS, chat, and git threads | Be specific |
|
|
199
|
+
| "message" | LLM message, log message, pane input | "log entry", "send" |
|
|
200
|
+
| "config" | mu has no config file | "settings", "options" |
|
|
201
|
+
| "manager", "instance" | Vague | The specific noun |
|
|
202
|
+
| "service", "broker" | Imply a daemon or middleware; mu has none | Be specific |
|
|
203
|
+
| "plugin" | pi has extensions | "extension" |
|
|
204
|
+
| "checkpoint", "snapshot" | mu has no savepoints | "group" (undo unit), "backup" (whole-DB copy) |
|
|
205
|
+
| "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 |
|
|
206
|
+
| "worker" (general) | Names the task-claimer role | "agent" |
|
|
207
|
+
| "claimer" | | "owner" |
|
|
208
|
+
|
|
209
|
+
## Senses of "session"
|
|
210
|
+
|
|
211
|
+
| mu term | What it is |
|
|
212
|
+
| --- | --- |
|
|
213
|
+
| **workstream** | mu's unit of organization |
|
|
214
|
+
| **mux session** | The backend-agnostic container for a workstream's panes |
|
|
215
|
+
| **tmux session** | The tmux session `mu-<workstream>` |
|
|
216
|
+
| **herdr session** | herdr's server-level unit (one socket). Not a mux session: a workstream maps to a herdr workspace, one level down |
|
|
217
|
+
| **pi session** | pi's conversation |
|
|
218
|
+
| **agent session** | Colloquial for an agent's lifetime; avoid in code |
|
|
219
|
+
|
|
220
|
+
New columns and variables say `workstream_id`, not `session_id`. The
|
|
221
|
+
`agents.session_id` column keeps its name for schema stability.
|