@mu-crew/mu 3.6.2 → 3.8.0

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.
Files changed (38) hide show
  1. package/dist/cli.js +12652 -12264
  2. package/dist/cli.js.map +1 -1
  3. package/dist/extension/mu-pi.js +302 -48
  4. package/dist/extension/mu-pi.js.map +1 -1
  5. package/dist/index.d.ts +59 -13
  6. package/dist/index.js +1376 -1205
  7. package/dist/index.js.map +1 -1
  8. package/docs/ARCHITECTURE.md +4 -3
  9. package/docs/architecture/control-socket.md +10 -5
  10. package/docs/architecture/mux.md +4 -1
  11. package/docs/guide/delegate.md +25 -3
  12. package/docs/guide/dispatch.md +22 -5
  13. package/docs/guide/getting-started.md +2 -2
  14. package/docs/guide/recovery.md +11 -6
  15. package/docs/guide/upgrade.md +4 -4
  16. package/docs/reference/env.md +2 -2
  17. package/docs/specs/2026-10-03-keep-driving-nudge.md +21 -0
  18. package/package.json +1 -1
  19. package/skills/mu/SKILL.md +40 -16
  20. package/skills/mu/recipes/adversarial-review.md +14 -6
  21. package/skills/mu/recipes/brief.md +29 -1
  22. package/skills/mu/recipes/codemode-driver.md +19 -9
  23. package/skills/mu/recipes/deep-research.md +6 -5
  24. package/skills/mu/recipes/drift-audit.md +72 -0
  25. package/skills/mu/recipes/findings.md +24 -15
  26. package/skills/mu/recipes/hypothesis-panel.md +5 -3
  27. package/skills/mu/recipes/long-run.md +13 -3
  28. package/skills/mu/recipes/loop-until-done.md +4 -3
  29. package/skills/mu/recipes/orchestrator-loop.md +29 -16
  30. package/skills/mu/recipes/plan.md +10 -6
  31. package/skills/mu/recipes/refute.md +3 -2
  32. package/skills/mu/recipes/remote-workers.md +23 -6
  33. package/skills/mu/recipes/review-panel.md +2 -2
  34. package/skills/mu/recipes/rules-audit.md +10 -5
  35. package/skills/mu/recipes/tasks-or-calls.md +40 -23
  36. package/skills/mu/recipes/tournament.md +4 -3
  37. package/skills/mu/recipes/ultrathink.md +2 -2
  38. package/skills/mu/recipes/worker.md +3 -2
@@ -120,7 +120,7 @@ hub that re-exports them. `src/tasks.ts`, `src/agents.ts`, `src/mux.ts`,
120
120
  | `src/agents/spawn.ts` | spawn: CLI resolution, pane create or reuse, liveness, readiness, ctl handshake, rollback |
121
121
  | `src/agents/spawn-lock.ts` | per-session lock around topology and row insert |
122
122
  | `src/agents/transport.ts` | send routing: ctl for pi agents, mux paste otherwise, no fallback; `expectsCtl` |
123
- | `src/agents/abort.ts` | stop a pi turn through the control socket |
123
+ | `src/agents/abort.ts` | stop a pi turn through the control socket; abort + send (`--interrupt`) |
124
124
  | `src/agents/delegate.ts` | `delegateOutcome`: one wait result to `done` / `empty` / `died` / `timeout` / `pending` |
125
125
  | `src/agents/wait.ts` | block until task-less agents leave `busy` |
126
126
  | `src/agents/adopt.ts` | register an existing pane as an agent |
@@ -139,7 +139,7 @@ hub that re-exports them. `src/tasks.ts`, `src/agents.ts`, `src/mux.ts`,
139
139
  | `src/ctl/client.ts` | `ctlRequest`, `ctlProbe`, version and unknown-op errors |
140
140
  | `extension/mu-pi.ts` | pi extension, child side: serves `$MU_CTL_SOCK`; built to `dist/extension/mu-pi.js` |
141
141
  | `extension/delegate.ts` | pi extension, parent side: `mu_delegate` / `mu_delegate_cancel`, which shell out to `mu` |
142
- | `extension/nudge.ts` | pi extension: the keep-driving nudge. Arms on a `mu` dispatch, checks `mu state --json` at `agent_before_settle`, injects the SKILL.md rule once per prompt, logs `--kind nudge` |
142
+ | `extension/nudge.ts` | pi extension: the keep-driving nudge. Arms on a `mu` dispatch, checks `mu state --json` at `agent_before_settle`, injects the SKILL.md rule once per prompt, logs `--kind nudge`. Also the close nudge (worker settles owning a task) and the refute nudge (`task claim --for` on a task with no refute decision in `mu task notes --json`; one notice, no continuation) |
143
143
  | `src/link.ts` | `mu link pi`: extension shim, skill symlink, `inspectLinks` for doctor |
144
144
 
145
145
  ### Tasks, workspaces and workstreams
@@ -168,7 +168,8 @@ hub that re-exports them. `src/tasks.ts`, `src/agents.ts`, `src/mux.ts`,
168
168
  | `src/cli.ts` | Commander wiring (`buildProgram`) |
169
169
  | `src/cli/*.ts` | one file per verb namespace, thin wrappers over the SDK; `format.ts` renders tables, `handle.ts` maps errors to exit codes and runs ambient sync |
170
170
  | `src/cli/tasks/*.ts` | the `mu task` namespace, including `mu me tasks` / `mu me next` |
171
- | `src/cli/dispatch-hints.ts` | dispatch-time `Next:` hints for pi agents (`--fresh`, `--steer`, abort) |
171
+ | `src/cli/dispatch-hints.ts` | dispatch-time `Next:` hints for pi agents (`--fresh`, `--interrupt`, abort) |
172
+ | `src/cli/stdin.ts` | `-` as a text argument: reads note / send text from stdin (heredoc prose) |
172
173
  | `src/cli/agents-remote.ts` | `mu agent remote-env`: prints the ssh forward and env, runs nothing |
