@mu-crew/mu 3.13.3 → 4.1.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 (119) hide show
  1. package/AGENTS.md +73 -35
  2. package/README.md +71 -57
  3. package/dist/chunk-4ULY3ZKO.js +4482 -0
  4. package/dist/chunk-4ULY3ZKO.js.map +1 -0
  5. package/dist/chunk-7M634JMD.js +375 -0
  6. package/dist/chunk-7M634JMD.js.map +1 -0
  7. package/dist/{chunk-3BARPJLY.js → chunk-PZ5GOCWH.js} +16 -2
  8. package/dist/chunk-PZ5GOCWH.js.map +1 -0
  9. package/dist/chunk-SS7NQSSZ.js +3660 -0
  10. package/dist/chunk-SS7NQSSZ.js.map +1 -0
  11. package/dist/chunk-UCZPJX7K.js +14253 -0
  12. package/dist/chunk-UCZPJX7K.js.map +1 -0
  13. package/dist/{cli-CKOMHBXV.js → cli-TV3N47AZ.js} +7317 -4593
  14. package/dist/cli-TV3N47AZ.js.map +1 -0
  15. package/dist/cli.js +2 -3
  16. package/dist/cli.js.map +1 -1
  17. package/dist/errors-GRONKWFS.js +65 -0
  18. package/dist/extension/mu-pi.js +1632 -837
  19. package/dist/extension/mu-pi.js.map +1 -1
  20. package/dist/index.d.ts +4067 -2561
  21. package/dist/index.js +614 -297
  22. package/dist/index.js.map +1 -1
  23. package/dist/tui-FUBQ326D.js +1848 -0
  24. package/dist/tui-FUBQ326D.js.map +1 -0
  25. package/docs/ARCHITECTURE.md +118 -66
  26. package/docs/VOCABULARY.md +113 -49
  27. package/docs/guide/README.md +3 -3
  28. package/docs/guide/backends.md +8 -4
  29. package/docs/guide/cleanup.md +69 -33
  30. package/docs/guide/delegate.md +149 -66
  31. package/docs/guide/dispatch.md +148 -53
  32. package/docs/guide/getting-started.md +37 -44
  33. package/docs/guide/other-agent-clis.md +102 -0
  34. package/docs/guide/recovery.md +31 -50
  35. package/docs/guide/remote.md +122 -45
  36. package/docs/guide/sql.md +104 -24
  37. package/docs/guide/stop-a-worker.md +30 -37
  38. package/docs/guide/sync.md +11 -3
  39. package/docs/guide/upgrade.md +93 -43
  40. package/docs/reference/env.md +24 -32
  41. package/docs/reference/naming.md +14 -7
  42. package/docs/reference/surface-contract.md +153 -0
  43. package/package.json +33 -11
  44. package/prompts/mu-debug.md +3 -1
  45. package/prompts/mu-refute.md +2 -1
  46. package/prompts/mu-research.md +3 -1
  47. package/prompts/mu-review.md +2 -1
  48. package/prompts/mu-rules-audit.md +5 -4
  49. package/prompts/mu-sweep.md +2 -1
  50. package/prompts/mu-tournament.md +4 -2
  51. package/prompts/mu-until.md +3 -1
  52. package/prompts/ultrathink.md +1 -1
  53. package/skills/mu/SKILL.md +60 -291
  54. package/skills/mu/recipes/adversarial-review.md +12 -103
  55. package/skills/mu/recipes/brief.md +14 -61
  56. package/skills/mu/recipes/course-correct.md +23 -0
  57. package/skills/mu/recipes/findings.md +17 -111
  58. package/skills/mu/recipes/models.md +17 -92
  59. package/skills/mu/recipes/move.md +14 -0
  60. package/skills/mu/recipes/orchestrator-loop.md +20 -119
  61. package/skills/mu/recipes/recovery.md +8 -29
  62. package/skills/mu/recipes/remote-workers.md +17 -318
  63. package/skills/mu/recipes/tasks-or-calls.md +15 -78
  64. package/skills/mu/recipes/test-health.md +19 -77
  65. package/skills/mu/recipes/worker.md +17 -24
  66. package/dist/chunk-45KQLMEL.js +0 -2908
  67. package/dist/chunk-45KQLMEL.js.map +0 -1
  68. package/dist/chunk-G434JKV2.js +0 -641
  69. package/dist/chunk-G434JKV2.js.map +0 -1
  70. package/dist/chunk-KUGUFBWH.js +0 -12527
  71. package/dist/chunk-KUGUFBWH.js.map +0 -1
  72. package/dist/chunk-QITLNZEM.js +0 -87
  73. package/dist/chunk-QITLNZEM.js.map +0 -1
  74. package/dist/chunk-VIEYQMQA.js +0 -393
  75. package/dist/chunk-VIEYQMQA.js.map +0 -1
  76. package/dist/chunk-WI6AWCMK.js +0 -19
  77. package/dist/chunk-WI6AWCMK.js.map +0 -1
  78. package/dist/cli-CKOMHBXV.js.map +0 -1
  79. package/dist/devtools-LS4ZEPWM.js +0 -3772
  80. package/dist/devtools-LS4ZEPWM.js.map +0 -1
  81. package/dist/tui-LFJ5P4Y2.js +0 -40956
  82. package/dist/tui-LFJ5P4Y2.js.map +0 -1
  83. package/dist/tui-sync-worker.js +0 -17
  84. package/dist/tui-sync-worker.js.map +0 -1
  85. package/dist/yank-XDOONQSL.js +0 -13
  86. package/dist/yank-XDOONQSL.js.map +0 -1
  87. package/docs/ROADMAP.md +0 -123
  88. package/docs/VISION.md +0 -298
  89. package/docs/architecture/control-socket.md +0 -142
  90. package/docs/architecture/dag.md +0 -117
  91. package/docs/architecture/mux.md +0 -167
  92. package/docs/architecture/ops-log.md +0 -160
  93. package/docs/architecture/sdk.md +0 -187
  94. package/docs/architecture/sync.md +0 -159
  95. package/docs/architecture/tui.md +0 -165
  96. package/docs/guide/tui.md +0 -98
  97. package/docs/history/CHANGELOG-pre-3.md +0 -4866
  98. package/docs/img/tui-dashboard.png +0 -0
  99. package/docs/specs/2026-09-28-agent-state-from-murmur.md +0 -333
  100. package/docs/specs/2026-09-29-task-substates.md +0 -247
  101. package/docs/specs/2026-10-03-keep-driving-nudge.md +0 -163
  102. package/docs/specs/2026-10-21-fast-and-slow-gates.md +0 -229
  103. package/skills/mu/recipes/backlog-triage.md +0 -45
  104. package/skills/mu/recipes/codemode-driver.md +0 -118
  105. package/skills/mu/recipes/deep-research.md +0 -69
  106. package/skills/mu/recipes/drift-audit.md +0 -75
  107. package/skills/mu/recipes/fan-out.md +0 -42
  108. package/skills/mu/recipes/hypothesis-panel.md +0 -48
  109. package/skills/mu/recipes/long-run.md +0 -32
  110. package/skills/mu/recipes/loop-until-done.md +0 -45
  111. package/skills/mu/recipes/plan.md +0 -67
  112. package/skills/mu/recipes/refute.md +0 -55
  113. package/skills/mu/recipes/review-panel.md +0 -65
  114. package/skills/mu/recipes/rules-audit.md +0 -89
  115. package/skills/mu/recipes/tournament.md +0 -44
  116. package/skills/mu/recipes/ultrathink.md +0 -86
  117. package/skills/mu/recipes/watcher.md +0 -28
  118. package/skills/mu/recipes/waves.md +0 -21
  119. /package/dist/{chunk-3BARPJLY.js.map → errors-GRONKWFS.js.map} +0 -0
