@mu-crew/mu 3.13.2 → 4.0.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-5HECDBK3.js +4482 -0
  4. package/dist/chunk-5HECDBK3.js.map +1 -0
  5. package/dist/chunk-AW2NONYU.js +301 -0
  6. package/dist/chunk-AW2NONYU.js.map +1 -0
  7. package/dist/chunk-GQNHIPJN.js +4060 -0
  8. package/dist/chunk-GQNHIPJN.js.map +1 -0
  9. package/dist/{chunk-3BARPJLY.js → chunk-PZ5GOCWH.js} +16 -2
  10. package/dist/chunk-PZ5GOCWH.js.map +1 -0
  11. package/dist/chunk-S424D5EP.js +13766 -0
  12. package/dist/chunk-S424D5EP.js.map +1 -0
  13. package/dist/{cli-CKOMHBXV.js → cli-GB2CAVVW.js} +6776 -4350
  14. package/dist/cli-GB2CAVVW.js.map +1 -0
  15. package/dist/cli.js +2 -3
  16. package/dist/cli.js.map +1 -1
  17. package/dist/errors-KHOYKD7D.js +65 -0
  18. package/dist/extension/mu-pi.js +1617 -836
  19. package/dist/extension/mu-pi.js.map +1 -1
  20. package/dist/index.d.ts +4015 -2561
  21. package/dist/index.js +614 -297
  22. package/dist/index.js.map +1 -1
  23. package/dist/tui-ALPHYTA2.js +1773 -0
  24. package/dist/tui-ALPHYTA2.js.map +1 -0
  25. package/docs/ARCHITECTURE.md +117 -66
  26. package/docs/VOCABULARY.md +112 -49
  27. package/docs/guide/README.md +3 -3
  28. package/docs/guide/backends.md +8 -4
  29. package/docs/guide/cleanup.md +61 -33
  30. package/docs/guide/delegate.md +141 -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 +115 -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 +23 -32
  41. package/docs/reference/naming.md +12 -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 +16 -318
  63. package/skills/mu/recipes/tasks-or-calls.md +15 -78
  64. package/skills/mu/recipes/test-health.md +19 -69
  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-KHOYKD7D.js.map} +0 -0
package/docs/guide/sql.md CHANGED
@@ -1,36 +1,62 @@
1
1
  # How to query and script mu
2
2
 
3
- Use typed verbs with `--json` first. Use `mu sql` for ad-hoc joins,
4
- reports, and repairs that no verb covers.
3
+ Use typed verbs with `--json` first. Use `mu db query` (alias `mu sql`) for
4
+ ad-hoc joins and reports that no verb covers; a repair needs `--write`.
5
5
 
6
6
  ## Script with `--json`
7
7
 
8
8
  Every verb takes `--json`.
9
9
 
10
- - Collection reads print `{"items": [...], "count": N}`.
11
- - Single objects, such as `mu task show`, print an object with named
12
- fields.
13
- - `mu sql --json` prints a bare array of rows.
14
- - `mu log --tail --json` prints one JSON object per line.
15
- - Errors go to stderr as `{error, message, nextSteps, exitCode}`.
10
+ - Every verb prints ONE document on stdout: `{"ok": true, "data": ..., "next": null}`.
11
+ - Collection reads put `{"items": [...], "count": N}` in `data`. The task
12
+ reads (`task list/next/owned-by/show/tree`, `me tasks`) cap the result at 4 KB:
13
+ `more` counts what is left and `retrieve` is the same command with `--all`
14
+ (`--full` on `owned-by`, whose `--all` means every workstream), which prints
15
+ everything whole. Task items use the SQL column names (`id` is the `local_id`,
16
+ `effort_days`, `workstream_id`, `owner_id`; `owner` only while
17
+ IN_PROGRESS), never `name`/`effortDays`/`roi`. `task add --json` returns its
18
+ `task` (and `tasks`) in the same shape.
19
+ - Single objects, such as `mu task show`, put an object with named
20
+ fields in `data`.
21
+ - `mu db query` and `mu sql` print this document by default (`--table` gives
22
+ the human table). A read puts `{items, count, more}` in `data`, the same
23
+ shape as task lists, with your row keys untouched. It stops at 4 KB of final
24
+ output, cells never cut: `more` counts the rows left out and `retrieve` is the
25
+ same query with `--all`, which prints every row whole. One row bigger than the
26
+ cap gives zero `items`, `more: 1` and `retrieve`. A write prints
27
+ `{changes, lastInsertRowid}` (a script `{statements, multiStatement}`); an
28
+ `UPDATE ... RETURNING` prints its rows whole, with no `retrieve`, since running
29
+ it again would write again. Errors are the failure document on stdout, with or
30
+ without `--json`.
31
+ - `mu state` and bare `mu me` print the document by default too (no `--json` needed); a read over 4 KB is cut and names `mu state ... --full`.
32
+ - `mu log --tail` (no `--json` needed) is the exception: one raw JSON object per line, keyed by the `ops` column names.
33
+ - A failure is `{"ok": false, "error", "message", "next", "exit"}` on
34
+ **stdout** (plus, for usage errors, `usage`, at most 3 lines), with
35
+ the same non-zero exit code. `2>/dev/null` loses nothing.
16
36
 