173
174
  | `src/cli/tui-launch-focus.ts` | initial-tab focus ladder for bare `mu` and `mu state --tui` |
174
175
  | `src/cli/tui/` | the ink TUI; the only place ink and react are imported |
@@ -28,10 +28,11 @@ reply carries `v: 1`; the client (`src/ctl/client.ts`) throws
28
28
  | op | does |
29
29
  | --- | --- |
30
30
  | `hello` | identity plus `ops` (served ops) and `extVersion` (the mu version the extension was built from) |
31
- | `status` | `state` (`busy` / `idle` / `needs_input`), `since`, `runs`, `pending` |
32
- | `send` | `pi.sendUserMessage`; when busy, `mode` is `steer` or `followUp` (default) |
31
+ | `status` | `state` (`busy` / `idle`), `since`, `runs`, `pending` |
32
+ | `send` | `pi.sendUserMessage`; when busy, `mode` is `steer` or `followUp` (default). Replies with the `status` fields measured before the dispatch, so `runs` is the baseline for a later `wait` |
33
33
  | `wait` | resolves on pi's `agent_settled` after `afterRuns`, with the run's `lastText` |
34
34
  | `abort` | `ctx.abort()` (pi's Esc) |
35
+ | `interrupt` | busy: `ctx.abort()`, wait for `agent_settled` (up to `timeoutMs`), then `pi.sendUserMessage(text)` as a new run; idle: just the send. Replies `wasBusy`, `pending` (measured before the abort) and the `status` fields measured after the settle and before the send, so `runs` is the `wait` baseline for the new run. `timeout`: nothing sent |
35
36
  | `fresh` | new session plus prompt as one operation, through the internal `/mu-fresh` command; replies once the new run starts; refused with `busy` unless `force` |
36
37
  | `command` | `name` `new`, `reload` or `compact` (with optional `instructions`), through the internal `/mu-new`, `/mu-reload`, `/mu-compact` commands, which call `ctx.newSession()`, `ctx.reload()`, `ctx.compact()`. `new` replies once the session is replaced, `reload` after the `session_start` it causes, `compact` when compaction starts or with pi's error (`Nothing to compact ...`). Refused with `busy` unless `force` |
37
38
 
@@ -41,13 +42,14 @@ extension command, triggered with `expandPromptTemplates: true`, and
41
42
  its handler gets the command context that can replace or reload the
42
43
  session.
43
44
 
44
- These back `mu agent send` (`--fresh`, `--steer`, session commands), `mu agent wait`,
45
+ These back `mu agent send` (`--fresh`, `--steer`, `--interrupt`, session commands), `mu agent wait`,
45
46
  `mu agent abort`, the agent state reading, and `mu_delegate`.
46
47
 
47
48
  ## Version skew
48
49
 
49
50
  An op the extension does not serve returns `unknown op: <op>` with its
50
- `ops` list. mu raises `AgentExtensionOutdatedError` (exit 4), whose
51
+ `ops` list. `mu agent send --interrupt` then composes `abort`, `wait`
52
+ and `send` itself. Any other op raises `AgentExtensionOutdatedError` (exit 4), whose
51
53
  next steps are `/reload` through the mux (an extension that lacks the
52
54
  `command` op cannot run it over ctl) or a respawn. `mu doctor`
53
55
  probes every pi agent's socket (the `ctl` row) and flags an
@@ -68,7 +70,10 @@ row reports whether `mu link pi` installed the extension.
68
70
 
69
71
  1. If the path already answers, another pi serves it (a nested `pi -p`
70
72
  inherited `MU_CTL_SOCK`). The extension leaves it alone and says so
71
- on stderr.
73
+ on stderr. Identity follows the bind: that pi, and one that loses
74
+ the `link()` race, is nested and gets no keep-driving, close or
75
+ refute nudge, though it inherited the agent's `MU_AGENT_NAME`.
76
+ The decision lives in the process-global map, so `/reload` keeps it.
72
77
  2. Otherwise it listens on a private name in the same directory, sets
73
78
  mode 0600, and hard-links that name onto the public path. libuv
74
79
  unlinks the listened-on name at close, so the public path must never
@@ -104,7 +104,10 @@ as a capability, not a name check:
104
104
  - **tmux.** After `MU_SPAWN_LIVENESS_MS` (default 1500, `0` skips) mu
105
105
  verifies the pane survived and scans its tail for provider or auth
106
106
  startup errors. mu does not wait for murmur to claim the pane: state
107
- reads before the first claim report `unknown`.
107
+ reads before the first claim report `unknown`. A pi agent skips the
108
+ fixed wait: mu checks the pane on every handshake tick (a dead pane
109
+ rolls back at once) and scans for startup errors when the handshake
110
+ ends.
108
111
  - **herdr.** mu creates a bare pane and calls `startAgentInPane`, which
109
112
  subsumes both checks.
110
113
  - **pi, either backend.** After the agent starts, mu polls its control
@@ -23,6 +23,7 @@ Delegates called in one turn run in parallel.
23
23
  | `workspace` | Own VCS checkout, for delegates that edit files. The follow-up names its path. It isolates repository edits only. |
24
24
  | `cli` | Key for `$MU_<CLI>_COMMAND` (default `pi`), e.g. a cheaper model. |
25
25
  | `keep` | Keep the pane after it finishes, to talk to it again. |
26
+ | `record` | `{ task, workstream? }` (or `task: "<ws>/<task>"`): write the answer's verdict onto that task as a note. See [Record a verdict](#record-a-verdict). |
26
27
 
27
28
  - Watch or steer: run the attach command.
28
29
  - Stop: `mu_delegate_cancel`, or `mu agent abort <name> -w scratch`.
@@ -45,21 +46,42 @@ Delegates called in one turn run in parallel.
45
46
 
46
47
  `MU_DELEGATE=0` hides the tool ([env vars](../reference/env.md)).
47
48
 
49
+ ### Record a verdict
50
+
51
+ For a refuter or check, pass `record` so the verdict lands on the task
52
+ it judged. The task must exist (`mu task show`), else the call fails
53
+ and nothing spawns. When the answer arrives, the tool runs
54
+ `mu task note <task> -w <ws> --author <delegate>` once:
55
+
56
+ - Done: `REFUTER <label> (<delegate>, <elapsed>):`, then the last
57
+ `VERDICT:` line and every `EVIDENCE:` line after it. Without a
58
+ VERDICT line, the answer's last 1500 chars after `NO VERDICT LINE:`.
59
+ Capped at 4000 chars.
60
+ - Timeout, died, empty, error, or cancelled:
61
+ `REFUTER <label>: no verdict (<outcome>)`.
62
+
63
+ The follow-up still carries the full answer and ends with
64
+ `Recorded on <ws>/<task> as a note.`, or the reason recording failed.
65
+
48
66
  ## Delegate without pi
49
67
 
50
68
  From a script or other shell, run what the tool runs:
51
69
 
52
70
  ```bash
53
71
  mu agent spawn helper-1 -w scratch
54
- mu agent send helper-1 -w scratch --fresh 'Investigate why foo.spec.ts fails. Report the cause.'
55
- mu agent wait helper-1 -w scratch --json
72
+ runs=$(mu agent send helper-1 -w scratch --fresh --json 'Investigate why foo.spec.ts fails. Report the cause.' | jq .runs)
73
+ mu agent wait helper-1 -w scratch --after-runs "$runs" --json
56
74
  ```
57
75
 
76
+ `--after-runs` waits for the first run past the count the send
77
+ reported, so a run that settles before the wait starts still counts.
78
+
58
79
  Use `--fresh`: a plain send to a reused agent carries the previous
59
80
  task's context.
60
81
 
61
82
  `mu agent wait` fires when the agent leaves busy (including to
62
- `needs_input`), never on an already idle agent. For pi, the `--json`
83
+ `needs_input`), never on an already idle agent unless it finished a run
84
+ past `--after-runs`. For pi, the `--json`
63
85
  row carries:
64
86
 
65
87
  - `outcome`: `done`, `empty`, `error`, `died`, `timeout`, or `pending`.
@@ -33,7 +33,8 @@ socket. Pick the mode by what the agent is doing:
33
33
  | Situation | Command |
34
34
  | --------- | ------- |
35
35
  | New task, any state | `mu agent send worker-1 --fresh '<brief>'` |
36
- | Busy, and you want to interrupt the current run | `mu agent send worker-1 --steer '<text>'` |
36
+ | Busy, and the current work is wasted (wrong input, changed rule): act now | `mu agent send worker-1 --interrupt '<text>'` |
37
+ | Busy, advice that can wait for the next tool boundary | `mu agent send worker-1 --steer '<text>'` |
37
38
  | Answering a question, or adding to the same task | `mu agent send worker-1 '<text>'` |
38
39
 
39
40
  - `--fresh` starts a new pi session and sends the prompt into it as
@@ -41,7 +42,14 @@ socket. Pick the mode by what the agent is doing:
41
42
  `mu agent abort` first, or pass `--force` to drop the running turn.
42
43
  - Never send `/new` and then the prompt as two sends to pi. The prompt
43
44
  can land during the reset and vanish.
44
- - A plain send to a busy agent queues as a follow-up.
45
+ - A plain send to a busy agent queues as a follow-up: pi sees it after
46
+ the run ends. `--steer` lands between tool calls, after the running
47
+ tool returns; it does not stop a long tool such as a test batch, a
48
+ wait, or a `sleep`.
49
+ - `--interrupt` aborts the run (kills the running tool), waits for pi
50
+ to settle, then sends the text as a new run. Queued messages go back
51
+ to the pane's editor unsent. Exit 5 when pi does not settle within
52
+ `--timeout` (default 30 s); the text is then not sent.
45
53
  - `/new`, `/reload` and `/compact [instructions]` run inside pi through
46
54
  the socket and report pi's answer (for example `Nothing to compact`).
47
55
  They refuse with exit 4 while pi is busy; `--force` overrides. Any
@@ -77,7 +85,9 @@ git commit -am '...' THEN
77
85
  mu task close build -w auth --evidence '...'