@@ -8,19 +8,20 @@ You need Node.js, the pi coding agent, tmux 3.0
8
8
  or later, and a git repository to work in. Run every command from
9
9
  inside a tmux session, in the root of that repository.
10
10
 
11
- ## Install mu and link pi
11
+ ## Install mu and the pi package
12
12
 
13
- First, install mu and connect it to pi:
13
+ First, install mu and install its pi package:
14
14
 
15
15
  ```bash
16
16
  npm i -g @mu-crew/mu
17
- mu link pi
17
+ pi install npm:@mu-crew/mu
18
18
  mu doctor
19
19
  ```
20
20
 
21
- `mu link pi` installs the mu pi extension and the mu skill. In the
22
- output of `mu doctor`, the extension, skill, and `ctl` rows say `ok`.
23
- If pi was already running, restart it so it loads the extension.
21
+ `pi install` loads the mu extension, skill, and recipe prompts as one
22
+ package. Once a pi agent is running, the "mu ext" and `ctl` rows of
23
+ `mu doctor` report the live extension; before that, "mu ext" says
24
+ `unknown`. If pi was already running, restart it so it loads the package.
24
25
 
25
26
  ## Create a workstream
26
27
 
@@ -33,13 +34,11 @@ mu workstream init auth
33
34
 
34
35
  ```
35
36
  Created workstream auth (mux session mu-auth)
36
- Next:
37
- Attach the session : tmux attach -t mu-auth
38
- ...
37
+ attach: tmux attach -t mu-auth
39
38
  ```
40
39
 
41
- Every verb ends with a `Next:` block of follow-up commands. We use
42
- them as we go.
40
+ When a verb leaves a move to make (a wait a send owes, the `--if` apply
41
+ of a preview), it ends with a `next:` line you can run as printed.
43
42
 
44
43
  ## Plan two tasks
45
44
 
@@ -53,11 +52,7 @@ mu task next -w auth
53
52
  ```
54
53
 
55
54
  ```
56
- ┌────────┬────────┬────────────────────────┬────────┬────────┬──────┬───────┐
57
- │ name │ status │ title │ impact │ effort │ ROI │ owner │
58
- ├────────┼────────┼────────────────────────┼────────┼────────┼──────┼───────┤
59
- │ design │ OPEN │ Design the auth module │ 80 │ 2 │ 40.0 │ — │
60
- └────────┴────────┴────────────────────────┴────────┴────────┴──────┴───────┘
55
+ task design OPEN impact=80 effort_days=2 Design the auth module
61
56
  ```
62
57
 
63
58
  Only `design` is ready. `build` appears when `design` closes.
@@ -67,12 +62,12 @@ Only `design` is ready. `build` appears when `design` closes.
67
62
  Spawn a pi agent with its own git worktree:
68
63
 
69
64
  ```bash
70
- mu agent spawn worker-1 -w auth --workspace
65
+ mu agent spawn worker-1 -w auth --worktree
71
66
  ```
72
67
 
73
68
  ```