17
37
  ```bash
18
- mu state -w auth --json
19
- mu task list -w auth --json | jq '.count'
38
+ mu state -w auth
39
+ mu task list -w auth --json | jq '.data.count'
40
+ mu task show t1 -w auth --json | jq 'if .ok then .data.task else .message end'
20
41
  ```
21
42
 
22
- Exit codes: `2` usage error, `3` not found, `4` conflict, `5`
23
- timeout, a locked DB, or a multiplexer, VCS, drift, or `mu doctor`
24
- FAIL-row failure, `6` reaped pane, `7` stall.
43
+ Exit codes: `1` any other failure (a spawn that failed, an unreachable
44
+ control socket, a send whose delivery is unknown), `2` usage error, `3` not found, `4` conflict, `5`
45
+ an abort or interrupt that timed out (pi still busy), a locked DB, or a multiplexer, VCS, drift, or `mu doctor`
46
+ FAIL-row failure, `6` a watched owner died, `7` stall, `130`/`143`
47
+ SIGINT/SIGTERM during a wait. A wait that hits its cap
48
+ (`mu wait`, `mu task wait`, `mu agent wait`) is `0` with `timedOut: true`, not `5`.
25
49
 
26
50
  ## Read the schema
27
51
 
28
- The schema has 12 tables and 3 views (`ready`, `blocked`, `goals`).
52
+ The schema (version 13) has 26 tables and 4 views (`ready`, `blocked`,
53
+ `blockers`, `goals`). Views skip torn-down workstreams. `blockers` has one
54
+ row per open edge: `task_local_id` is held up by `blocker_local_id`.
29
55
  Four tables travel between machines: `workstreams`, `tasks`,
30
56
  `task_edges`, and `task_notes`. List them all:
31
57
 
32
58
  ```bash
33
- mu sql "SELECT type, name FROM sqlite_master WHERE type IN ('table','view') ORDER BY type, name"
59
+ mu db query "SELECT type, name FROM sqlite_master WHERE type IN ('table','view') ORDER BY type, name"
34
60
  ```
35
61
 
36
62
  Task ids (`local_id`) are unique per workstream only. Join
@@ -42,21 +68,21 @@ Edges and notes reference `tasks.id`, not `local_id`.
42
68
  Blocked tasks and the goals view, which replaced removed verbs:
43
69
 
44
70
  ```bash
45
- mu sql "SELECT b.local_id, b.title FROM blocked b JOIN workstreams w ON w.id = b.workstream_id WHERE w.name = 'auth'"
46
- mu sql "SELECT g.local_id, g.title FROM goals g JOIN workstreams w ON w.id = g.workstream_id WHERE w.name = 'auth'"
71
+ mu db query "SELECT b.local_id, b.title FROM blocked b JOIN workstreams w ON w.id = b.workstream_id WHERE w.name = 'auth'"
72
+ mu db query "SELECT g.local_id, g.title FROM goals g JOIN workstreams w ON w.id = g.workstream_id WHERE w.name = 'auth'"
47
73
  ```
48
74
 
49
75
  Search task titles:
50
76
 
51
77
  ```bash
52
- mu sql "SELECT t.local_id, t.status, t.title FROM tasks t JOIN workstreams w ON w.id = t.workstream_id
78
+ mu db query "SELECT t.local_id, t.status, t.title FROM tasks t JOIN workstreams w ON w.id = t.workstream_id
53
79
  WHERE w.name = 'auth' AND LOWER(t.title) LIKE '%token%'"
54
80
  ```
55
81
 
56
82
  Every task that blocks `launch`, directly or not:
57
83
 
58
84
  ```bash
59
- mu sql "WITH RECURSIVE prereqs(id) AS (
85
+ mu db query "WITH RECURSIVE prereqs(id) AS (
60
86
  SELECT t.id FROM tasks t JOIN workstreams w ON w.id = t.workstream_id
61
87
  WHERE t.local_id = 'launch' AND w.name = 'auth'
62
88
  UNION
@@ -65,15 +91,69 @@ mu sql "WITH RECURSIVE prereqs(id) AS (
65
91
  SELECT t.local_id, t.title, t.status FROM prereqs JOIN tasks t ON t.id = prereqs.id"
66
92
  ```
67
93
 
68
- `mu sql` runs writes too and prints the number of rows changed. Writes
69
- are captured as ops under one `sql.write` group per call, so `mu undo`
70
- reverts the whole call. `--confirm-rows N` counts rows removed by
94
+ `mu db query` syncs like every other verb (when `MU_SYNC_DIR` is set): it
95
+ ingests peer ops before the query and flushes its own writes after, even
96
+ when the statement fails. It reads by default: a statement SQLite reports as
97
+ not read-only (`INSERT`, `UPDATE`, `DELETE`, `REPLACE`, `WITH ... INSERT`,
98
+ anything with `RETURNING`) is refused in one line, exit 2, until you add
99
+ `--write`. A write prints the number of rows changed. Writes
100
+ are captured as ops under one `sql.write` group per call, so `mu log --group`
101
+ shows the whole call. `--confirm-rows N` counts rows removed by
71
102
  `ON DELETE CASCADE` too: deleting a task with one edge counts 2.
72
103
 