78
86
  ```
79
87
 
80
- Single-quote prompts that contain `$` or backticks. To see only what the worker wrote after the claim, run
88
+ Single-quote short prompts. Pass prose (apostrophes, `$`, backticks) as `-` with a
89
+ quoted heredoc: `mu agent send worker-1 - <<'EOF'`; see
90
+ [brief](../../skills/mu/recipes/brief.md#quoting). To see only what the worker wrote after the claim, run
81
91
  `mu task notes build -w auth --since-claim`.
82
92
 
83
93
  ## Give each worker a workspace
@@ -118,8 +128,11 @@ mu task wait auth/build auth/docs --first --on-stall exit --json
118
128
  polling instead.
119
129
 
120
130
  Exit 7 has several causes: the worker finished but did not close, it
121
- asked a question, or it hit a prompt. In each case, run
122
- `mu agent read <owner>` first.
131
+ asked a question, or it hit a prompt. In each case, run the exit's
132
+ `Next:` first. For a pi owner, it starts with
133
+ `mu agent wait <owner> --after-runs <runs-1> --json`, which prints the
134
+ last answer at once. Then it shows `mu agent read <owner>`, for the
135
+ dialogs and crashes that only the pane shows.
123
136
 
124
137
  Pipeline the work: merge one task, run your tests on the merged tree,
125
138
  then dispatch the next task. Waiting for a whole wave hides partial
@@ -136,6 +149,10 @@ mu task release build -w auth
136
149
 
137
150
  - Closed substates other than `done` need `--why`. Every `CLOSED`
138
151
  substate satisfies a blocker.
152
+ - Deciding a finding (`accept`, or `close --as rejected|wontfix|duplicate`)
153
+ with a reason under 40 characters and no `VERDICT:` or `REFUTER` note
154
+ prints a warning. The exit code stays the same. A `REJECTED` or `SUPERSEDED`
155
+ note appends the title of each task id it names.
139
156
  - `--if-ready` closes only when every blocker is closed. Otherwise it
140
157
  lists the open blockers and exits 0.
141
158
  - A parked task leaves the ready set, and its dependents keep waiting.
@@ -126,7 +126,7 @@ The exit code tells you what happened:
126
126
  | `0` | the task closed | merge the work |
127
127
  | `5` | `--timeout` expired (default 600 s) | wait again |
128
128
  | `6` | the worker's pane died and the task went back to `OPEN` | spawn and claim again |
129
- | `7` | the worker sat in `needs_input` for 2 minutes | `mu agent read worker-1 -w auth`, then answer it |
129
+ | `7` | the worker sat in `needs_input` for `--stuck-after` seconds (120, or 5 for a pi worker read over its control socket) | run its `Next:` (for a pi worker, `mu agent wait worker-1 --after-runs <runs-1> --json -w auth` returns the last answer; otherwise `mu agent read worker-1 -w auth`), then answer it |
130
130
 
131
131
  `--on-stall exit` is the default; it is spelled out above so older mu
132
132
  versions behave the same. With `--on-stall warn`, a stalled worker only
@@ -179,7 +179,7 @@ mu has not freed. That protects work you have not merged.
179
179
  ## Next steps
180
180
 
181
181
  - [Dispatch work to a worker](dispatch.md) covers more than one
182
- worker and the `--fresh`, `--steer`, and plain send choices.
182
+ worker and the `--fresh`, `--interrupt`, `--steer`, and plain send choices.
183
183
  - [Use the TUI dashboard](tui.md): run bare `mu` to see every
184
184
  workstream at once.
185
185
  - [Clean up](cleanup.md) when the workstream is finished.
@@ -41,22 +41,27 @@ back to pasting.
41
41
 
42
42
  | State | Means | Fix |
43
43
  | ----- | ----- | --- |
44
- | `ctl missing` | no socket: the extension is not loaded | run `mu link pi`, then respawn the agent |
44
+ | `ctl missing` | no socket: the extension is not loaded, or a new pi waits at its trust prompt | answer the [trust prompt](#a-new-pi-agent-shows-ctl-missing), or run `mu link pi`, then respawn the agent |
45
45
  | `ctl refused` | the socket exists but does not answer | respawn the agent; for a remote agent, check the ssh forward |
46
- | `extension X older than installed Y` | pi loaded an older mu build | type `/reload` in the pane, or respawn |
46
+ | `extension X older than installed Y` or `extension lacks ops` | pi loaded an older mu build | send `/reload`, or respawn |
47
47
 
48
48
  To send `/reload` without attaching:
49
49
 
50
50
  ```bash
51
- mu agent send worker-1 -w auth '/reload' --via mux
51
+ mu agent send worker-1 -w auth '/reload' # extension serves op command
52
+ mu agent send worker-1 -w auth '/reload' --via mux # older: type it into the pane
52
53
  ```
53
54
 
54
- `/reload` keeps the pi session. A `send --fresh` or `abort` against an
55
+ The error names the right form. `/reload` keeps the pi session. A `send --fresh` or `abort` against an
55
56
  outdated extension fails with exit 4 and prints the same two fixes.
56
57
 
57
- ## A new pi agent is stuck at `needs_input`
58
+ ## A new pi agent shows `ctl missing`
58
59
 
59
- pi asks whether to trust a project folder on first start. Run
60
+ pi asks whether to trust a project folder on first start. It asks
61
+ before it loads extensions, so the agent has no control socket yet:
62
+ mu shows `unknown (ctl missing)` and spawn warns after 30 s. A pi
63
+ launched through a wrapper whose name is not `pi` or `pi-meta` shows
64
+ `needs_input` instead. Run
60
65
  `/trust` in the pane once, or spawn with
61
66
  `--command 'pi --approve'`. `--approve` trusts every directory,
62
67
  including workspace forks, so use it only on your own code.
@@ -25,15 +25,15 @@ For agents other than pi, see [README § Install](../../README.md#install).
25
25
  ## Reload running pi agents
26
26
 
27
27
  A running pi keeps the extension it loaded at start. After an upgrade,
28
- type `/reload` in each agent's pane, or respawn the agent. From a
29
- shell:
28
+ send `/reload` to each agent, or respawn the agent. From a shell:
30
29
 
31
30
  ```bash