74
- Spawned worker-1 (pi) in window worker-1 of mu-auth, pane %15 with auto-workspace
75
- workspace: ~/.local/state/mu/workspaces/auth/worker-1 (git)
69
+ Spawned worker-1 (pi) in window worker-1 of mu-auth, pane %15 with its own worktree
70
+ cwd: ~/.local/state/mu-next/worktrees/auth/worker-1
76
71
  ```
77
72
 
78
73
  The worker runs in a new window of the `mu-auth` session. To watch
@@ -81,7 +76,7 @@ it, run `tmux attach -t mu-auth`. Detach with `Ctrl+b d`.
81
76
  ## Claim the task for the worker
82
77
 
83
78
  Assign the task before you send any text. The claim is what
84
- `mu task wait` watches.
79
+ `mu wait` watches.
85
80
 
86
81
  ```bash
87
82
  mu task note design -w auth 'SCOPE: write docs/auth.md with the token design'
@@ -108,29 +103,26 @@ and closes the task.
108
103
  ## Wait for the task to close
109
104
 
110
105
  ```bash
111
- mu task wait design -w auth --first --on-stall exit
106
+ mu wait task:design -w auth
112
107
  ```
113
108
 
114
109
  ```
115
- auth/design
116
- any-of 1 reached CLOSED in 41094ms
117
- Next:
118
- Cherry-pick worker-1's commit onto your branch : git cherry-pick 26278f1...
119
- Refresh worker-1's workspace onto current main : mu workspace refresh worker-1 -w auth
110
+ {"ok":true,"data":{"firing":{"kind":"closed","ref":"task:auth/design"},...},"next":null}
120
111
  ```
121
112
 
122
113
  The exit code tells you what happened:
123
114
 
124
115
  | Exit | Meaning | Next move |
125
116
  | ---- | ------- | --------- |
126
- | `0` | the task closed | merge the work |
127
- | `5` | `--timeout` expired (default 600 s) | wait again |
117
+ | `0` | the task closed (`firing.kind` `closed`), or the heartbeat cap passed (`heartbeat`, `timedOut: true`; default 1500 s) | merge the work, or run `data.resume` to keep waiting |
128
118
  | `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 `--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 |
119
+ | `7` | the worker sat in `needs_input` for `--stuck-after` seconds (120, or 5 for a pi worker read over its control socket) | read it (`mu agent read worker-1 -w auth`), then answer it |
130
120
 
131
- `--on-stall exit` is the default; it is spelled out above so older mu
132
- versions behave the same. With `--on-stall warn`, a stalled worker only
133
- prints a warning and the wait keeps running.
121
+ To keep waiting, run `data.resume` as printed (`mu wait --resume <id> -w auth`):
122
+ it replays the stored wait whole, and refuses any other flag. With
123
+ `--on-stall warn`, a stalled worker only prints a warning and the wait
124
+ keeps running. `--on-stall steer|release` act on a worker you spawned;
125
+ see [dispatch](dispatch.md).
134
126
 
135
127
  A pi worker that ends its turn while it still owns an `IN_PROGRESS`
136
128
  task gets one reminder to close it or say why not, so exit 7 mostly
@@ -138,8 +130,8 @@ means it is blocked or asking something.
138
130
 
139
131
  ## Merge the commit
140
132
 
141
- Run the cherry-pick command from the `Next:` block, then run your
142
- tests on the result:
133
+ Inspect the worker's commits (`git -C <worktree> log --oneline`),
134
+ cherry-pick what you want, then run your tests on the result:
143
135
 
144
136
  ```bash
145
137
  git cherry-pick 26278f1
@@ -149,11 +141,12 @@ Before the next task, rebase the worker's worktree onto the new main.
149
141
  The worker keeps its pi context:
150
142
 
151
143
  ```bash
152
- mu workspace refresh worker-1 -w auth
144
+ cwd=$(mu agent list -w auth --json | jq -r '.agents[] | select(.name == "worker-1") | .cwd')
145
+ git -C "$cwd" fetch && git -C "$cwd" rebase origin/HEAD
153
146
  ```
154
147
 
155
- The default target is the remote's main branch (`origin/HEAD`). In a
156
- repository without a remote, name the branch: `--from main`.
148
+ In a repository without a remote, rebase onto local `main`. On a conflict,
149
+ run `git -C "$cwd" rebase --abort` and resolve it by hand.
157
150
 
158
151
  ## Check the graph
159
152
 
@@ -166,20 +159,20 @@ claim, send, wait, and merge steps.
166
159
 
167
160
  ## Close the worker
168
161
 
169
- When you are done, free the worktree and close the agent:
162
+ When you are done, close the agent:
170
163
 
171
164
  ```bash