73
- `mu sql` refuses to change a natural-key column (`workstreams.name`,
104
+ With `-w <ws>` (a spawned worker defaults to its own), the views `ready`,
105
+ `blocked` and `blockers` cover that workstream only, with the same columns;
106
+ `main.ready` is still the global one. The scope is the connection's own
107
+ (`TEMP` views), so nothing is left in the file.
108
+
109
+ `no such column: x` answers in one line with the closest columns and the real
110
+ columns of every table the query names; the SQL is never rewritten.
111
+
112
+ The whole call runs in one transaction that mu opens, so `BEGIN`,
113
+ `COMMIT`, `END`, `ROLLBACK`, `SAVEPOINT` and `RELEASE` are refused, each
114
+ statement checked by SQLite itself (a table or column named `begin` or
115
+ `end` is fine, a leading `;` hides nothing); a failed statement rolls the call
116
+ back. An `INSERT` that names an `id` of 0 or below is refused too (omit it), and
117
+ so is an `UPDATE` of `id` (or `rowid`) on `workstreams`, `tasks`, `task_notes`
118
+ and `task_edges`: ids are set once. Notes are append-only: an `UPDATE` of a
119
+ note's `author`, `content`, `created_at` or `kind` is refused (add a new
120
+ note), and so is an `UPDATE` of an edge's `created_at`. A rebuild or peer
121
+ would replay either as a different row than the one you edited.
122
+
123
+ What it will not touch: mu's capture bookkeeping, the schema and the
124
+ connection. The rule is a policy, so nothing has to be detected:
125
+
126
+ - No `CREATE`, `DROP` or `ALTER` of any kind, `TEMP` tables, views and
127
+ triggers included, and no `ANALYZE` or `REINDEX`. A trigger that outlives
128
+ one statement can fire inside mu's own bookkeeping.
129
+ - No `ATTACH` or `DETACH`: the verb works on this one DB. Another mu DB
130
+ (a backup, a peer) is not for it to write.
131
+ - `PRAGMA` only as a read: `table_info`, `table_xinfo`, `table_list`,
132
+ `index_list`, `index_info`, `index_xinfo`, `foreign_key_list`,
133
+ `foreign_key_check`, `integrity_check`, `quick_check`, and a bare
134
+ `user_version`, `foreign_keys`, `busy_timeout` or `database_list`.
135
+ Anything else is refused before it runs.
136
+ - No write to `op_ctx`, `op_dying`, `ops`, `schema_version`, `machine_identity`,
137
+ `sqlite_sequence` (the AUTOINCREMENT counters; inserts still advance them) or
138
+ `sync_peers` (sync watermarks: `mu sync --repair <peer>` re-reads a peer).
139
+
140
+ SQLite itself decides what a statement does (its `EXPLAIN` listing, trigger
141
+ bodies included), never the text, so a table named `begin` is fine and a
142
+ leading `;` hides nothing.
143
+
144
+ Need DDL? `mu db backup`, edit the copy with `sqlite3` yourself, and leave the
145
+ live DB to mu's verbs. `mu db forget` shrinks it.
146
+
147
+ `mu db --shell [-w <ws>]` opens the `sqlite3` CLI on the same file,
148
+ read-only and in safe mode, with the same scoped views. Plain `sqlite3`
149
+ starts with `foreign_keys=0` and no capture context: use it to look, never to write.
150
+
151
+ The whole-file commands stay under `mu db`: `backup`, `compact`, `forget`.
152
+
153
+ `mu db query` refuses to change a natural-key column (`workstreams.name`,
74
154
  `tasks.workstream_id`, `tasks.local_id`, a note's task, or an edge's
75
155
  endpoints). The ops log files every change under that key, so changing
76
- it would desync sync, undo, and `mu doctor`. Add a new row instead.
156
+ it would desync sync and `mu doctor`. Add a new row instead.
77
157
 
78
158
  ## Pass list flags
79
159
 
@@ -1,6 +1,6 @@
1
1
  # How to stop a worker
2
2
 
3
- Use the gentlest step that works, in this order: abort, kick, close.
3
+ Use the gentlest step that works, in this order: abort, close.
4
4
 
5
5
  ## Abort the turn (pi agents)
6
6
 
@@ -14,53 +14,46 @@ keeps the session and its context. It works for remote agents too.
14
14
 
15
15
  - An idle agent is left alone (exit 0).
16
16
  - Exit 5 means pi was still busy after `--timeout` (default 30 s).
17
- Go to the next step.
17
+ Close the agent.
18
18
  - A follow-up queued before the abort does not run. pi puts it back
19
19
  into the pane's editor, unsent. Send it again if you still want it.
20
20
 
21
21
  To give the agent new work after the abort, send with `--fresh`. See