32
- mu agent send worker-1 -w auth '/reload' --via mux
31
+ mu agent send worker-1 -w auth '/reload' # extension serves op command
32
+ mu agent send worker-1 -w auth '/reload' --via mux # older extension
33
33
  ```
34
34
 
35
35
  Until then, the `ctl` row of `mu doctor` warns
36
- `extension X older than installed Y`. Ops the old extension lacks,
36
+ `extension X older than installed Y` or `extension lacks ops`. Ops the old extension lacks,
37
37
  such as `send --fresh`, fail with exit 4.
38
38
 
39
39
  ## Upgrade from 3.1 or earlier
@@ -29,7 +29,7 @@ milliseconds; `0` disables a wait unless noted.
29
29
  | --- | --- | --- |
30
30
  | `MU_<CLI>_COMMAND` | Executable for `--cli <cli>`; hyphens become underscores (`--cli pi-meta` reads `MU_PI_META_COMMAND`). May hold arguments. `--command` wins; the spawn line names the env var when it supplied the command. | the cli value |
31
31
  | `MU_SPAWN_CTL_MS` | Budget for the control-socket handshake with a new pi agent, polled every 250ms. On timeout spawn still exits 0, reports `ctl: missing` or `ctl: refused`, and warns. `--no-ctl` skips it per spawn. | `30000` |
32
- | `MU_SPAWN_LIVENESS_MS` | tmux only. Wait this long, then check the pane is alive and its scrollback has no startup error (auth failure, `command not found`). Failure rolls back the row and throws `AgentDiedOnSpawnError` or `AgentSpawnStartupError`. | `1500` |
32
+ | `MU_SPAWN_LIVENESS_MS` | tmux only. Wait this long, then check the pane is alive and its scrollback has no startup error (auth failure, `command not found`). Failure rolls back the row and throws `AgentDiedOnSpawnError` or `AgentSpawnStartupError`. A pi agent skips the wait: the pane is checked on each ctl handshake tick and scanned when it ends. | `1500` |
33
33
  | `MU_SPAWN_LOCK_TIMEOUT_MS` | How long a spawn waits for the per-workstream spawn lock. | `15000` |
34
34
 
35
35
  ## Send and state (tmux path)
@@ -68,7 +68,7 @@ pi agent's `--command`. mu never reads them back.
68
68
  | --- | --- | --- |
69
69
  | `MU_DELEGATE` | `0` hides the `mu_delegate` tool. `mu doctor` reports it. | unset: shown |
70
70
  | `MU_DELEGATE_MAX` | Most delegates one pi session runs at once. Further calls queue, up to four times this; past that `mu_delegate` refuses. A positive integer. | `16` |
71
- | `MU_NUDGE` | `0` turns off both nudges: the keep-driving nudge (one reminder when an orchestrator ends a turn with dispatched work still IN_PROGRESS) and the close nudge (one reminder when a mu-spawned pi worker ends a turn still owning an IN_PROGRESS task). `mu doctor` reports it. | unset: on |
71
+ | `MU_NUDGE` | `0` turns off the three nudges: the keep-driving nudge (one reminder when an orchestrator ends a turn with dispatched work still IN_PROGRESS), the close nudge (one reminder when a mu-spawned pi worker ends a turn still owning an IN_PROGRESS task), and the refute nudge (one notice when an orchestrator dispatched a task whose notes hold no `REFUTER`, `VERDICT:` or `REFUTE-EXEMPT:` line). A pi nested in an agent's pane (it found `MU_CTL_SOCK` served by another pi) gets none of them; a pi without UI (`pi -p`, json mode) gets no close or refute nudge. `mu doctor` reports it. | unset: on |
72
72
  | `MU_PI_HOME` | Root under which `mu link pi` writes `.pi/` and `.agents/`. | `$HOME` |
73
73
  | `PI_CODING_AGENT_DIR` | Read only. pi's agent dir, checked for a linked murmur extension. | `~/.pi/agent` |
74
74
  | `MU_EXTENSION_ENTRY` | Test only. Built extension the `mu link pi` shim imports. | `dist/extension/mu-pi.js` |
@@ -140,3 +140,24 @@ claimed a task for a worker, ended its turn, received one
140
140
  `[mu-keep-driving]` message with `mu task wait nudgeprobe/slow ...`, and
141
141
  `mu log --kind nudge` held one entry. With nothing IN_PROGRESS, no
142
142
  nudge fired.
143
+
144
+ ## Refute nudge (2026-10-05)
145
+
146
+ A sibling hook in the same file (`registerRefuteNudge`), registered on
147
+ its own: it does not need the keep-driving markers and has its own
148
+ once-per-prompt flag.
149
+
150
+ - **Arm** on `mu task claim <id> --for` (`dispatchedTasks`: the id is
151
+ the first positional that is not an option value; `<ws>/<id>`
152
+ splits). `review_*` tasks never arm: their brief is the fixed
153
+ reviewer template, judging work against an already-refuted brief.
154
+ - **Check** at a completed `agent_before_settle`: `mu task notes <id>
155
+ -w <ws> --json` per armed task. This is a second read beside
156
+ `mu state --json`. A task counts as refuted when a note has a line
157
+ starting `REFUTER `, `VERDICT:` or `REFUTE-EXEMPT:`; prose that only
158
+ mentions the words mid-line does not count.
159
+ - **Notify** once per prompt: one `custom_message`
160
+ (`customType: "mu-refute-brief"`, `display: true`) listing every
161
+ unrefuted `<ws>/<id>`, and `mu log --kind nudge 'refute: ...'`. No
162
+ `continue`: it informs, and the orchestrator decides. `MU_NUDGE=0`
163
+ turns it off.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mu-crew/mu",
3
- "version": "3.6.2",
3
+ "version": "3.8.0",
4
4
  "description": "A persistent, observable crew of pi agents running in one tmux or herdr session per workstream, coordinated through a built-in task DAG.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: mu
3
- description: Manage AI agents in terminal-multiplexer panes (tmux or herdr) — from a single off-the-cuff helper to a persistent crew coordinated through a built-in task graph. Use when the user asks to "create/spin up a subagent to X", "run X in the background", "do this in parallel", "use one subagent per X to do Y", "kick off a helper to watch/investigate/draft X", or to spawn, send work to, observe, or coordinate one or many agents — especially work you'll keep talking to, long-lived or background agents, or anything needing a dependency graph. For zero-ceremony helpers use the reserved `scratch` workstream; for one-shot "fire and get a result back" use the `mu_delegate` tool — its pane is attachable while it runs.
3
+ description: Fresh-context subagents and crews in tmux/herdr panes. Check before you commit: before acting on a claim, root cause, plan, fix or brief, have a `mu_delegate` call refute it. Also for a fresh delegate's second look at your own work or diff; parallel read-only research, investigation or fan-out over files; background helpers (watchers, soak runs, agents you keep talking to); persistent crews with a task graph, review gates and merges. Outside pi, delegate through the reserved `scratch` workstream.
4
4
  ---
5
5
 
6
6
  # mu — Multi-agent orchestration
@@ -53,9 +53,9 @@ Recipe words, the same in every recipe:
53
53
  - **wave** — the units dispatched from one main revision; refresh
54
54
  workspaces before the next.
55
55
  - **finding** — one reported problem, recorded as an `OPEN/triage` task.
56
- - **verdict** — the last line of a check's answer:
57
- `VERDICT: CONFIRMED | REFUTED | UNVERIFIED <evidence>` (or ACCEPT |
58
- REJECT for a review gate).
56
+ - **verdict** — a check's answer: `VERDICT: <id> CONFIRMED | REFUTED |
57
+ UNVERIFIED <one line>` plus `EVIDENCE:` lines (or ACCEPT | REJECT for
58
+ a review gate).
59
59
  - **stop rule** — the command and condition that end a loop, written on
60
60
  the umbrella first ([loop-until-done](recipes/loop-until-done.md)).
61
61
 
@@ -65,12 +65,18 @@ Use mu for persistent helpers, parallel work, dependencies, gated review, or
65
65
  work that must survive context compaction. Stay in one context for tiny edits
66
66
  or inspection.
67
67
 
68
- ### Off-the-cuff helpers (`scratch`)
68
+ ### Quick checks and off-the-cuff helpers (`scratch`)
69
69
 
70
- For one-shot work inside pi, call `mu_delegate` (installed by `mu link pi`).
71
- Outside pi: spawn into the reserved `scratch` workstream (no task DAG,
72
- auto-created), `send --fresh`, then `mu agent wait --json` (`lastText`).
73
- Recipes call either form a **delegate call**.
70
+ Before you act on a claim, root cause, plan, fix or brief, or to get a second
71
+ look at your own work or diff, make a [delegate call](recipes/tasks-or-calls.md#delegate-call)
72
+ that tries to refute it (briefs: see Orchestrator rules; many claims: [refute](recipes/refute.md)).
73
+
74
+ For one-shot work inside pi, call `mu_delegate` (installed by `mu link pi`). Outside pi: spawn
75
+ into the reserved `scratch` workstream (no task DAG, auto-created), `send --fresh --json`, then
76
+ `mu agent wait --after-runs <its runs> --json` (`lastText`); without `--after-runs` a run that
77
+ ends first hangs the wait. Recipes call either form a **delegate call**. A check judging a mu
78
+ task lands its verdict there: `record: { task: "<ws>/<id>" }` on `mu_delegate`, a hand-written
79
+ `REFUTER` note on the spawn path; with no task, skip both.
74
80
 
75
81
  - `mu agent wait <names...> --first` waits for busy → idle instead of a
76
82
  `sleep` loop; exit 0 met, 5 timeout, 6 pane died.
@@ -78,8 +84,10 @@ Recipes call either form a **delegate call**.
78
84
  - One agent per independent unit; `--workspace` for any helper that may edit,
79
85
  build, or test the shared repo.
80
86
 
81
- A helper stuck at `needs_input` right after spawn likely hit pi's
82
- project trust prompt: add `--approve` to `MU_<CLI>_COMMAND`. Move off
87
+ A pi helper showing `unknown (ctl missing)` right after spawn (spawn
88
+ warns after 30 s) likely sits at pi's project trust prompt (through a
89
+ wrapper whose argv0 is not `pi`/`pi-meta` it shows `needs_input`): add
90
+ `--approve` to `MU_<CLI>_COMMAND`. Move off
83
91
  `scratch` when work gains dependencies or review gates.
84
92
 
85
93
  ## Mental model
@@ -170,11 +178,18 @@ rules hold even when you skip it:
170
178
  - **`mu task wait --first --on-stall exit`**; exit 7 means read the
171
179
  owner's pane and answer it.
172
180
  - **Stop a worker gently**: `mu agent abort`, then `kick`, then `close`.
181
+ To redirect a busy pi now: `send --interrupt` (`--steer` waits for
182
+ the running tool).
183
+ - **Send, don't note, a running worker**: it never sees new notes. The note is the durable copy
184
+ ([how to send](recipes/orchestrator-loop.md#sending)).
185
+ - **Refute a brief claiming a cause, fix, threshold or code fact** before dispatch:
186
+ one `record`ed refuter call; rewrite on AMEND. Others get a `REFUTE-EXEMPT: <why>` note.
173
187
  - **Checks are calls, not tasks.** Refuters, claim checkers, judges and
174
188
  skeptics are [delegate calls](recipes/tasks-or-calls.md#delegate-call)
175
189
  (`mu_delegate`, or a `scratch` spawn without it), all issued in one
176
- turn; their verdict lands on the task they judged. `mu_delegate` runs
177
- `MU_DELEGATE_MAX` (default 16) at once and queues the rest.
190
+ turn. Refuters and claim checkers pass `record` (or hand-write the
191
+ `REFUTER` note) to land the verdict on the task; judges report
192
+ `WINNER:`. `MU_DELEGATE_MAX` (16) run at once; the rest queue.
178
193
  - **Findings are tasks.** A reviewer's or auditor's findings become
179
194
  `mu task add --triage` tasks blocking the review, decided with
180
195
  `mu task accept` or `close --as rejected|duplicate`. A review nobody
@@ -193,6 +208,8 @@ rules hold even when you skip it:
193
208
  - **For waits use `task wait`, not `log --tail`.** `mu log --kind <k>`
194
209
  entries are your own durable state (a ledger); `--intent` filters what
195
210
  mu recorded.
211
+ - **Prose in notes, prompts, `--why` or `--evidence`**: an apostrophe
212
+ ends `'...'`; use a quoted heredoc ([brief](recipes/brief.md#quoting)).
196
213
  - **`mu sql`** does not pull synced ops; run any other mu command first.
197
214
  - **Never put `MU_DB_PATH` inside `MU_SYNC_DIR`**: it corrupts the DB.
198
215
  - Before undo, rebuild, teardown (dry-run without `--yes`), sync setup,
@@ -209,9 +226,15 @@ export MU_PI_COMMAND="pi --model sonnet:medium"
209
226
  mu agent spawn a --cli pi_big # uses $MU_PI_BIG_COMMAND
210
227
  ```