172
- mu workspace free worker-1 -w auth
173
- mu agent close worker-1 -w auth
165
+ mu agent close worker-1 -w auth # preview; prints the --if command
166
+ mu agent close worker-1 -w auth --if <n> # close it
174
167
  ```
175
168
 
176
- `mu agent close` refuses while the worktree has commits or edits that
177
- mu has not freed. That protects work you have not merged.
169
+ Close removes the worktree. It refuses while the worktree has uncommitted
170
+ edits, which protects work you have not committed. If the worker made
171
+ commits you have not merged, close keeps them on a branch
172
+ `mu/auth/worker-1-<sha>` and prints its name.
178
173
 
179
174
  ## Next steps
180
175
 
181
176
  - [Dispatch work to a worker](dispatch.md) covers more than one
182
177
  worker and the `--fresh`, `--interrupt`, `--steer`, and plain send choices.
183
- - [Use the TUI dashboard](tui.md): run bare `mu` to see every
184
- workstream at once.
185
178
  - [Clean up](cleanup.md) when the workstream is finished.
@@ -0,0 +1,102 @@
1
+ # How to run mu with other agent CLIs
2
+
3
+ mu is built around pi, but codex, cursor-agent and claude can be its workers
4
+ or its orchestrator. They get less: they have no control socket, so mu
5
+ cannot hand them a prompt on tmux, abort them, or read their final text, and
6
+ nothing replaces `mu_delegate`. Tested with codex 0.164 and cursor-agent
7
+ 2026.10.22 on tmux ([report](../eval/harnesses.md)).
8
+
9
+ ## Pin the mu the harness runs
10
+
11
+ Check this first. codex and cursor-agent pass mu's env (`MU_*`, `PATH`) on
12
+ to their shell tool, but they run each command through your interactive
13
+ shell rc, and the rc wins. If `~/.zshrc` puts another `mu` first on `PATH`,
14
+ or exports `MU_SYNC_DIR` / `MU_DB_PATH`, the harness uses that `mu` and
15
+ that DB. codex's `shell_environment_policy.set` does not help: it is applied
16
+ before the rc runs. Pin it for each harness:
17
+
18
+ - codex: `codex --disable shell_snapshot` (as `MU_CODEX_COMMAND`, or in a
19
+ wrapper).
20
+ - cursor-agent: `ZDOTDIR=<a dir with an empty .zshrc> cursor-agent`.
21
+ - Or keep the rc file from adding `mu` and `MU_*` at all.
22
+
23
+ A wrapper that sets the env itself (`MU_SYNC_DIR=`, `MU_DB_PATH`,
24
+ `MU_STATE_DIR`, then `exec mu "$@"`) holds its env under any rc. Name it
25
+ something the rc does not shadow (`mun`). The skill and briefs still say
26
+ `mu`, so pin `mu` as well.
27
+
28
+ Then check from inside the harness:
29
+ `command -v mu; mu --version; echo "<$MU_SYNC_DIR>"`.
30
+
31
+ ## Run one as a worker
32
+
33
+ On tmux, mu cannot type into a non-pi pane, so `spawn --send` and `agent
34
+ send` exit 4. Put the brief in a task note, start the agent with the prompt
35
+ as its argument, then claim:
36
+
37
+ ```bash
38
+ mu task note build -w auth - <<'EOF'
39
+ <brief, ending with: mu task close build -w auth --evidence "...">
40
+ EOF
41
+ export MU_CODEX_COMMAND="codex --disable shell_snapshot --dangerously-bypass-approvals-and-sandbox"
42
+ mu agent spawn worker-1 -w auth --cli codex --worktree \
43
+ --command "$MU_CODEX_COMMAND 'Do task build. Run: mu task notes build -w auth'"
44
+ mu task claim build -w auth --for worker-1
45
+ mu wait task:auth/build
46
+ ```
47
+
48
+ On herdr, `--send` and `agent send` work (herdr's `agent prompt`, see
49
+ [dispatch](dispatch.md#non-pi-agents)), but herdr refuses `--command`.
50
+
51
+ - **Dialogs.** A new folder makes codex ask "Trust this folder?", and
52
+ unreviewed hooks ask again. On tmux mu cannot answer them. Pre-trust the
53
+ repo (`-c 'projects."<repo>".trust_level="trusted"'`; worktrees inherit the
54
+ root's trust), or attach and press the key.
55
+ - **State** comes from murmur on tmux, from herdr on herdr. Wire murmur to
56
+ the harness's stop hook (cursor: `.cursor/hooks.json` `stop`, see murmur's
57
+ `docs/setup.md`). Without it the agent is `unknown` throughout. With it,
58
+ you see `needs_input` when a turn ends, but never `busy`.
59
+ - **codex: use its `Stop` hook, not `notify`.** codex runs a hidden turn to
60
+ title each new thread, and `notify` reports that turn as finished about 2 s
61
+ into the worker's first turn. murmur then reads `done`, and `--stuck-after`
62
+ reports the running worker as stalled (3.x does the same). `Stop` fires only
63
+ for real turns. Drop `notify` from `~/.codex/config.toml` and put this in
64
+ `~/.codex/hooks.json`:
65
+
66
+ ```json
67
+ {"hooks": {"Stop": [{"hooks": [{"type": "command",
68
+ "command": "murmur notify --source codex --event-type agent-turn-complete"}]}]}}
69
+ ```
70
+
71
+ Keep `--event-type`: without it murmur reads a `Stop` payload as `blocked`.
72
+ codex asks you to trust a new hook once (`/hooks`), and `--disable hooks`
73
+ turns it off.
74
+ - **Wait on the task, not the agent.** `mu wait task:<ws>/<id>` fires when the
75
+ worker closes it. `mu wait agent:<w>` fires only on busy → not busy, as in
76
+ 3.x: an agent already idle, or one that goes from `unknown` to
77
+ `needs_input`, does not count. murmur's notify path never reports `busy`,
78
+ so on tmux that wait runs to its timeout. `--until-text <re>` matches the
79
+ pane's tail instead.
80
+ - **Stalls** mean the owner has read `needs_input` for `--stuck-after` while
81
+ its task is still open. Read the pane (`mu agent read`) before you act.
82
+ - **No abort, no CLOSE block.** Stop it with `mu agent close` (preview, then
83
+ `--if`). The brief must end with the worker's own `mu task close`.
84
+
85
+ ## Run one as the orchestrator
86
+
87
+ Install the skill (`npx skills add mu-crew/mu`) and point the harness at it.
88
+ It dispatches pi workers as usual (`spawn --worktree --task --send -`).
89
+
90
+ - **Wait on refs.** A bare `mu wait` owns only what a pi session or a managed
91
+ pane started. From codex or cursor in a plain shell it is a usage error.
92
+ Name what you started: `mu wait task:<ws>/<a> task:<ws>/<b>`.
93
+ - **Delegate through `scratch`**, since there is no `mu_delegate`. Spawn a pi
94
+ agent there (the first spawn creates the workstream; `init` refuses it),
95
+ `send --fresh --json -`, then wait on the exact ref the send prints. See
96
+ [tasks-or-calls](../../skills/mu/recipes/tasks-or-calls.md).
97
+
98
+ ## With no multiplexer
99
+
100
+ Task, DAG, `sql`, `log` and `mu wait task:` verbs work. `workstream init`,
101
+ `state`, and the agent verbs exit 5 and name the install. The child-pi
102
+ delegate (`mu delegates run`) needs pi.
@@ -8,33 +8,25 @@ read the same rules in
8
8
 
9
9
  ```bash