22
22
  [Dispatch work to a worker](dispatch.md#pick-the-send-mode).
23
23
 
24
- ## Kick the foreground process
25
-
26
- Use `mu agent kick` when abort failed, when the agent is not pi, or
27
- when pi's socket is gone:
28
-
29
- ```bash
30
- mu agent kick worker-1 -w auth # SIGINT
31
- mu agent kick worker-1 -w auth --signal SIGTERM
32
- mu agent kick worker-1 -w auth --signal SIGKILL
33
- ```
34
-
35
- mu finds the foreground process group on the pane's TTY and signals
36
- it. This is the fix for a worker stuck on an unbounded `find /` or a
37
- busy-wait loop. Typing Ctrl-C into the pane does not work, because
38
- the agent CLI catches it as input.
39
-
40
- - Kick refuses with `NoForegroundProcessError` when the foreground
41
- process is the agent CLI itself. Close the agent instead.
42
- - Kick works on local panes only. On herdr it works on Linux only.
43
-
44
- To prevent the problem, scope worker prompts to the workspace. Do not
45
- ask for filesystem-wide `find` or `grep -r /`.
46
-
47
24
  ## Close the agent
48
25
 
49
26
  ```bash
50
- mu agent close worker-1 -w auth
27
+ mu agent close worker-1 -w auth # preview: writes and kills nothing, prints rev=<n>
28
+ mu agent close worker-1 -w auth --if <n> # apply that preview
51
29
  ```
52
30
 
53
- Close kills the pane and deletes the agent row. A clean workspace is
54
- freed with it. If the workspace has edits or commits, close refuses
55
- with exit 4. Then do one of these:
56
-
57
- - Run `mu workspace free worker-1 -w auth --commit` to commit pending
58
- changes and free the workspace, then close. For git, if the commits
59
- are on no branch, mu keeps them on a new branch
60
- `mu/<workstream>/<agent>-<sha>`.
61
- - Run `mu agent close worker-1 -w auth --discard-workspace` to free
62
- and close in one step. You lose the changes.
31
+ Bare close only previews: it names the pane, the tasks the agent owns
32
+ (an `IN_PROGRESS` one goes back to `OPEN`), the worktree's state and
33
+ the `--if` command that applies it. Pass `--if <n>` to close. It
34
+ refuses (exit 4, nothing killed) when the agent changed since the
35
+ preview: a task claimed or released, a respawn under the same name.
36
+ Another `mu` closing or spawning the same name makes it exit 4 too
37
+ (`AgentLockBusyError`; nothing was done, retry). If the agent was
38
+ replaced while the close was running, the replacement is left alone
39
+ (`AgentReplacedError`).
40
+
41
+ Close kills the pane and deletes the agent row. It is also the step
42
+ for a non-pi agent, which has no socket to abort through. To prevent a
43
+ worker stuck on an unbounded `find /` or a busy-wait loop, scope worker
44
+ prompts to the worktree. Do not ask for filesystem-wide `find` or
45
+ `grep -r /`. A clean worktree is removed with it, and unmerged commits
46
+ are kept on a new branch `mu/<workstream>/<agent>-<sha>` that the close
47
+ prints. If the worktree has uncommitted edits, close refuses with exit 4
48
+ before it kills the pane, and the preview offers no apply command. Then
49
+ do one of these:
50
+
51
+ - Commit the edits in the worktree (`git -C <cwd> add -A && git -C <cwd>
52
+ commit`), then preview and close again.
53
+ - Preview with `--discard` (`mu agent close worker-1 -w auth --discard`),
54
+ then apply it with `--if <n>` to drop the edits in one step. You lose
55
+ them, and no branch is made.
63
56
 
64
57
  If the worker owned a task, its pane is gone, so the reaper returns
65
- the task to `OPEN` on the next reconcile. A `mu task wait` on that task
58
+ the task to `OPEN` on the next reconcile. A `mu wait` on that task
66
59
  exits 6.
@@ -18,12 +18,20 @@ mu task add auth_fix -w app -t "Fix the auth redirect" -i 80 -e 2
18
18
  mu task close auth_fix -w app --evidence "shipped in #412"
19
19
  ```
20
20
 
21
+ ## Keep 3.x out of the sync folder
22
+
23
+ 4.0 writes v2 segments, which 3.x warns about on every command. So 4.0
24
+ refuses a `MU_SYNC_DIR` where any segment opens with a v1 line (exit 4,
25
+ nothing read or written): `MU_SYNC_DIR holds 3.x segments; give mu 4
26
+ its own dir`. Point it at a new folder.
27
+
21
28
  ## Keep the DB out of the sync folder
22
29
 
23
30
  Never put `MU_DB_PATH` inside `MU_SYNC_DIR`. A live SQLite DB is three
24
31
  files (`mu.db`, `-wal`, `-shm`). A file syncer copies them out of
25
32
  order and can bring back a stale `-wal`, which corrupts the DB with no
26
- error. `mu doctor` fails the `db-vs-sync` row when you do this.
33
+ error. Every `mu` verb refuses to run (exit 4, before it opens or
34
+ writes anything) when the DB path is inside `MU_SYNC_DIR`.
27
35
 
28
36
  Also keep the DB off NFS, SMB, and sshfs. `mu doctor` warns in the
29
37
  `db-filesystem` row.
@@ -60,7 +68,7 @@ it, and `behind`: lines you hold but have not applied. A non-zero
60
68
 
61
69
  ```bash
62
70
  mu sync --repair c8ec # re-read a peer's file from the start
63
- mu sync --from /mnt/devserver/.local/state/mu/mu.db # read a peer's DB directly
71
+ mu sync --from /mnt/devserver/.local/state/mu-next/mu.db # read a peer's DB directly
64
72
  MU_SYNC_DIR=/media/usb-stick mu state # one-off folder
65
73
  ```
66
74
 
@@ -68,7 +76,7 @@ Ingest is idempotent, so `--repair` is safe at any time.
68
76
 
69
77
  ## Know what travels
70
78
 
71
- Workstreams, tasks, edges, and notes travel. Agents and workspaces
79
+ Workstreams, tasks, edges, and notes travel. Agents and worktrees
72
80
  stay on their machine, because a pane id and a local path mean nothing
73
81
  elsewhere. So task ownership does not sync: a task claimed on the
74
82
  devserver shows no owner on your laptop.
@@ -1,25 +1,43 @@
1
1
  # How to upgrade mu
2
2
 
3
+ Coming from 3.x? Read [Upgrade from 3.x to 4.0](#upgrade-from-3x-to-40)
4
+ first: `npm i -g @mu-crew/mu@latest` replaces your 3.x `mu`.
5
+
3
6
  ## Upgrade the package
4
7
 
5
8
  ```bash
6
9
  npm i -g @mu-crew/mu@latest
7
- mu link pi
10
+ pi update --extensions # only if the package is already registered in pi
8
11
  mu doctor
9
12
  ```
10
13
 
11
- `mu link pi` installs a one-line shim at
12
- `~/.pi/agent/extensions/mu.ts` that loads the installed extension, and
13
- links `~/.agents/skills/mu` to the package's skill. Later upgrades
14
- update both with no relink. `MU_PI_HOME` changes the home directory
15
- that both paths start from.
14
+ pi loads mu as a package: one extension, one skill, and the recipe
15
+ prompts, all from the declared `pi` manifest. There is no relink step.
16
+
17
+ ### Coming from the old `link pi` install
18
+
19
+ Before the package, the old `link pi` verb wrote a shim at
20
+ `~/.pi/agent/extensions/mu.ts` and a skill link at
21
+ `~/.agents/skills/mu`. `pi update` registers nothing for that install,
22
+ and a shim left in place loads a second copy of the extension next to
23
+ the package. In this order:
24
+
25
+ ```bash
26
+ npm i -g @mu-crew/mu@latest
27
+ pi install npm:@mu-crew/mu # once: registers the package
28
+ mu pi migrate # report only
29
+ mu pi migrate --apply # move mu-owned files aside; nothing is deleted
30
+ ```
31
+
32
+ Then restart pi agents or send them `/reload`.
16
33
 
17
- - `--copy` copies the extension instead of the shim. You then have to
18
- relink after every upgrade.
19
- - `--force` replaces an extension or skill symlink that points
20
- elsewhere, such as a dev checkout. mu replaces the link, never its
21
- target, and never replaces a real skill directory (exit 4).
22
- - `--extension-only` and `--skill-only` install one part.
34
+ Only a file mu can prove it generated is moved, into a `disabled/`
35
+ folder pi does not read: a file byte for byte equal to the shim `link pi` generated (only its
36
+ generated import URL may differ), or a skill
37
+ link into this install's `skills/mu` (or a skill copy identical to a
38
+ released one). Anything else (your own `mu.ts`, an edited copy, a link
39
+ to somewhere else, even one named `mu`) is reported with its path and
40
+ left for you. `MU_PI_HOME` changes the home directory both start from.
23
41
 
24
42
  For agents other than pi, see [README § Install](../../README.md#install).
25
43
 
@@ -29,46 +47,78 @@ A running pi keeps the extension it loaded at start. After an upgrade,
29
47
  send `/reload` to each agent, or respawn the agent. From a shell:
30
48
 
31
49
  ```bash
32
- mu agent send worker-1 -w auth '/reload' # extension serves op command
33
- mu agent send worker-1 -w auth '/reload' --via mux # older extension
50
+ mu agent send worker-1 -w auth '/reload' # extension serves op command; older: respawn
34
51
  ```
35
52
 
36
53
  Until then, the `ctl` row of `mu doctor` warns
37
54
  `extension X older than installed Y` or `extension lacks ops`. Ops the old extension lacks,
38
55
  such as `send --fresh`, fail with exit 4.
39
56
 
40
- ## Upgrade from 3.1 or earlier
41
-
42
- Before 3.2.0, mu pasted text into pi panes. Now it sends through a
43
- control socket. Agents started before `mu link pi` have no socket, so
44
- sends to them fail with `ctl missing` instead of pasting. Restart those
45
- agents, or force the old path with `--via mux`.
57
+ An agent started before the pi package was installed has no control
58
+ socket, so a send to it fails with `ctl missing`. Restart it.
59
+
60
+ ## Upgrade from 3.x to 4.0
61
+
62
+ 4.0 is a breaking release with its own DB. It does not import a 3.x DB,
63
+ and nothing converts one. 3.x keeps its state in `~/.local/state/mu`
64
+ (`$XDG_STATE_HOME/mu`); 4.0 keeps its state in `~/.local/state/mu-next`
65
+ (`$XDG_STATE_HOME/mu-next`), a name kept so the two never share a DB,
66
+ socket or worktree. The release notes list every breaking change:
67
+ [CHANGELOG](../../CHANGELOG.md).
68
+
69
+ There is no flag day. Each machine switches when its human decides:
70
+
71
+ 1. Close the 3.x agents you no longer need (`mu agent close`); 4.0 cannot
72
+ see them.
73
+ 2. If you still need the old DB, [keep 3.x runnable](#keep-3x-runnable)
74
+ before you install: `npm i -g @mu-crew/mu@latest` replaces the `mu` on
75
+ your `PATH`.
76
+ 3. Install 4.0 as above (`npm i -g @mu-crew/mu@latest`, `pi install`,
77
+ `mu pi migrate --apply`). `mu --version` should print `4.x`.
78
+ 4. Give 4.0 its own sync folder, or none. Sync stays off until you set
79
+ `MU_SYNC_DIR`. If any segment in the folder opens with a 3.x (v1)
80
+ line, every 4.0 verb exits 4 with `MU_SYNC_DIR holds 3.x segments;
81
+ give mu 4 its own dir` before it reads or writes anything. Unset
82
+ or change `MU_SYNC_DIR` in a shell that also runs 3.x.
83
+ 5. Move workstreams one at a time, from the machine that holds each one,
84
+ with the [Move recipe](../../skills/mu/recipes/move.md). Only open
85
+ tasks move; owners, agents and history stay in the 3.x DB, an archive.
86
+ The recipe's one 3.x write is the last step: it parks each moved task
87
+ with `--why "moved to 4.0"`, so nobody works it twice.
88
+
89
+ Rollback: `pi remove npm:@mu-crew/mu`, `npm i -g @mu-crew/mu@3`, then
90
+ 3.x's `link pi` to relink its shim. Nothing needs converting. For each
91
+ moved workstream, run `mu task unpark <id> -w <ws>` on 3.x per task. Work
92
+ done in 4.0 since the move stays in 4.0's DB.
93
+
94
+ ## Keep 3.x runnable
95
+
96
+ `npm i -g @mu-crew/mu@latest` replaces the `mu` on your `PATH`, so the
97
+ Move recipe needs 3.x from somewhere else. Install it into its own
98
+ prefix, or run it once with npx:
46
99
 
47
- ## Migrate an old DB
100
+ ```bash
101
+ npm i --prefix ~/.local/share/mu3 @mu-crew/mu@3.13.3
102
+ ~/.local/share/mu3/node_modules/.bin/mu --version # 3.13.3
48
103
 
49
- mu refuses a DB older than schema v11 with `SchemaTooOldError`, and a
50
- newer one with `SchemaTooNewError`. Both exit 4 and leave the file
51
- alone. `scripts/migrate.ts` converts a v7, v8, v9, or v10 DB into a new
52
- v11 file. It never writes in place.
104
+ npx -y @mu-crew/mu@3.13.3 --version # no install; slower to start
105
+ ```
53
106
 
54
- The npm package does not ship `scripts/` or `src/`, so run the recipe
55
- from a git checkout of mu at the version you upgraded to (`npm install`
56
- there first; `npx tsx` fetches tsx on first use).
107
+ Use that path, or the `npx` command, as `<mu3>` in the recipe. 3.x reads its own state dir, so
108
+ it finds the old DB with no flags. Use it to read the archive and to park
109
+ moved tasks, not for new work: `mu pi migrate --apply` moved aside the
110
+ shim 3.x's pi agents load. A harness that runs `mu` through your shell rc
111
+ may pick the wrong one; see
112
+ [Pin the mu the harness runs](other-agent-clis.md#pin-the-mu-the-harness-runs).
57
113
 
58
- ```bash
59
- git clone --branch "v$(mu --version)" https://github.com/mu-crew/mu && cd mu && npm install
60
- DB=${MU_DB_PATH:-$HOME/.local/state/mu/mu.db}
61
- BACKUP="$HOME/mu-old-backup-$(date +%Y%m%d-%H%M%S).db"
62
- sqlite3 "$DB" ".backup '$BACKUP'"
63
- npx tsx scripts/migrate.ts "$BACKUP" --out "${DB}.v11"
64
- MU_DB_PATH="${DB}.v11" mu doctor --deep
65
- mv "$DB" "${DB}.old-kept" && mv "${DB}.v11" "$DB"
66
- mu doctor
67
- ```
114
+ ## Old DBs
68
115
 
69
- Legacy `REJECTED` tasks become `CLOSED/rejected`, and `DEFERRED` tasks
70
- become `OPEN/parked`. Flags and the rules for what carries over are in
71
- [scripts/README.md](../../scripts/README.md).
116
+ A v13 DB missing a later addition is completed on its first open, in one
117
+ transaction: it either completes or leaves the file as it was. mu
118
+ refuses a DB older than v13 with `SchemaTooOldError`, and a newer one
119
+ with `SchemaTooNewError`. Both exit 4 and leave the file alone, so
120
+ pointing 4.0 at a 3.x DB (`MU_DB_PATH`) is safe. Back up an old DB and
121
+ move it aside.
72
122
 
73
123
  ## Run mu from a checkout
74
124
 
@@ -78,5 +128,5 @@ alias mu="node $PWD/dist/cli.js"
78
128
  ```
79
129
 
80
130
  Or run `npm install -g .` in the checkout. Its `prepare` script builds
81
- first. Run `mu link pi --force` to point the skill link at the
82
- checkout.
131
+ first. To load the checkout as the pi package instead of the installed
132
+ one, run `pi install /path/to/checkout`.
@@ -10,11 +10,10 @@ milliseconds; `0` disables a wait unless noted.
10
10
  | Name | Effect | Default |
11
11
  | --- | --- | --- |
12
12
  | `MU_DB_PATH` | SQLite file path. Wins over the state dir. | `<state-dir>/mu.db` |
13
- | `MU_STATE_DIR` | State directory. Also holds `compile-cache/` (V8 code cache for the CLI; `NODE_DISABLE_COMPILE_CACHE=1` turns it off). | `$XDG_STATE_HOME/mu` |
14
- | `XDG_STATE_HOME` | XDG state base; mu appends `mu/`. An empty or relative value is ignored. | `~/.local/state` |
13
+ | `MU_STATE_DIR` | State directory. Also holds `compile-cache/` (V8 code cache for the CLI; `NODE_DISABLE_COMPILE_CACHE=1` turns it off). | `$XDG_STATE_HOME/mu-next` |
14
+ | `XDG_STATE_HOME` | XDG state base; mu appends `mu-next/` (3.x owns `mu/`, so the two builds never share a DB). An empty or relative value is ignored. | `~/.local/state` |
15
15
  | `XDG_CACHE_HOME` | XDG cache base; mu appends `mu/`. Holds only disposable copies. An empty or relative value is ignored. | `~/.cache` |
16
- | `MU_SESSION` | Active workstream when `-w` is absent. Beats `MU_WORKSTREAM`. | current `mu-<name>` mux session |
17
- | `MU_SYNC_DIR` | Folder holding one segment per machine. This is the whole sync setup; there is no peer list. | unset: sync off |
16
+ | `MU_SYNC_DIR` | Folder holding one segment per machine. This is the whole sync setup; there is no peer list. 4.0 refuses (exit 4, nothing read or written) a folder holding a segment that opens with a v1 line, i.e. a 3.x peer: give it its own folder. | unset: sync off |
18
17
 
19
18
  ## Multiplexer
20
19
 
@@ -22,12 +21,11 @@ milliseconds; `0` disables a wait unless noted.
22
21
  | --- | --- | --- |
23
22
  | `MU_MUX` | Force `tmux` or `herdr`, skipping mux detection. An unknown value fails the invocation. | detected |
24
23
  | `HERDR_ENV` | Read only. `1` (set by herdr in its panes) selects herdr; outranks `$TMUX`. | |
25
- | `TMUX`, `TMUX_PANE` | Read only. Either one (set by tmux in its panes) selects tmux. `$TMUX` gates reading the current session name, which resolves the workstream when `-w` and `MU_SESSION` are absent. `$TMUX_PANE` identifies the calling agent (`mu me`, `mu log`, task claim errors). | |
24
+ | `TMUX`, `TMUX_PANE` | Read only. Either one (set by tmux in its panes) selects tmux. `$TMUX` gates reading the current session name, used only as the suggestion in the missing-`-w` error: it never routes a verb. `mu me` / `mu state --me` never read `$TMUX_PANE` (identity is the injected `MU_AGENT_NAME` or `PI_SESSION_ID`); only `mu task list --mine` and a few older self-readers still fall back to it when no `MU_AGENT_NAME` is injected. | |
26
25
  | `HERDR_WORKSPACE_ID` | Read only. Set by herdr in its panes; pane listing with no target reads this workspace first. | |
27
- | `HERDR_PANE_ID` | Read only. Set by herdr in its panes; fallback rung of actor identity after `MU_AGENT_NAME`. | |
28
- | `MU_TMUX_SOCKET` | Test isolation. Runs every tmux call on a private server (`-L <name>`) with `-f /dev/null`, so `~/.tmux.conf` is not loaded. tmux only. | unset: tmux's default server |
26
+ | `HERDR_PANE_ID` | Read only. Set by herdr in its panes. | |
27
+ | `MU_TMUX_SOCKET` | Run every tmux call on a private server (`-L <name>`) with `-f /dev/null`, so `~/.tmux.conf` is not loaded. The test suite uses it, and so can you to keep a crew off your own tmux server (a demo, a smoke test). Attach lines mu prints then carry `-L <name>`, and the dashboard's `a` nests a client on purpose when you are inside another tmux. tmux only. | unset: tmux's default server |
29
28
  | `MU_HERDR_SESSION` | Named herdr server (`--session <name>`). herdr only. Tests must set it to a non-default name. | herdr default session |
30
- | `MU_BANNER_QUIET` | `1` disables mu's pane border and banner decorations. tmux only. | unset |
31
29
 
32
30
  ## Spawn
33
31
 
@@ -37,16 +35,7 @@ milliseconds; `0` disables a wait unless noted.
37
35
  | `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` |
38
36
  | `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` |
39
37
  | `MU_SPAWN_LOCK_TIMEOUT_MS` | How long a spawn waits for the per-workstream spawn lock. | `15000` |
40
-
41
- ## Send and state (tmux path)
42
-
43
- These apply to the tmux paste path only. For when that path runs, see
44
- [No silent fallback](../architecture/control-socket.md#no-silent-fallback).
45
-
46
- | Name | Effect | Default |
47
- | --- | --- | --- |
48
- | `MU_SEND_READINESS_MS` | Before pasting, wait out a pane's modal or re-init; after Enter, confirm it landed. `0` sends fire-and-forget. A busy pane is not waited on. | `15000` |
49
- | `MU_SEND_DELAY_MS` | Delay between the bracketed paste and Enter. | `500` |
38
+ | `MU_AGENT_LOCK_TIMEOUT_MS` | How long `agent close` and `--worktree` staging wait for the per-agent lock before exit 4 (`AgentLockBusyError`, nothing done). | `15000` |
50
39
 
51
40
  ## Agents and timing
52
41
 
@@ -60,36 +49,38 @@ These apply to the tmux paste path only. For when that path runs, see
60
49
  | Name | Value |
61
50
  | --- | --- |
62
51
  | `MU_AGENT_NAME` | The agent's name; first rung of actor identity. |
63
- | `MU_WORKSTREAM` | The agent's workstream. With `MU_MANAGED_AGENT=1` it also resolves the workstream when `-w` and `MU_SESSION` are absent (no tmux lookup); without it, ignored. |
52
+ | `MU_WORKSTREAM` | The agent's workstream. With `MU_MANAGED_AGENT=1` it is the scope of every verb that has no `-w` (no tmux lookup); without it, ignored. `MU_SESSION` is not read: a verb with no `-w` and no injected scope fails with `no workstream: pass -w <name>`. |
64
53
  | `MU_CTL_SOCK` | The agent's control socket path, derived from state dir, workstream, and agent. The mu pi extension serves it. |
65
54
  | `MU_MANAGED_AGENT` | `1`. Hides `mu_delegate` inside agents, so a helper cannot spawn helpers. |
55
+ | `MU_DB_PATH`, `MU_STATE_DIR` | The absolute DB path and state dir the spawn used, so the worker's `mu` reads the same DB from any cwd. Local agents only. |
56
+ | `SSH_AUTH_SOCK` | The caller's, when it is a socket (a detached tmux session would otherwise hand the pane a stale one). Local agents only. |
57
+ | `MU_REMOTE` | `1`, only for an agent spawned with `--host`. Tells its pi extension there is no mu DB on that host: it skips the close nudge (you close the task) and the settle wait. |
66
58
 
67
- `mu agent remote-env <agent> --shell` prints `MU_SSH_ARGS` (the ssh
68
- socket forward) and `MU_REMOTE_ENV` (the four vars above) for a remote
69
- pi agent's `--command`. mu never reads them back.
59
+ A `--host` agent gets exactly `MU_MANAGED_AGENT`, `MU_AGENT_NAME`,
60
+ `MU_WORKSTREAM`, `MU_CTL_SOCK` (the host-side path) and `MU_REMOTE`, written
61
+ into its profile's shell template (`{env}`). It never gets `MU_DB_PATH`,
62
+ `MU_STATE_DIR` or `MU_SYNC_DIR`; its local pane gets none of the vars above.
70
63
 
71
- ## pi extension and `mu link pi`
64
+ ## pi package and `mu pi migrate`
72
65
 
73
66
  | Name | Effect | Default |
74
67
  | --- | --- | --- |
75
68
  | `MU_DELEGATE` | `0` hides the `mu_delegate` tool. `mu doctor` reports it. | unset: shown |
76
- | `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` |
77
- | `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 |
78
- | `MU_PI_HOME` | Root under which `mu link pi` writes `.pi/` and `.agents/`. | `$HOME` |
69
+ | `MU_DELEGATE_TRACE` | `1` appends one JSON line per delegate wake send and pi lifecycle event to `<cache dir>/delegate-trace.log`. For debugging a lost wake. | unset: off |
70
+ | `MU_DELEGATE_MAX` | Most delegates one pi session runs at once. Further calls queue, up to four times this; past that a call is recorded as `refused` and reissued with `mu_delegate_reissue` in its session. A positive integer. | `16` |
71
+ | `MU_NUDGE` | `0` turns off the two nudges: the keep-driving settle wait (every completed settle of an orchestrator runs `mu wait` for the work it owns and continues the turn on an event) and the close nudge (one reminder when a mu-spawned pi worker ends a turn still owning an IN_PROGRESS task). A pi nested in an agent's pane (it found `MU_CTL_SOCK` served by another pi) gets neither; a pi without UI (`pi -p`, json mode) gets no close nudge. `mu doctor` reports it. | unset: on |
72
+ | `MU_PI_HOME` | Root under which `mu pi migrate` looks for the old `.pi/` shim and `.agents/` skill link. | `$HOME` |
73
+ | `PI_SESSION_ID` | Read only. The pi session's id: the caller identity `pi:<id>` of `mu_delegate` calls, a bare `mu wait`, `mu me` and `mu next --refute` when no managed agent is injected. | |
79
74
  | `PI_CODING_AGENT_DIR` | Read only. pi's agent dir, checked for a linked murmur extension. | `~/.pi/agent` |
80
- | `MU_EXTENSION_ENTRY` | Test only. Built extension the `mu link pi` shim imports. | `dist/extension/mu-pi.js` |
81
75
 
82
- ## Output and TUI
76
+ ## Output
83
77
 
84
78
  | Name | Effect | Default |
85
79
  | --- | --- | --- |
86
80
  | `NO_COLOR` | Any value turns colour off; wins over everything. | unset |
87
81
  | `MU_FORCE_COLOR` | Truthy forces colour on. `""`, `0`, and `false` opt out. Checked before `FORCE_COLOR`. | unset |
88
- | `MU_NO_TUI` | `1` makes bare `mu` print help instead of opening the TUI. | unset |
89
- | `MU_TUI_DEBUG_MOUSE` | `1` logs mouse hit-tests to stderr. | unset |
90
- | `NODE_ENV` | Unset: the TUI loads React's production build. Set it (e.g. `development`) to get React's dev build and warnings. | unset |
91
82
 
92
83
  ## Tests
93
84
 
94
- `VITEST` or `NODE_ENV=test` makes `openDb()` refuse the real default DB.
85
+ `VITEST` or `NODE_ENV=test` makes `openDb()` refuse the real default DB. The dashboard sets `NODE_ENV=production` for itself when it is unset, to get React's production build.
95
86
  `test/_setup.ts` clears inherited `MU_*` vars except `MU_TMUX_SOCKET`.