211
228
 
212
- Convention: `pi_mini` for probing, `pi` for build and refactor,
213
- `pi_big` for design, review, and incidents. List models with
214
- `pi --list-models [search]`.
229
+ `pi_mini` scouts and finds, `pi` builds and refactors, `pi_big` designs and
230
+ handles incidents. A reviewer, refuter,
231
+ judge or auditor checks level or up, never down: at least the worker's
232
+ capability, from another family if one exists (provider or model-id prefix in
233
+ `pi --list-models`). Small-model signs: a local runtime (ollama, lmstudio,
234
+ llama.cpp), a size (`4b`, `27b`), mini/haiku/flash/nano/lite, thinking `no`,
235
+ context or max-out far below the worker's. No such peer: same model, fresh
236
+ context, higher thinking unless at max. Probe an unused model first
237
+ (`pi --model <provider/model> -p "say ok"`); `ctl: ok` does not test it.
215
238
 
216
239
  ## Reaper and agent state
217
240
 
@@ -254,6 +277,7 @@ risky job that needs several of them, start with
254
277
  | [remote-workers](recipes/remote-workers.md) | an agent runs on another machine |
255
278
  | [waves](recipes/waves.md) | more than one worker edits the same repo at once |
256
279
  | [long-run](recipes/long-run.md) | a task or proof runs for hours, or must survive flakes |