10
10
  mu doctor # exit 0 healthy (WARN rows too), 5 on drift or a FAIL row
11
- mu doctor --deep # rebuild the ops log and diff every field
12
- mu doctor --disk # add per-workspace byte usage
11
+ mu doctor --deep # rebuild the ops log and diff every field; PRAGMA quick_check on the DB
12
+ mu doctor --disk # add per-worktree byte usage
13
13
  ```
14
14
 
15
15
  ## An agent's pane died
16
16
 
17
- Run `mu agent list` or `mu state`. Each run reconciles the registry
17
+ Run `mu agent list` or `mu state -w <ws>`. Each run reconciles the registry
18
18
  with the multiplexer:
19
19
 
20
20
  - An agent row whose pane is gone is a ghost. mu deletes the row, and
21
- its `IN_PROGRESS` task returns to `OPEN`. A `mu task wait` on that
21
+ its `IN_PROGRESS` task returns to `OPEN`. A `mu wait` on that
22
22
  task exits 6.
23
23
  - A pane in the session that runs an agent CLI but has no row is an
24
- orphan. mu lists it with an adopt hint and does not adopt it.
24
+ orphan. mu lists it and does not register it. Close the pane with
25
+ tmux or herdr, or ignore it.
25
26
 
26
- To register an orphan pane, or a pi you started by hand:
27
-
28
- ```bash
29
- mu agent adopt %15 -w auth
30
- mu agent adopt %15 -w auth --name investigator
31
- ```
32
-
33
- On herdr, pass its pane id instead (`mu agent adopt w1:p2 -w auth`).
34
-
35
- The pane title becomes the agent name. The pane must be in the
36
- `mu-<workstream>` session. An adopted pi has no control socket until
37
- you restart it with the `MU_CTL_SOCK` path that `adopt` prints.
27
+ A `mu agent spawn --task` after a dead worker, with the same attempt
28
+ key but a new request, is refused (`already ran with a different
29
+ request`): pass `--attempt <new key>`.
38
30
 
39
31
  ## A pi agent shows `ctl missing` or `ctl refused`
40
32
 
@@ -43,19 +35,19 @@ back to pasting.
43
35
 
44
36
  | State | Means | Fix |
45
37
  | ----- | ----- | --- |
46
- | `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 |
38
+ | `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 `pi install npm:@mu-crew/mu`, then respawn the agent |
47
39
  | `ctl refused` | the socket exists but does not answer | respawn the agent; for a remote agent, check the ssh forward |
48
40
  | `extension X older than installed Y` or `extension lacks ops` | pi loaded an older mu build | send `/reload`, or respawn |
49
41
 
50
42
  To send `/reload` without attaching:
51
43
 
52
44
  ```bash
53
- mu agent send worker-1 -w auth '/reload' # extension serves op command
54
- mu agent send worker-1 -w auth '/reload' --via mux # older: type it into the pane
45
+ mu agent send worker-1 -w auth '/reload' # extension serves op command
55
46
  ```
56
47
 
57
- The error names the right form. `/reload` keeps the pi session. A `send --fresh` or `abort` against an
58
- outdated extension fails with exit 4 and prints the same two fixes.
48
+ An extension without op command cannot be reloaded by mu: respawn the
49
+ agent. `/reload` keeps the pi session. A `send --fresh` or `abort`
50
+ against an outdated extension fails with exit 4 and prints the fix.
59
51
 
60
52
  ## A new pi agent shows `ctl missing`
61
53
 
@@ -66,7 +58,7 @@ launched through a wrapper whose name is not `pi` or `pi-meta` shows
66
58
  `needs_input` instead. Run
67
59
  `/trust` in the pane once, or spawn with
68
60
  `--command 'pi --approve'`. `--approve` trusts every directory,
69
- including workspace forks, so use it only on your own code.
61
+ including worktrees, so use it only on your own code.
70
62
 
71
63
  Claude asks the same on an untrusted folder, with "No, exit" as the
72
64
  default. Spawn warns and skips `--send`: attach the pane, answer it, then
@@ -80,22 +72,10 @@ needs restarting.
80
72
 
81
73
  ## You ran the wrong command
82
74
 
83
- Every action is one group of ops. Find it, preview the inverse, then
84
- apply:
85
-
86
- ```bash
87
- mu undo # list recent groups
88
- mu undo 6380dd3d # preview
89
- mu undo 6380dd3d --yes # apply
90
- ```
91
-
92
- - Any unique prefix of a group id works.
93
- - The undo is its own group, so undoing it again is redo.
94
- - If later work changed the same fields, undo exits 4 and names the
95
- conflict. A later write to `updated_at` alone, such as `mu task note`,
96
- does not count. `--force --yes` overrides and discards the newer work.
97
- - Undo restores rows only. Killed panes and freed workspace
98
- directories do not come back.
75
+ Every action is one group of ops. `mu log` shows the group ids, and
76
+ `mu log --group <prefix>` shows what one action wrote. Any unique prefix
77
+ of a group id works. mu has no general undo: fix a wrong change with the
78
+ inverse verb.
99
79
 
100
80
  ## `mu doctor` reports drift
101
81
 
@@ -106,7 +86,7 @@ a write, the live table holds real work that a rebuild would discard.
106
86
  ```bash
107
87
  mu db backup /tmp/mu-drift-evidence.db # 1. keep the evidence
108
88
  mu rebuild /tmp/mu-rebuilt.db # 2. replay the log into a new file
109
- MU_DB_PATH=/tmp/mu-rebuilt.db mu sql "SELECT local_id, title FROM tasks" # 3. compare
89
+ MU_DB_PATH=/tmp/mu-rebuilt.db mu db query "SELECT local_id, title FROM tasks" # 3. compare
110
90
  ```
111
91
 
112
92
  Decide which side is right, then report the drift as a bug with the
@@ -117,22 +97,23 @@ command.
117
97
 
118
98
  | Row | Means |
119
99
  | --- | ----- |
120
- | `ws-rows` | a workspace row whose directory is gone; the next send fails in the VCS |
121
- | `ws-dirs` | a workspace directory with no row; it blocks the next `--workspace` spawn with that name |
122
- | `ws-empty` | a workstream directory with no checkouts |
100
+ | `ws-rows` | a worktree row whose directory is gone; a reused name trips over it. `mu agent close <agent>` forgets it |
101
+ | `ws-staging` | a `--worktree` spawn killed (SIGKILL, crash) before its pane: its placeholder row and maybe a half-made worktree. The next `mu agent spawn` in that workstream removes them |
102
+ | `ws-empty` | a workstream directory with no worktrees |
123
103
  | `db-copies` | stray `mu.db*` files that nothing reads, including the `pre-compact` / `pre-forget` backups |
124
104
  | `exports` | output left by the removed export verb |
125
105
  | `locks` | lock directories older than one hour, from a spawn or flush that died |
126
106
 
127
- To list workspace orphans with cleanup commands, run
128
- `mu workspace orphans -w auth`. An orphan has no row, so
129
- `mu workspace free` cannot remove it. Remove a git-backed orphan with
130
- `git worktree remove --force <path>` from the project root, and any
131
- other with `rm -rf <path>`.
107
+ The worktree registry outlives the agent, so a worktree whose agent was
108
+ reaped is still closed by name: `mu agent close <agent>` keeps unmerged
109
+ commits on a branch and refuses dirty work (`--discard` drops it). A
110
+ directory mu never registered has no row: inspect it, then remove it with
111
+ `git worktree remove --force <path>` from the project root, or `rm -rf
112
+ <path>` when it is not a worktree.
132
113
 
133
114
  ## Rename a workstream
134
115
 
135
- There is no rename verb, and `mu sql` refuses to change
116
+ There is no rename verb, and `mu db query` refuses to change
136
117
  `workstreams.name`. The name is part of the key that every op is filed
137
118
  under, so a raw rename leaves the ops log on the old name: `mu doctor`
138
119
  reports drift, and sync or `mu rebuild` brings the old name back.
@@ -142,7 +123,7 @@ Re-create the workstream instead:
142
123
  mu db backup /tmp/mu-before-rename.db
143
124
  mu workstream init auth-refactor
144
125
  # re-add the tasks you still need, with -w auth-refactor
145
- mu workstream teardown auth-refator --yes
126
+ mu workstream teardown auth # preview, then --if <rev>
146
127
  ```
147
128
 
148
129
  Names start with a lowercase letter, use letters, digits, `_`, or
@@ -1,74 +1,151 @@
1
1
  # How to run a worker on another machine
2
2
 
3
- The pane is local and the pi process is remote. mu runs no ssh
4
- itself. `mu agent remote-env` prints the ssh pieces, and you put them
5
- in the spawn command. The full recipe, with recording, collecting,
6
- and the traps, is in
3
+ The pane is local and the pi process is remote. mu never connects and
4
+ knows no ssh flags: a host profile holds your own command templates, and
5
+ `mu agent spawn --host` fills them. The full recipe, with collecting and
6
+ the traps, is in
7
7
  [skills/mu/recipes/remote-workers.md](../../skills/mu/recipes/remote-workers.md).
8
8
  Read it before your first remote spawn.
9
9
 
10
10
  ## Prepare the host
11
11
 
12
- On the remote host:
13
-
14
- 1. Install mu and run `mu link pi`, so the remote pi loads the mu
15
- extension.
12
+ 1. On the host, install pi and the mu package:
13
+ `pi install npm:@mu-crew/mu`. The host needs no mu DB: a remote worker
14
+ cannot run `mu task` verbs. It ends with a `CLOSE:` block. `mu wait`
15
+ applies it when it sees the run settle; `mu agent wait` does not. Re-run
16
+ the evidence first, or close the task yourself.
16
17
  2. Allow unix-socket forwards in the host's sshd:
17
18
  `AllowStreamLocalForwarding yes`.
18
- 3. Create the worker's checkout yourself. `--workspace` works only
19
- for local agents:
20
-
21
- ```bash
22
- ssh dev 'git -C ~/repo worktree add ~/ws/worker-1'
23
- ```
19
+ 3. Have a git repo there for `--worktree` to branch from. One profile serves
20
+ every repo on the host: `--max-sessions` caps the machine, so never add
21
+ the same machine twice.
24
22
 
25
23
  Use one orchestrator DB on your machine. Do not run a second mu on the
26
- host for the same work: task ownership points into your local
27
- `agents` table.
24
+ host for the same work: task ownership points into your local `agents`
25
+ table.
28
26
 
29
- ## Spawn the agent
27
+ ## Add a profile and check it
30
28
 
31
29
  ```bash
32
- eval "$(mu agent remote-env worker-1 -w auth --shell)" # sets MU_SSH_ARGS, MU_REMOTE_ENV
33
- mu agent spawn worker-1 -w auth --command \
34
- "ssh $MU_SSH_ARGS dev -t 'cd ~/ws/worker-1 && $MU_REMOTE_ENV \$SHELL -ilc \"pi --approve\"'"
30
+ mu host init dev --ssh # prints a starter `mu host add`; runs nothing
31
+ mu host add dev --shell '<pane cmd>' --exec '<run cmd>' --fetch '<fetch cmd>' --max-sessions 1
32
+ mu host check dev --root '~/repo' # host answers, repo, pi, mu extension
35
33
  ```
36
34
 
37
- What each part does:
35
+ ## Spawn the agent
38
36
 
39
- - `MU_SSH_ARGS` forwards the remote pi's control socket to the local
40
- path mu connects to. It includes `-o ControlMaster=no -o
41
- ControlPath=none`, because a multiplexed ssh connection never binds
42
- the local end of the forward.
43
- - `MU_REMOTE_ENV` sets the agent's identity inside the remote shell.
44
- tmux `-e` variables do not cross the ssh hop.
45
- - `$SHELL -ilc` starts pi in an interactive login shell, so the
46
- remote `~/.zshrc` or `~/.bashrc` loads PATH and provider keys.
47
- Without it, ssh's non-interactive shell skips them and pi can die at
48
- startup.
49
- - `pi --approve` skips pi's project-trust prompt. It trusts whatever
50
- directory the pane starts in, so use it only on your own code.
37
+ ```bash
38
+ mu agent spawn worker-1 -w auth --host dev --worktree --root '~/repo' --task build --send 'Read: mu task notes build -w auth'
39
+ ```
51
40
 
52
- If the spawn reports `ctl refused`, check `AllowStreamLocalForwarding`
53
- on the host.
41
+ - The pane runs the profile's `--shell` template, filled with the remote
42
+ identity (`MU_MANAGED_AGENT`, `MU_AGENT_NAME`, `MU_WORKSTREAM`,
43
+ `MU_CTL_SOCK`, `MU_REMOTE`, never your DB, state or sync dir, which are unset in the pane), your
44
+ `--command` (default `pi`), the local and remote socket paths and the
45
+ remote cwd. The template starts the remote pi and forwards its control
46
+ socket to the local derived path.
47
+ - `--worktree` makes a detached `git worktree add` in the remote repo
48
+ `--root` names (default: the profile's `--repo`; with neither the spawn
49
+ refuses) through its `--exec`, at
50
+ `~/.local/state/mu-next/remote-worktrees/<ws>/<name>` on the host. Its path,
51
+ base commit and the repo's absolute toplevel are the agent's `cwd`, `base`
52
+ and `repo`; close and recovery use that repo, never the profile's.
53
+ `--cwd` names an existing remote directory instead; without either, the
54
+ agent starts in `--root` (default the profile's repo), else the login
55
+ directory. Without a repo, templates get `.` for `{cwd}`. `--root` is part
56
+ of a `--task` attempt's identity: the same attempt in another repo conflicts.
57
+ - `--task` is the same one journaled attempt as for a local agent: claim
58
+ for the new agent, then the brief, never sent twice.
59
+ - mu reserves the host's channel before its first remote command and
60
+ keeps it while the pane lives (`--max-sessions` counts it). A full host
61
+ refuses the spawn up front and names the holder. A failed spawn removes
62
+ only the worktree it made (still clean, still at its base) and frees
63
+ the slot. If the host's answer was lost after it made the worktree (a
64
+ timeout), spawning the same name again takes that tree over while it is
65
+ clean, at its base and unused; anything else at the path refuses.
66
+ - The pane is the transport: closing it, or its death, ends the agent
67
+ and frees the slot. tmux only; herdr refuses `--host` in one line.
68
+ - A non-pi `--command` (`--host dev --command 'npm test'`, no `--cli pi`) is a
69
+ **command gate**, not a worker: its pane is kept after the command ends, and
70
+ `mu wait agent:auth/gate-1` returns the command's own exit status (the
71
+ `exit` of the result and of the process; 23 stays 23). Never a held
72
+ `ssh dev …`. The remote command writes a token-bound completion record that
73
+ mu reads once the pane has exited; no record, a signal or a lost transport is
74
+ exit 6, never 0. `mu agent close gate-1` frees the pane and removes that
75
+ record. No `--worktree`, no `--task`.
76
+ - If the spawn reports `ctl refused` or `missing`, run
77
+ `mu host check dev --agent worker-1 -w auth`, then check
78
+ `AllowStreamLocalForwarding` on the host.
54
79
 