280
+ | [drift-audit](recipes/drift-audit.md) | hours into a long run, after a batch, compaction or resume, before a stop rule |
257
281
  | [watcher](recipes/watcher.md) | a helper polls a PR, CI, or log for change |
258
282
  | [findings](recipes/findings.md) | any review, audit, or check reports problems: where they live, how they are triaged |
259
283
  | [tasks-or-calls](recipes/tasks-or-calls.md) | a recipe step spawns an agent: DAG task or delegate call |
@@ -28,9 +28,8 @@ PR or diff from several angles, use [review-panel](review-panel.md).
28
28
  3. **Dispatch the work** as usual. When `x` closes, `review_x` becomes
29
29
  ready.
30
30
  4. **Spawn a fresh reviewer.** A new agent, or `--fresh` on an idle one,
31
- so it has none of the author's reasoning. Use a different model from
32
- the worker where you can (`--cli pi_big` reviewing `pi`): a second
33
- model has different blind spots. Give it the worker's workspace path
31
+ so it has none of the author's reasoning, on a model chosen per
32
+ [Models](../SKILL.md#models-and-thinking-effort). Give it the worker's workspace path
34
33
  and commits read-only by instruction; it edits nothing.
35
34
  5. **Claim `review_x` for the reviewer**, send the brief (below), wait.
36
35
  6. **Act on the verdict:**
@@ -71,11 +70,20 @@ where it fails the acceptance criteria in `mu task notes <x>`.
71
70
  UNVERIFIED for it. Unverified is not a pass and not a fail.
72
71
  - Do not edit files.
73
72
 
74
- End with a note in the task note contract, then close with the verdict:
75
- ACCEPT: mu task close review_<x> --evidence '<key check>'
76
- REJECT: mu task close review_<x> --as rejected --why '<gap ids>'
73
+ Write the verdict note (FILES/COMMANDS/FINDINGS/VERIFIED), then close
74
+ (long reasons: a heredoc variable, brief.md § Quoting):
75
+ ACCEPT: mu task close review_<x> --evidence "<key command>: <result>"
76
+ REJECT: mu task close review_<x> --as rejected \
77
+ --why "<n> gaps: <id> (<why it fails, one clause>), ..."
77
78
  ```
78
79
 
80
+ mu adds each named gap's title to the REJECTED note.
81
+
82
+ **Closing a review yourself** (you fixed a finding, or accept without a
83
+ fresh reviewer): write the reviewer's note plus `COMMIT: <sha> fixes
84
+ <finding>`, and name the check in `--evidence`. Not `one finding fixed
85
+ by orchestrator`.
86
+
79
87
  ## Traps
80
88
 
81
89
  - **A reviewer that sees the author's reasoning agrees with it.** Send
@@ -32,6 +32,7 @@ not in the brief, or one pointer away, does not exist for it.
32
32
  6. **Ask for evidence, not reassurance.** The final note names the
33
33
  commands run and their exit codes (the task note contract). "Done,
34
34
  all good" is not a result.
35
+ 7. **A brief that makes claims is refuted before dispatch** ([SKILL.md](../SKILL.md#orchestrator-rules)).
35
36
 
36
37
  ## Shape
37
38
 
@@ -46,8 +47,35 @@ VERIFY: <exact command>
46
47
 
47
48
  ⚠️ FINAL ACTION
48
49
  git commit -am '<msg>' THEN
49
- mu task close <id> -w <ws> --evidence '<command + result>'
50
+ mu task close <id> -w <ws> --evidence "$v" # command + result; see Quoting
50
51
  ```
51
52
 
53
+ ## Quoting
54
+
55
+ Single quotes suit short text with no `'`. Prose (an apostrophe ends
56
+ `'...'`; `"..."` expands `$` and backticks) goes through stdin with a
57
+ quoted heredoc, stored verbatim:
58
+
59
+ ```bash
60
+ mu task note <id> -w <ws> - <<'EOF'
61
+ it's fine: $HOME and `pwd` stay literal
62
+ EOF
63
+ ```
64
+
65
+ `mu task add --note -`, `--text -` and `mu agent send <a> -` read stdin
66
+ the same way. `--why` and `--evidence` have no `-` form: capture the
67
+ heredoc in a variable and double-quote it:
68
+
69
+ ```bash
70
+ v=$(cat <<'EOF'
71
+ it's fine: $HOME stays literal
72
+ EOF
73
+ )
74
+ mu task close <id> -w <ws> --evidence "$v"
75
+ ```
76
+
77
+ Chain batched adds with `&&`, then `mu task list`, so one
78
+ quoting failure cannot half-apply the batch unseen.
79
+
52
80
  Done when a worker with no other context could finish the task from
53
81
  the brief and the notes it points at, and could tell when it is done.
@@ -62,16 +62,24 @@ the `scratch` form through `tools.bash`:
62
62
 
63
63
  ```js
64
64
  // @options: {"timeout_ms": 1800000}
65
+ const ws = "audit";
65
66
  const findings = ["f_auth_put", "f_sql_order"]; // finding task ids, at most the cap per script
66
67
  const sh = async (c) => (await tools.bash({ command: c })).output;
68
+ const json = (s) => { try { return JSON.parse(s); } catch { return {}; } };
69
+ const el = (ms) => { const t = Math.round(ms / 1000); return t < 60 ? `${t}s` : `${Math.floor(t / 60)}m ${String(t % 60).padStart(2, "0")}s`; };
67
70
  const rows = await Promise.all(findings.map(async (id, i) => {
68
- const a = `refuter-${i + 1}`;
71
+ const a = `refuter-${i + 1}`, t0 = Date.now();
69
72
  await sh(`mu agent spawn ${a} -w scratch`);
70
- await sh(`mu agent send ${a} -w scratch --fresh '<brief for ${id}: ... end with VERDICT: ...>'`);
71
- const w = JSON.parse(await sh(`mu agent wait ${a} -w scratch --json --timeout 1200`));
72
- const text = w.agents?.[0]?.lastText ?? "";
73
- await sh(`mu agent close ${a} -w scratch`);
74
- return `${id}\t${(text.match(/VERDICT:.*$/m) ?? ["VERDICT: UNVERIFIED no verdict line"])[0]}`;
73
+ const { runs } = json(await sh(`mu agent send ${a} -w scratch --fresh --json - <<'MU_EOF'\n<brief for ${id}, ending with the VERDICT block>\nMU_EOF`));
74
+ const w = json(await sh(`mu agent wait ${a} -w scratch --after-runs ${runs} --json --timeout 1200`)).agents?.[0] ?? {};
75
+ const lines = (w.lastText ?? "").trimEnd().split("\n");
76
+ const at = lines.findLastIndex((l) => /^[\s>*_`-]*VERDICT:/.test(l));
77
+ const note = w.outcome !== "done" ? `REFUTER ${i + 1}: no verdict (${w.outcome ?? "no result"})`
78
+ : `REFUTER ${i + 1} (${a}, ${el(Date.now() - t0)}):\n` + (at < 0 ? `NO VERDICT LINE: ${(w.lastText ?? "").slice(-1500)}`
79
+ : [lines[at], ...lines.slice(at + 1).filter((l) => /^[\s>*_`-]*EVIDENCE:/.test(l))].join("\n"));
80
+ await sh(`mu task note ${id} -w ${ws} - <<'MU_EOF'\n${note}\nMU_EOF`);
81
+ if (w.outcome === "done") await sh(`mu agent close ${a} -w scratch`);
82
+ return `${id}\t${note.split("\n")[1] ?? note}`;
75
83
  }));
76
84
  return rows.join("\n");
77
85
  ```
@@ -82,9 +90,11 @@ What the tool did for you, the script now owns:
82
90
  each script's list at or under it.
83
91
  - **Cleanup.** Close each scratch pane; a timed-out one stays, readable
84
92
  with `mu agent read <a> -w scratch`.
85
- - **Recording.** The table is not the record. Record each verdict on its
86
- finding (`mu task accept`, `close --as rejected --why`), from the
87
- script or after it returns.
93
+ - **Recording.** The script writes each `REFUTER` note in `record`'s
94
+ shape, and `no verdict (<outcome>)` for a failed wait; it does not
95
+ cap long answers or special-case empty ones as `record` does. Decide
96
+ each finding after it returns ([findings § Triage](findings.md#triage)
97
+ step 3).
88
98
 
89
99
  ## Traps
90
100
 
@@ -41,12 +41,13 @@ answer rests on is an `OPEN/triage` task, decided like a finding
41
41
  gets the claim and its sources, not the searcher's reasoning, fetches
42
42
  the source itself, and tries to break the claim: is the quote there,
43
43
  does it say that, is it current, does another source disagree? It
44
- ends with `VERDICT: c4 CONFIRMED | REFUTED | UNVERIFIED <evidence>`.
44
+ ends with the VERDICT block
45
+ ([tasks-or-calls § Delegate call](tasks-or-calls.md#delegate-call)).
45
46
  For contradicted claims, one checker per side. Batch under the cap
46
- ([orchestrator-loop § Concurrency](orchestrator-loop.md#concurrency)). In workstream mode the
47
- verdict is the claim task's state: `accept` for confirmed,
48
- `close --as rejected` for refuted, an `UNVERIFIED:` note in triage
49
- otherwise.
47
+ ([orchestrator-loop § Concurrency](orchestrator-loop.md#concurrency)).
48
+ In workstream mode the checker passes `record` on the claim task, and
49
+ the verdict is decided as in [findings § Triage](findings.md#triage)
50
+ step 3.
50
51
  5. **Synthesize** once every checker has answered (in workstream mode,
51
52
  when no claim task is left undecided): answer the question
52
53
  from confirmed claims only, each with its source URL. List