55
80
  ## Drive it like a local agent
56
81
 
57
- Claim, `mu agent send --fresh`, `mu agent wait`, and `mu agent abort`
58
- all go through the forwarded socket and behave as they do locally:
82
+ `mu agent send`, `wait`, `abort` and `check` go through the forwarded
83
+ socket and behave as they do locally. `mu state` shows remote workers
84
+ as agent records with `host`, `cwd`, `base` and `collected_head`.
85
+
86
+ ## Collect and close
59
87
 
60
88
  ```bash
61
- mu task note build -w auth 'REMOTE: dev:~/ws/worker-1'
62
- mu task claim build -w auth --for worker-1
63
- mu agent send worker-1 -w auth --fresh 'Work on build. Read: mu task notes build -w auth'
89
+ cd ~/repo # the local repo the commits come home to
90
+ mu agent collect worker-1 -w auth # runs the profile's --fetch, prints the merge
91
+ git merge <sha> # mu prints the exact sha; it never merges
92
+ mu agent close worker-1 -w auth # preview, then --if <rev>
64
93
  ```
65
94
 
66
- The `REMOTE: <host>:<path>` note line is how `mu state` lists remote
67
- workers. Collect the work by fetching from the remote checkout:
95
+ - `collect` runs the `--fetch` template locally, in the current directory's
96
+ git repo, under one channel. It pins the fetched commit as
97
+ `refs/mu/collect/<ws>/<agent>` (gc keeps it) and records it as the
98
+ agent's `collected_head`. A fetch that fails, or succeeds without leaving a
99
+ commit, records nothing. Collecting the same HEAD again says so.
100
+ - The pane is the transport, so a live worker holds its host's channel. At
101
+ `--max-sessions 1`, `collect` is refused with the holder named; raise the
102
+ cap to collect while the worker runs.
103
+ - `close` kills the pane first (that frees the channel), then removes the
104
+ remote worktree **only if** it is clean and its HEAD is the base or the
105
+ collected HEAD, and no process on the host is inside it. Its `git worktree
106
+ remove` never uses git's `--force`. A process inside it, or a host with
107
+ neither /proc nor lsof to check, refuses naming `close --force`, which
108
+ removes past either with a warning. A newer HEAD refuses with one line:
109
+ `collect` again, or `close --keep-branch` to keep the commits on the remote
110
+ branch `mu/<ws>/<agent>-<sha12>` and remove the worktree. Uncommitted files
111
+ always refuse (`--discard` and `--keep-branch` do not override that).
112
+ - A `--cwd` agent has no remote worktree of its own: close never touches the
113
+ host. When a row is deleted (its pane died and the
114
+ reaper took it, a workstream teardown, a peer's workstream delete, even a raw
115
+ SQL delete) the database keeps a
116
+ machine-local `remote_recovery` record of the host, path and base (`mu sql
117
+ "select * from remote_recovery"`; `mu rebuild` loses it). A failed spawn whose
118
+ own rollback removed the tree (or found it gone) leaves none. With no agent row left,
119
+ `mu agent close <name> -w <ws>` (or `<ws>/<name>`) previews and closes THAT
120
+ worktree under the rule above (no pane, no task; the recorded host profile only), and
121
+ `mu agent collect <name>` fetches from it. Several records of one name need
122
+ `--recovery <id>`; a current agent of the name or path, a newer record of the same path, or a changed `--exec`, refuses (a changed default repo does not).
123
+
124
+ ## Host profiles
125
+
126
+ A host profile stores your own command templates, so you stop retyping
127
+ them. mu never connects and knows no ssh flags: it fills the templates and
128
+ runs them with `/bin/sh -c`. `spawn --host` uses them (above); `agent
129
+ collect` runs the `--fetch` template.
68
130
 
69
131
  ```bash
70
- git fetch "ssh://dev/~/ws/worker-1" HEAD && git cherry-pick FETCH_HEAD
132
+ mu host init dev --ssh # prints a starter `mu host add`; runs nothing
133
+ mu host add dev --shell '<pane cmd>' --exec '<run cmd>' --fetch '<fetch cmd>' --max-sessions 1
134
+ mu host check dev --root '~/repo' # host answers, repo, pi, mu extension
135
+ mu host show dev # templates and the holders of its channels
71
136
  ```
72
137
 
73
- `mu agent kick` does not reach a remote process. Use `mu agent abort`,
74
- or close the pane.
138
+ - Placeholders: `{env}` (the five `MU_*` identity variables only), `{cmd}`,
139
+ `{local_sock}`, `{remote_sock}`, `{cwd}`. Each value is one quoted word and
140
+ is exactly the text you passed, also inside your own `"..."` or `'...'`.
141
+ A template that hands command text to a second shell (ssh's remote login
142
+ shell) says so per placeholder: `{cmd:sh}`, `{cwd:sh}` quote the value for
143
+ that shell too. Write `{{` and `}}` for literal braces. Anything else in
144
+ braces is refused.
145
+ - A command that times out, or whose caller is killed, takes its whole process
146
+ group with it before its channel is freed, so `--max-sessions` never
147
+ undercounts a live transport.
148
+ - `--max-sessions N` counts every channel mu holds to the host. At the cap mu
149
+ refuses with one line naming the holder.
150
+ - `host check --agent <name> -w <ws>` also probes that agent's forwarded
151
+ control socket.