@mu-crew/mu 3.8.1 → 3.10.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.
- package/AGENTS.md +2 -1
- package/README.md +1 -0
- package/dist/chunk-4PACTXAB.js +17 -0
- package/dist/chunk-4PACTXAB.js.map +1 -0
- package/dist/chunk-77GBZ2KR.js +85 -0
- package/dist/chunk-77GBZ2KR.js.map +1 -0
- package/dist/chunk-7OQNJWBY.js +391 -0
- package/dist/chunk-7OQNJWBY.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-WT7IN2OD.js +9887 -0
- package/dist/chunk-WT7IN2OD.js.map +1 -0
- package/dist/chunk-XXK7FR37.js +640 -0
- package/dist/chunk-XXK7FR37.js.map +1 -0
- package/dist/chunk-YIF3X32A.js +2792 -0
- package/dist/chunk-YIF3X32A.js.map +1 -0
- package/dist/cli-UZEMPIPW.js +6266 -0
- package/dist/cli-UZEMPIPW.js.map +1 -0
- package/dist/cli.js +37 -25280
- package/dist/cli.js.map +1 -1
- package/dist/extension/mu-pi.js +230 -138
- package/dist/extension/mu-pi.js.map +1 -1
- package/dist/index.d.ts +543 -142
- package/dist/index.js +340 -11169
- package/dist/index.js.map +1 -1
- package/dist/tui-X2XICJTN.js +6452 -0
- package/dist/tui-X2XICJTN.js.map +1 -0
- package/dist/tui-sync-worker.js +16 -0
- package/dist/tui-sync-worker.js.map +1 -0
- package/dist/yank-T3AYQTVR.js +12 -0
- package/dist/yank-T3AYQTVR.js.map +1 -0
- package/docs/ARCHITECTURE.md +14 -6
- package/docs/VISION.md +14 -7
- package/docs/VOCABULARY.md +5 -6
- package/docs/architecture/control-socket.md +6 -2
- package/docs/architecture/mux.md +4 -2
- package/docs/architecture/ops-log.md +2 -1
- package/docs/architecture/sdk.md +3 -1
- package/docs/architecture/sync.md +32 -10
- package/docs/architecture/tui.md +18 -8
- package/docs/guide/backends.md +5 -3
- package/docs/guide/cleanup.md +8 -3
- package/docs/guide/delegate.md +3 -1
- package/docs/guide/dispatch.md +9 -3
- package/docs/guide/recovery.md +19 -8
- package/docs/guide/sql.md +11 -4
- package/docs/guide/stop-a-worker.md +3 -1
- package/docs/guide/tui.md +3 -3
- package/docs/guide/upgrade.md +8 -2
- package/docs/reference/env.md +11 -5
- package/docs/reference/naming.md +3 -4
- package/package.json +1 -1
- package/skills/mu/SKILL.md +2 -2
- package/skills/mu/recipes/drift-audit.md +5 -3
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ambientSyncPass,
|
|
3
|
+
openDb
|
|
4
|
+
} from "./chunk-YIF3X32A.js";
|
|
5
|
+
import "./chunk-4PACTXAB.js";
|
|
6
|
+
import "./chunk-PZ5AY32C.js";
|
|
7
|
+
|
|
8
|
+
// src/cli/tui/sync-worker.ts
|
|
9
|
+
import { parentPort, workerData } from "worker_threads";
|
|
10
|
+
var port = parentPort;
|
|
11
|
+
if (port === null) throw new Error("tui-sync-worker must run as a worker_thread");
|
|
12
|
+
var db = openDb({ path: workerData.dbPath });
|
|
13
|
+
port.on("message", () => {
|
|
14
|
+
void ambientSyncPass(db, { quiet: true }).catch(() => null).then((result) => port.postMessage(result));
|
|
15
|
+
});
|
|
16
|
+
//# sourceMappingURL=tui-sync-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/tui/sync-worker.ts"],"sourcesContent":["// worker_thread entry for the TUI's ambient sync pass (built to\n// dist/tui-sync-worker.js). ingest and flush are synchronous SQLite and\n// file work (seconds on a first read, hundreds of ms per peer append on\n// a 40 MB segment), so on ink's thread they froze input and repaints.\n// Here they block only this thread. The worker owns its own connection\n// to the same DB file: WAL serves the TUI's reads meanwhile, and the\n// TUI sees the result on its next read.\n//\n// Protocol: the parent posts any message to start one pass, and the\n// worker replies with the pass's result (`null` if it threw) when the\n// pass is over. ambientSyncPass is total, and `quiet` keeps stderr off\n// the alternate screen.\n\nimport { parentPort, workerData } from \"node:worker_threads\";\nimport { openDb } from \"../../db.js\";\nimport { ambientSyncPass } from \"../../sync.js\";\n\nconst port = parentPort;\nif (port === null) throw new Error(\"tui-sync-worker must run as a worker_thread\");\nconst db = openDb({ path: (workerData as { dbPath: string }).dbPath });\nport.on(\"message\", () => {\n void ambientSyncPass(db, { quiet: true })\n .catch(() => null)\n .then((result) => port.postMessage(result));\n});\n"],"mappings":";;;;;;;;AAaA,SAAS,YAAY,kBAAkB;AAIvC,IAAM,OAAO;AACb,IAAI,SAAS,KAAM,OAAM,IAAI,MAAM,6CAA6C;AAChF,IAAM,KAAK,OAAO,EAAE,MAAO,WAAkC,OAAO,CAAC;AACrE,KAAK,GAAG,WAAW,MAAM;AACvB,OAAK,gBAAgB,IAAI,EAAE,OAAO,KAAK,CAAC,EACrC,MAAM,MAAM,IAAI,EAChB,KAAK,CAAC,WAAW,KAAK,YAAY,MAAM,CAAC;AAC9C,CAAC;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -74,7 +74,7 @@ them, and is the only code that imports ink:
|
|
|
74
74
|
**CLI and SDK surface.** Each operation is a typed SDK function plus a
|
|
75
75
|
thin Commander wrapper. Public functions take operator-facing names and
|
|
76
76
|
resolve them to surrogate ids exactly once. Typed errors map to exit
|
|
77
|
-
codes 0-
|
|
77
|
+
codes 0-7. The same page covers testing layers and packaging:
|
|
78
78
|
[architecture/sdk.md](architecture/sdk.md).
|
|
79
79
|
|
|
80
80
|
## Module map
|
|
@@ -88,13 +88,14 @@ hub that re-exports them. `src/tasks.ts`, `src/agents.ts`, `src/mux.ts`,
|
|
|
88
88
|
|
|
89
89
|
| Module | Responsibility |
|
|
90
90
|
| --- | --- |
|
|
91
|
-
| `src/db.ts` | better-sqlite3 connection (WAL); schema v11 (
|
|
91
|
+
| `src/db.ts` | better-sqlite3 connection (WAL); schema v11 (12 tables, 3 views) in `applySchema`; installs capture; owns `SYNCED_ENTITIES`, `PORTABLE_TABLES`, `MACHINE_LOCAL_TABLES`; refuses older (`SchemaTooOldError`) or newer (`SchemaTooNewError`) DBs, exit 4 |
|
|
92
92
|
| `src/hlc.ts` | hybrid logical clock, serialized as sortable TEXT |
|
|
93
|
-
| `src/capture.ts` | TEMP triggers recording each portable write as an op |
|
|
93
|
+
| `src/capture.ts` | TEMP triggers recording each portable write as an op, and refusing natural-key changes |
|
|
94
94
|
| `src/op-context.ts` | `withOpContext` (intent, actor, group); `withCaptureSuppressed` echo guard |
|
|
95
95
|
| `src/apply.ts` | apply one op: per-field LWW, tombstones, substate pair repair, `reprojectDeferredOps` |
|
|
96
96
|
| `src/undo.ts` | inverse ops for one group; refuses a superseded group |
|
|
97
97
|
| `src/rebuild.ts` | replay the whole log into a new DB file |
|
|
98
|
+
| `src/compact.ts` | `mu db compact` (blank redundant note-tombstone payloads) and `mu db forget` (drop a torn-down workstream's ops); this machine only |
|
|
98
99
|
| `src/legacy-ops.ts` | classifier for historical log-only intents; rebuild copies them unprojected |
|
|
99
100
|
| `src/drift.ts` | cheap invariant (every row has an op) and `--deep` rebuild-diff |
|
|
100
101
|
| `src/logs.ts` | typed reader over `ops`; `appendLog` and `emitEvent`, the writes triggers cannot cover |
|
|
@@ -105,6 +106,8 @@ hub that re-exports them. `src/tasks.ts`, `src/agents.ts`, `src/mux.ts`,
|
|
|
105
106
|
| Module | Responsibility |
|
|
106
107
|
| --- | --- |
|
|
107
108
|
| `src/segments.ts` | JSONL segments: flush own ops, ingest peers from a watermark |
|
|
109
|
+
| `src/segment-manifest.ts` | segment `.manifest` sidecar: read, write, seal, and the self-consistency check the owner's flush runs before trusting it |
|
|
110
|
+
| `src/sha256-resumable.ts` | SHA-256 with a serialisable running state, so the segment manifest hashes only appended bytes |
|
|
108
111
|
| `src/sync.ts` | peer status, the ambient hook, `--from`, `--repair` |
|
|
109
112
|
| `src/file-lock.ts` | cross-process advisory lock through atomic `fs.mkdir` |
|
|
110
113
|
| `src/fleet-hazards.ts` | mixed-fleet checks: DB inside `MU_SYNC_DIR`, network mount, case collisions |
|
|
@@ -121,13 +124,14 @@ hub that re-exports them. `src/tasks.ts`, `src/agents.ts`, `src/mux.ts`,
|
|
|
121
124
|
| `src/agents/spawn-lock.ts` | per-session lock around topology and row insert |
|
|
122
125
|
| `src/agents/transport.ts` | send routing: ctl for pi agents, mux paste otherwise, no fallback; `expectsCtl` |
|
|
123
126
|
| `src/agents/abort.ts` | stop a pi turn through the control socket; abort + send (`--interrupt`) |
|
|
124
|
-
| `src/agents/delegate.ts` | `delegateOutcome`: one wait result to `done` / `empty` / `died` / `timeout` / `pending` |
|
|
127
|
+
| `src/agents/delegate.ts` | `delegateOutcome`: one wait result to `done` / `empty` / `error` / `died` / `timeout` / `pending` |
|
|
125
128
|
| `src/agents/wait.ts` | block until task-less agents leave `busy` |
|
|
126
129
|
| `src/agents/adopt.ts` | register an existing pane as an agent |
|
|
127
130
|
| `src/agents/kick.ts` | `mu agent kick`: signal a wedged pane's foreground process group |
|
|
128
131
|
| `src/agents/errors.ts` | typed agent errors |
|
|
129
132
|
| `src/agent-state.ts` | runtime state from ctl, herdr or murmur; `unknown` with a reason |
|
|
130
133
|
| `src/reconcile.ts` | ghost prune and orphan surfacing |
|
|
134
|
+
| `src/mux/index.ts` | cluster barrel that `src/mux.ts` re-exports |
|
|
131
135
|
| `src/mux/types.ts` | `MuxBackend` interface; `MuxError`, `PaneNotFoundError`, `NoMultiplexerError` |
|
|
132
136
|
| `src/mux/detect.ts` | `MU_MUX` → `HERDR_ENV` → `$TMUX` → `PATH` ladder; `activeMux()` |
|
|
133
137
|
| `src/mux/tmux.ts` | tmux backend: the `tmux()` wrapper, bracketed-paste send, pane validation |
|
|
@@ -139,7 +143,7 @@ hub that re-exports them. `src/tasks.ts`, `src/agents.ts`, `src/mux.ts`,
|
|
|
139
143
|
| `src/ctl/client.ts` | `ctlRequest`, `ctlProbe`, version and unknown-op errors |
|
|
140
144
|
| `extension/mu-pi.ts` | pi extension, child side: serves `$MU_CTL_SOCK`; built to `dist/extension/mu-pi.js` |
|
|
141
145
|
| `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`. 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) |
|
|
146
|
+
| `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). `parallelSettle` runs the three settle checks in parallel behind one `agent_before_settle` handler and merges their entries; `mu log` breadcrumbs are not awaited |
|
|
143
147
|
| `src/link.ts` | `mu link pi`: extension shim, skill symlink, `inspectLinks` for doctor |
|
|
144
148
|
|
|
145
149
|
### Tasks, workspaces and workstreams
|
|
@@ -165,14 +169,18 @@ hub that re-exports them. `src/tasks.ts`, `src/agents.ts`, `src/mux.ts`,
|
|
|
165
169
|
|
|
166
170
|
| Module | Responsibility |
|
|
167
171
|
| --- | --- |
|
|
168
|
-
| `src/
|
|
172
|
+
| `src/main.ts` | the `mu` bin (`dist/cli.js`): enables the compile cache, then `import()`s `src/cli.ts` |
|
|
173
|
+
| `src/compile-cache.ts`, `src/state-dir.ts` | best-effort V8 compile cache under `<state>/compile-cache`; builtins-only state-dir resolution |
|
|
174
|
+
| `src/cli.ts` | Commander wiring (`buildProgram`, `runCli`) |
|
|
169
175
|
| `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
176
|
| `src/cli/tasks/*.ts` | the `mu task` namespace, including `mu me tasks` / `mu me next` |
|
|
171
177
|
| `src/cli/dispatch-hints.ts` | dispatch-time `Next:` hints for pi agents (`--fresh`, `--interrupt`, abort) |
|
|
172
178
|
| `src/cli/stdin.ts` | `-` as a text argument: reads note / send text from stdin (heredoc prose) |
|
|
173
179
|
| `src/cli/agents-remote.ts` | `mu agent remote-env`: prints the ssh forward and env, runs nothing |
|
|
174
180
|
| `src/cli/tui-launch-focus.ts` | initial-tab focus ladder for bare `mu` and `mu state --tui` |
|
|
181
|
+
| `src/cli/tui-load.ts` | lazy `import()` of the TUI with `NODE_ENV` defaulted to `production` (React prod build) |
|
|
175
182
|
| `src/cli/tui/` | the ink TUI; the only place ink and react are imported |
|
|
183
|
+
| `src/cli/tui/sync-worker.ts` | worker_thread (`dist/tui-sync-worker.js`) running the TUI's slow-tick sync pass off ink's thread |
|
|
176
184
|
| `src/output.ts`, `src/shell-quote.ts` | `printNextSteps` / `errorNextSteps`; POSIX quoting for hints |
|
|
177
185
|
| `src/glyphs.ts` | the one glyph vocabulary (single-cell Nerd Font codepoints) |
|
|
178
186
|
| `src/index.ts` | SDK entry point |
|
package/docs/VISION.md
CHANGED
|
@@ -109,7 +109,7 @@ which task.
|
|
|
109
109
|
A workstream is a mux session: a tmux session on tmux, a herdr workspace
|
|
110
110
|
on herdr. Its agents are panes in that session, so
|
|
111
111
|
`tmux a -t mu-<workstream>` shows the whole crew. Workstreams on one
|
|
112
|
-
machine are separate sessions, partitioned in the DB by `
|
|
112
|
+
machine are separate sessions, partitioned in the DB by `workstream_id`.
|
|
113
113
|
The crew survives detach and reattach.
|
|
114
114
|
|
|
115
115
|
The multiplexer is a backend chosen by detection
|
|
@@ -161,7 +161,7 @@ win32-x64) or a C++ toolchain.
|
|
|
161
161
|
- **Read views**: the `ready`, `blocked`, and `goals` views, and
|
|
162
162
|
`mu state` as the state card.
|
|
163
163
|
- **Typed verbs** that map to resource transitions (`task claim`,
|
|
164
|
-
`task close`, `agent spawn --workspace`, `workstream
|
|
164
|
+
`task close`, `agent spawn --workspace`, `workstream teardown`).
|
|
165
165
|
- **`--json` on every read verb**, so scripts use `jq`, not table parsing.
|
|
166
166
|
- **`mu sql`** underneath, as the explicit escape hatch.
|
|
167
167
|
|
|
@@ -249,10 +249,17 @@ mu agent spawn reviewer-1 --cli pi_big
|
|
|
249
249
|
The exceptions are `mu log --tail` and the TUI (`mu state --tui`, or
|
|
250
250
|
bare `mu` on a TTY). Both are:
|
|
251
251
|
- owned by a human or parent script and gone when it ends;
|
|
252
|
-
-
|
|
253
|
-
clipboard and exit, so every
|
|
254
|
-
|
|
255
|
-
|
|
252
|
+
- free of user actions: TUI keys copy the `mu <verb>` command to
|
|
253
|
+
the clipboard and exit, so every change the operator asks for is a
|
|
254
|
+
fresh CLI call. The TUI's slow tick still does the housekeeping
|
|
255
|
+
any `mu` invocation does: ambient sync (ingest peer ops, flush the
|
|
256
|
+
local segment) and reconcile (prune agents whose pane died, which
|
|
257
|
+
reopens their tasks with a `[reaper]` note, and refresh pane
|
|
258
|
+
titles);
|
|
259
|
+
- limited to stdin, stdout, and a poll timer: no sockets or watches.
|
|
260
|
+
The TUI's slow tick runs short-lived probes (mux liveness, VCS
|
|
261
|
+
status) and ambient sync, and its handoffs (lazygit, tuicr, attach,
|
|
262
|
+
clipboard) run one foreground child each; none outlives the TUI;
|
|
256
263
|
- TTY-gated: pipes, CI, `--json`, and `MU_NO_TUI=1` never enter the
|
|
257
264
|
TUI, and plain `mu state` prints the static card.
|
|
258
265
|
|
|
@@ -276,5 +283,5 @@ the work, pi plus manual tmux is more transparent than mu.
|
|
|
276
283
|
|
|
277
284
|
Known gaps, none promoted yet: `--evidence` grounds but does not verify,
|
|
278
285
|
mutations have no declared idempotency keys, most mutations have no dry
|
|
279
|
-
run (`workstream
|
|
286
|
+
run (`workstream teardown`, `task delete`, and `undo` do), and the `role`
|
|
280
287
|
field is stored but not enforced.
|
package/docs/VOCABULARY.md
CHANGED
|
@@ -15,7 +15,7 @@ agent names, and file paths are in [reference/naming.md](reference/naming.md).
|
|
|
15
15
|
| Term | Meaning | Don't use |
|
|
16
16
|
| --- | --- | --- |
|
|
17
17
|
| **workstream** | The unit of organization: one **mux session** and one DB partition. | "project", "session" (alone), "context" |
|
|
18
|
-
| **scratch workstream** | The reserved workstream `scratch` for helpers outside any task DAG. Auto-created on first spawn with explicit `-w scratch`; `mu workstream init scratch` is rejected. | "throwaway ws", "temp workstream" |
|
|
18
|
+
| **scratch workstream** | The reserved workstream `scratch` for helpers outside any task DAG. Auto-created on first spawn with explicit `-w scratch`; `mu workstream init scratch` is rejected. Its session keeps a `_mu` window, so it stays open after the last agent closes; `teardown --empty` skips it. | "throwaway ws", "temp workstream" |
|
|
19
19
|
| **multiplexer** / **mux** | The terminal program that owns panes: **tmux** or **herdr**. "mux" in code (`src/mux/`, `MU_MUX`), "multiplexer" in prose; one is active per invocation. | "terminal", "emulator", "backend" (alone) |
|
|
20
20
|
| **mux backend** | An implementation of `MuxBackend` (`src/mux/tmux.ts`, `src/mux/herdr.ts`). Owns everything backend-specific. | "driver", "provider", "adapter" |
|
|
21
21
|
| **mux detection** | Picks the mux backend: `MU_MUX` → `HERDR_ENV=1` → `$TMUX` → binary on `PATH` (tmux wins a tie) → `NoMultiplexerError`. | "auto-detect", "probe" |
|
|
@@ -105,7 +105,7 @@ agent names, and file paths are in [reference/naming.md](reference/naming.md).
|
|
|
105
105
|
| **op** | One `ops` row, written by a trigger in the mutation's transaction. Holds only changed columns. | "event", "delta", "change" |
|
|
106
106
|
| **ops log** | The `ops` table: the append-only record that sync, undo, and history read. | "event log", "journal", "WAL" |
|
|
107
107
|
| **log entry** | An op as rendered by `mu log`, through one formatter (`src/log-render.ts`). | "message", "event" |
|
|
108
|
-
| **kind** | The operator's channel tag on a log entry (`mu log --kind`), stored as the op's `entity`. | "category", "type" |
|
|
108
|
+
| **kind** | The operator's channel tag on a log entry (`mu log --kind`), stored as the op's `entity`. `workstream`, `task`, `edge`, and `note` are reserved. | "category", "type" |
|
|
109
109
|
| **log ledger** | A convention: a custom `--kind` used as a watcher loop's durable dedupe record. | "state file" |
|
|
110
110
|
| **intent** | The semantic label on an op (`task.close`), set once per SDK function via **op context**. | "verb", "action" |
|
|
111
111
|
| **group** | The ops of one user action (`group_id`), undone as a unit. Any unique id prefix works. | "transaction", "batch" |
|
|
@@ -119,7 +119,7 @@ agent names, and file paths are in [reference/naming.md](reference/naming.md).
|
|
|
119
119
|
| **resurrection** | A put newer than a seen tombstone, which legitimately recreates the row. | "undelete", "revive" |
|
|
120
120
|
| **provenance** | Which HLC last wrote a field, derived from the ops log by query, never stored. | "version vector" |
|
|
121
121
|
| **inverse op** | The op that reverts another; an ordinary op, so undo syncs and is undoable. | "rollback" |
|
|
122
|
-
| **superseded** (group) | A later group wrote the same field, so `mu undo` refuses (exit 4) without `--force`. | "stale" |
|
|
122
|
+
| **superseded** (group) | A later group wrote the same field (other than `updated_at`), so `mu undo` refuses (exit 4) without `--force`. | "stale" |
|
|
123
123
|
| **teardown** | Kill a workstream's mux session and delete its rows. `mu undo` restores rows, not panes or dirs. | "destroy", "nuke", "purge" |
|
|
124
124
|
| **drift** | The ops log and live tables disagree. Always a bug. | "inconsistency", "corruption" |
|
|
125
125
|
| **shallow / deep check** | Drift checks in `mu doctor` and `mu doctor --deep` (full rebuild). | "fsck" |
|
|
@@ -128,7 +128,7 @@ agent names, and file paths are in [reference/naming.md](reference/naming.md).
|
|
|
128
128
|
| **machine_id** | Per-state-dir uuid naming this DB's **peer** and **segment**. | "device id", "host id" |
|
|
129
129
|
| **segment** | `<sync-dir>/<machine_id>.jsonl`: one machine's ops, single writer. | "replica", "shard" |
|
|
130
130
|
| **peer** | Another machine, found by its segment; named by `machine_id` prefix. **Stale** after 24h unchanged. | "node", "remote" |
|
|
131
|
-
| **watermark** | `sync_peers.last_applied_seq`: how many lines of a peer's segment have been applied. | "offset", "cursor" |
|
|
131
|
+
| **watermark** | `sync_peers.last_applied_seq`: how many lines of a peer's segment file have been applied. A conflict copy has its own, keyed by its file stem. | "offset", "cursor" |
|
|
132
132
|
| **flush** / **ingest** | Write my ops to my segment; apply peers' from their watermarks. Every invocation, when `MU_SYNC_DIR` is set. | "push", "pull" |
|
|
133
133
|
| **reprojection** | After ingest, apply deferred ops whose parent arrived from another peer. | "retry queue" |
|
|
134
134
|
| **mixed fleet** | Machines sharing state across different OSes or filesystems. | "cluster" |
|
|
@@ -222,5 +222,4 @@ deprecated, always `spawning`, and never read.
|
|
|
222
222
|
| **pi session** | pi's conversation |
|
|
223
223
|
| **agent session** | Colloquial for an agent's lifetime; avoid in code |
|
|
224
224
|
|
|
225
|
-
|
|
226
|
-
`agents.session_id` column keeps its name for schema stability.
|
|
225
|
+
Columns and variables say `workstream_id`, never `session_id`.
|
|
@@ -132,7 +132,11 @@ probes `refused`, and `mu agent close` or the reaper deletes it.
|
|
|
132
132
|
when `MU_MANAGED_AGENT` is set (inside a mu-spawned pane) or
|
|
133
133
|
`MU_DELEGATE=0`. The tools shell out to the `mu` CLI (spawn, send,
|
|
134
134
|
`wait --json`, read, abort, close) and only format the CLI's
|
|
135
|
-
`outcome` into a follow-up message.
|
|
135
|
+
`outcome` into a follow-up message. A call runs one
|
|
136
|
+
`mu agent spawn --next-free --send <task> --json` (mu picks the free
|
|
137
|
+
name under its spawn lock and returns the send's `runs`), then one
|
|
138
|
+
`mu agent wait --after-runs <runs>` in the background; with `record`,
|
|
139
|
+
a `mu task show` comes first. `delegateOutcome`
|
|
136
140
|
(`src/agents/delegate.ts`) maps one wait result to `done`, `empty`,
|
|
137
|
-
`died`, `timeout` or `pending`. The extension imports nothing from pi;
|
|
141
|
+
`error`, `died`, `timeout` or `pending`. The extension imports nothing from pi;
|
|
138
142
|
it types pi's API structurally.
|
package/docs/architecture/mux.md
CHANGED
|
@@ -5,7 +5,7 @@ mu runs agents in multiplexer panes, tmux or herdr, behind one
|
|
|
5
5
|
|
|
6
6
|
## One mux session per workstream
|
|
7
7
|
|
|
8
|
-
One workstream is one mux session and one `
|
|
8
|
+
One workstream is one mux session and one `workstream_id` partition in
|
|
9
9
|
`~/.local/state/mu/mu.db`. Workstreams on one machine are independent
|
|
10
10
|
mux sessions.
|
|
11
11
|
|
|
@@ -107,7 +107,9 @@ as a capability, not a name check:
|
|
|
107
107
|
reads before the first claim report `unknown`. A pi agent skips the
|
|
108
108
|
fixed wait: mu checks the pane on every handshake tick (a dead pane
|
|
109
109
|
rolls back at once) and scans for startup errors when the handshake
|
|
110
|
-
ends.
|
|
110
|
+
ends. After an ok handshake the scan skips shell exec-failure lines
|
|
111
|
+
(`command not found`, `No such file or directory`): pi demonstrably
|
|
112
|
+
started, and a resumed session can show such lines as old tool output.
|
|
111
113
|
- **herdr.** mu creates a bare pane and calls `startAgentInPane`, which
|
|
112
114
|
subsumes both checks.
|
|
113
115
|
- **pi, either backend.** After the agent starts, mu polls its control
|
|
@@ -103,7 +103,8 @@ prefix previews, and `--yes` applies.
|
|
|
103
103
|
old key (drift-641). The `task_notes` delete trigger records `OLD.*`
|
|
104
104
|
only when no put under the current key is in the log, and `'{}'`
|
|
105
105
|
otherwise; `planUndo` folds the puts first and falls back to the
|
|
106
|
-
tombstone payload
|
|
106
|
+
tombstone payload, and `applyOp` deletes by the payload's content
|
|
107
|
+
identity, falling back to the put under the same key. For the same reason `src/drift.ts` matches notes on
|
|
107
108
|
the task-key prefix.
|
|
108
109
|
|
|
109
110
|
## Rewriting history (local only)
|
package/docs/architecture/sdk.md
CHANGED
|
@@ -125,7 +125,9 @@ codes:
|
|
|
125
125
|
| 2 | usage error (Commander's default) |
|
|
126
126
|
| 3 | not found (agent, task, workspace) |
|
|
127
127
|
| 4 | conflict (name collision, double claim, dirty tree, schema too old or new, outdated extension) |
|
|
128
|
-
| 5 | substrate unavailable (no multiplexer, DB locked) |
|
|
128
|
+
| 5 | substrate unavailable or unsafe (no multiplexer, mux or VCS failure, DB locked, ops-log drift, a `mu doctor` FAIL row, timeout) |
|
|
129
|
+
| 6 | `mu task wait`: the reaper reopened a watched task (its pane died) |
|
|
130
|
+
| 7 | `mu task wait`: a watched task stalled (`--on-stall exit`) |
|
|
129
131
|
|
|
130
132
|
Errors carry structured context (operation, target, attempted action)
|
|
131
133
|
so `mu doctor` can show them readably.
|
|
@@ -15,14 +15,26 @@ Litestream, cr-sqlite or a peer list, and why the DB must never sit in
|
|
|
15
15
|
- `flushSegment` appends this machine's unflushed ops to
|
|
16
16
|
`<MU_SYNC_DIR>/<machine_id>.jsonl`.
|
|
17
17
|
- `ingestSegment` reads a peer segment from its watermark into
|
|
18
|
-
`applyOp`.
|
|
18
|
+
`applyOp`. Ambient ingest skips a peer that is caught up: the manifest
|
|
19
|
+
counts the watermark, and the segment's size, exact mtime and
|
|
20
|
+
manifest sha256 equal what the last clean read to the end recorded
|
|
21
|
+
(in `sync_fingerprints`). Any change, including a rewrite with a
|
|
22
|
+
backdated mtime, fails that check, so the full read hashes the file
|
|
23
|
+
and reports the mismatch. `mu sync` always reads every line.
|
|
19
24
|
- Peer discovery is implicit: every non-self `*.jsonl` is a peer. A
|
|
20
25
|
peer disappears only when its segment is deleted.
|
|
26
|
+
- A Syncthing conflict copy (`<machine>.sync-conflict-….jsonl`) is
|
|
27
|
+
ingested too, with its own watermark keyed by the file stem: it can
|
|
28
|
+
diverge from the original, so line counts are not comparable.
|
|
21
29
|
|
|
22
30
|
**Single writer per file.** A machine appends only to its own segment,
|
|
23
31
|
so nothing is contended and any folder syncer is adequate. Segments
|
|
24
32
|
are regenerable, so there is no fsync. `src/file-lock.ts` stops two
|
|
25
|
-
local `mu` processes from interleaving partial lines.
|
|
33
|
+
local `mu` processes from interleaving partial lines. A flush never
|
|
34
|
+
appends without that lock: if another process still holds it after
|
|
35
|
+
2 s (15 s for `mu sync`), the flush writes nothing, the ops stay
|
|
36
|
+
pending in `ops`, and the next invocation appends them. A stale lock
|
|
37
|
+
(holder crashed, older than 30 s) is broken as before.
|
|
26
38
|
|
|
27
39
|
**Only `SYNCED_ENTITIES` and only this machine's ops are flushed.**
|
|
28
40
|
Pane ids and absolute paths never leave the machine, and peers never
|
|
@@ -35,8 +47,10 @@ anything else, unwrapping on ingest.
|
|
|
35
47
|
|
|
36
48
|
## Robustness layers
|
|
37
49
|
|
|
38
|
-
|
|
39
|
-
watermark only that far:
|
|
50
|
+
The first three checks stop ingest at the first bad record and advance
|
|
51
|
+
the watermark only that far. The fourth is whole-file: it reports the
|
|
52
|
+
defect without halting, because every line still present is valid, and
|
|
53
|
+
only a fresh copy of the file clears it:
|
|
40
54
|
|
|
41
55
|
| Layer | Catches |
|
|
42
56
|
| --- | --- |
|
|
@@ -54,8 +68,9 @@ distinguishes three cases:
|
|
|
54
68
|
|
|
55
69
|
| Line | Result |
|
|
56
70
|
| --- | --- |
|
|
57
|
-
| damaged | halt |
|
|
71
|
+
| damaged (including a blank line) | halt |
|
|
58
72
|
| a known machine-local entity | reported defect, skipped |
|
|
73
|
+
| a historical log-only intent (`workstream.export` prose from mu < 1.1) | skipped silently, as flush and `--from` skip it |
|
|
59
74
|
| an unrecognised entity | applied as a no-op, no defect (a reader behind the writer is normal in a mixed fleet) |
|
|
60
75
|
|
|
61
76
|
`mu sync` suggests `--repair` only for defects a re-read can clear.
|
|
@@ -71,8 +86,9 @@ poll outliving a command. It happens because you already run `mu`
|
|
|
71
86
|
constantly.
|
|
72
87
|
|
|
73
88
|
The seam is `handle()` (`src/cli/handle.ts`), which every verb passes
|
|
74
|
-
through and which is already async. `
|
|
75
|
-
|
|
89
|
+
through and which is already async. It awaits `ambientIngest` and
|
|
90
|
+
`ambientFlush`; the flush is async because it takes the file lock,
|
|
91
|
+
while most verb bodies are synchronous
|
|
76
92
|
better-sqlite3 code. One `await` before `fn(db)` and one after cover
|
|
77
93
|
every verb.
|
|
78
94
|
|
|
@@ -86,7 +102,8 @@ every verb.
|
|
|
86
102
|
`src/sync.ts` holds `ambientIngest`, `ambientFlush`, `ambientSyncPass`,
|
|
87
103
|
`peerStatuses`, `ingestFromDb` (`mu sync --from <path>`) and
|
|
88
104
|
`repairPeer` (`mu sync --repair <peer>`, a unique prefix; ambiguity is
|
|
89
|
-
exit 4).
|
|
105
|
+
exit 4). A prefix names a machine, so it resets the watermark of the
|
|
106
|
+
peer's segment and of every conflict copy of it.
|
|
90
107
|
|
|
91
108
|
### Carve-outs
|
|
92
109
|
|
|
@@ -110,8 +127,13 @@ edge puts that are resolvable now but unprojected. It skips ops whose
|
|
|
110
127
|
parent task is gone and keys with a newer `del`, so a deleted edge is
|
|
111
128
|
never resurrected and an orphan is not retried forever. It runs once
|
|
112
129
|
per ingest pass, not per peer, because an edge in one segment may name
|
|
113
|
-
a task in another.
|
|
114
|
-
|
|
130
|
+
a task in another. Ambient ingest runs it only while SQLite's
|
|
131
|
+
`user_version` is 1. `applyIncomingOp` sets that marker in the same
|
|
132
|
+
transaction whenever an op changes a row or defers (`absent`), whether
|
|
133
|
+
it came from a segment, `--from`, or an SDK caller; the repair clears
|
|
134
|
+
it. A process that dies between
|
|
135
|
+
the two leaves the marker set, so the next invocation runs the repair.
|
|
136
|
+
`mu sync` and `--from` always run it.
|
|
115
137
|
|
|
116
138
|
There is no retry queue. A queue would be a second source of truth and
|
|
117
139
|
would not survive a short-lived `mu` process when the parent arrives
|
package/docs/architecture/tui.md
CHANGED
|
@@ -30,9 +30,10 @@ Press `?` in the TUI for the keymap.
|
|
|
30
30
|
| `index.ts`, `escapes.ts` | `runTui` entry; alt-screen and SGR mouse-mode lifecycle; pure escape bytes |
|
|
31
31
|
| `app.tsx` | `<App>` root: popup state machine, global keymap, tabs, footer |
|
|
32
32
|
| `state.ts` | `useDashboardSnapshot` poll loop (fast and slow tiers) |
|
|
33
|
-
| `keys.ts`, `keymap-spec.ts`, `help.tsx` | pure key dispatch; the keymap source of truth that drives both dispatch and the
|
|
33
|
+
| `keys.ts`, `keymap-spec.ts`, `help.tsx` | pure key dispatch; the keymap source of truth that drives both dispatch and the `?` overlay |
|
|
34
34
|
| `mouse.ts`, `use-popup-action-queue.ts` | vendored SGR mouse parser with double-click; one queued popup action per render |
|
|
35
35
|
| `layout.ts`, `columns.ts`, `wrap-ansi.ts`, `use-terminal-size.ts` | responsive columns and row budgets; aligned clipping; ANSI-aware wrapping; resize hook |
|
|
36
|
+
| `popup-input.ts` | `usePopupInput`: popup keyboard, off while the help overlay covers the popup |
|
|
36
37
|
| `titled-box.tsx`, `popup-shell.tsx`, `list-row.tsx`, `padded-rows.tsx`, `status-bar.tsx`, `tab-strip*.ts(x)` | chrome primitives |
|
|
37
38
|
| `format-helpers.ts`, `agent-display.ts` | shared formatters and agent-row display |
|
|
38
39
|
| `use-popup-filter.tsx`, `use-status-filter.tsx`, `use-notes-drill.ts` | `/` filter; status (`o`/`i`/`c`) and substate (`p`/`w`) toggles; shared notes drill |
|
|
@@ -45,9 +46,12 @@ Press `?` in the TUI for the keymap.
|
|
|
45
46
|
`<App>` owns:
|
|
46
47
|
|
|
47
48
|
- **Popup state:** `null` (dashboard) or one popup id. One popup at a
|
|
48
|
-
time; `Esc` or `q` returns to the dashboard.
|
|
49
|
+
time; `Esc` or `q` returns to the dashboard. `?` over a popup hides
|
|
50
|
+
it (`display="none"`) and turns its keyboard off rather than
|
|
51
|
+
unmounting it, so its cursor, filter and drill survive the overlay.
|
|
49
52
|
- **Card visibility:** toggled by `0`-`9`.
|
|
50
|
-
- **Tick rate:** fast tick, 1s default, adjusted with `+`
|
|
53
|
+
- **Tick rate:** fast tick, 1s default, adjusted with `+` / `=` and
|
|
54
|
+
`-`. There is no reset key: `0` toggles the Commits card.
|
|
51
55
|
- **Active workstream tab:** `Tab` / `Shift-Tab` cycles when there are
|
|
52
56
|
two or more. A torn-down workstream keeps its tab position and
|
|
53
57
|
renders dimmed with strikethrough until the name is recreated. A
|
|
@@ -98,7 +102,7 @@ the right border.
|
|
|
98
102
|
|
|
99
103
|
Every popup row exposes one canonical `mu` command through `y`.
|
|
100
104
|
`yank.ts` probes pbcopy, wl-copy, xclip, xsel and clip.exe, then falls
|
|
101
|
-
back to OSC-52
|
|
105
|
+
back to OSC-52 written to `/dev/tty`, since ink owns stdout.
|
|
102
106
|
|
|
103
107
|
The handoffs leave the alt screen, disable mouse mode, run a
|
|
104
108
|
foreground subprocess, and restore both on exit:
|
|
@@ -114,16 +118,22 @@ A TUI gesture that mutates state needs a ROADMAP entry first.
|
|
|
114
118
|
|
|
115
119
|
Mouse support uses SGR mouse mode. `mouse.ts` parses
|
|
116
120
|
`ESC[<button;x;y;M/m`, detects double-clicks, and exposes `useMouse()`.
|
|
117
|
-
A double-click on a card
|
|
118
|
-
|
|
119
|
-
|
|
121
|
+
A double-click on a card opens its popup. A double-click on a popup row
|
|
122
|
+
emits one `clickRow` action (the 0-based body row) through the action
|
|
123
|
+
queue. The popup maps that row through its own scroll window and
|
|
124
|
+
header strips (`clickedItem` in `popups/scroll.ts`), then focuses and
|
|
125
|
+
drills the item. App cannot do the mapping: only the popup knows its
|
|
126
|
+
window start and strip rows.
|
|
120
127
|
|
|
121
128
|
`keys.ts` holds `dispatchGlobalKey` (dashboard), `dispatchPopupKey`
|
|
122
129
|
(popup), and `shouldSwallowGlobalKey` (keys a popup consumes).
|
|
123
130
|
|
|
124
131
|
## Drill recursion
|
|
125
132
|
|
|
126
|
-
List popups drill with `Enter`.
|
|
133
|
+
List popups drill with `Enter`. Each popup pins the drilled row's
|
|
134
|
+
identity at `Enter` (task, agent, check, commit sha, log seq, track
|
|
135
|
+
goals), so a refresh that reorders or extends the list cannot move an
|
|
136
|
+
open drill to another row. `DrillScrollView`
|
|
127
137
|
(`popups/drill.tsx`) is the shared scrollable text leaf: Workspaces'
|
|
128
138
|
git show, Agents' scrollback, Activity log payloads, and Doctor
|
|
129
139
|
remediation. Task popups drill into `TaskDetailDrill`
|
package/docs/guide/backends.md
CHANGED
|
@@ -27,7 +27,7 @@ mu picks the backend from the first rule that matches:
|
|
|
27
27
|
| Workstream | session `mu-<ws>` | workspace labelled `mu-<ws>` |
|
|
28
28
|
| Window | window | tab |
|
|
29
29
|
| Pane id | `%15` | `w1:p1` |
|
|
30
|
-
| Attach | `tmux attach -t mu-<ws>` | `herdr
|
|
30
|
+
| Attach | `tmux attach -t mu-<ws>` | `herdr workspace focus <id>`, then `herdr` outside a herdr pane |
|
|
31
31
|
| pi agent state | control socket | control socket |
|
|
32
32
|
| Other agents' state | murmur | herdr |
|
|
33
33
|
| Send to non-pi agents | bracketed paste, then Enter | one atomic `agent prompt` |
|
|
@@ -44,8 +44,10 @@ Where each agent's state comes from, per backend:
|
|
|
44
44
|
`/proc/<pid>/fd/0`, which macOS does not have.
|
|
45
45
|
- `mu agent read --lines` cannot recover rows that scrolled off a pane
|
|
46
46
|
on the alternate screen.
|
|
47
|
-
-
|
|
48
|
-
|
|
47
|
+
- herdr starts the agent binary itself (`herdr agent start --kind
|
|
48
|
+
<cli>`), so spawn refuses `--command` and `MU_<CLI>_COMMAND` with
|
|
49
|
+
exit 2 instead of running a different binary. Unset the variable, or
|
|
50
|
+
spawn with `MU_MUX=tmux`.
|
|
49
51
|
- herdr creates every workspace detached. Run `herdr workspace focus`
|
|
50
52
|
yourself.
|
|
51
53
|
|
package/docs/guide/cleanup.md
CHANGED
|
@@ -22,14 +22,18 @@ mu workstream teardown auth --yes # kills the session, deletes the rows
|
|
|
22
22
|
Teardown kills the tmux session first, then deletes the agents, tasks,
|
|
23
23
|
edges, notes, and workspaces. Running it again is a no-op.
|
|
24
24
|
|
|
25
|
-
To sweep every workstream that has no tasks, agents, or workspaces
|
|
26
|
-
including `mu-*` tmux sessions with no DB row:
|
|
25
|
+
To sweep every workstream that has no tasks, agents, or workspaces:
|
|
27
26
|
|
|
28
27
|
```bash
|
|
29
28
|
mu workstream teardown --empty
|
|
30
29
|
mu workstream teardown --empty --yes
|
|
31
30
|
```
|
|
32
31
|
|
|
32
|
+
The sweep skips `scratch`, whose idle session is kept open on purpose.
|
|
33
|
+
It never takes a `mu-*` session that has no workstream row in this DB:
|
|
34
|
+
it may be a live workstream from another DB. The output names
|
|
35
|
+
such sessions; tear one down by name once you know it is unused.
|
|
36
|
+
|
|
33
37
|
## Undo a teardown
|
|
34
38
|
|
|
35
39
|
Teardown writes tombstone ops, so the ops log keeps everything. You do
|
|
@@ -40,7 +44,8 @@ mu workstream list --torn-down
|
|
|
40
44
|
mu undo 7a40e6cc --yes
|
|
41
45
|
```
|
|
42
46
|
|
|
43
|
-
The list shows one row per teardown, newest first.
|
|
47
|
+
The list shows one row per teardown, newest first. A `--empty` sweep
|
|
48
|
+
writes one row per workstream, so undo each one. `← recreated since`
|
|
44
49
|
means the name is in use again, so the undo would change nothing.
|
|
45
50
|
Killed panes and freed workspace directories do not come back.
|
|
46
51
|
|
package/docs/guide/delegate.md
CHANGED
|
@@ -95,7 +95,9 @@ Exit codes: `0` met, `5` timeout, `6` pane died. Non-pi agents have no
|
|
|
95
95
|
## Keep a helper
|
|
96
96
|
|
|
97
97
|
Pass `keep: true`, or spawn a named agent in `scratch`. It is created
|
|
98
|
-
on first spawn, needs no tasks, and cannot be `init`ed.
|
|
98
|
+
on first spawn, needs no tasks, and cannot be `init`ed. Its `mu-scratch`
|
|
99
|
+
session stays open after the last agent closes; remove it with
|
|
100
|
+
`mu workstream teardown scratch --yes`.
|
|
99
101
|
|
|
100
102
|
```bash
|
|
101
103
|
mu agent spawn helper -w scratch
|
package/docs/guide/dispatch.md
CHANGED
|
@@ -58,6 +58,10 @@ socket. Pick the mode by what the agent is doing:
|
|
|
58
58
|
pastes nothing. `--via mux` pastes into the pane on purpose.
|
|
59
59
|
|
|
60
60
|
After a claim, spawn, or wait, the `Next:` block prints the right send.
|
|
61
|
+
`mu agent spawn --send <text>` spawns and sends the first prompt in one
|
|
62
|
+
call; `--next-free` takes the next free name when `<name>` is taken
|
|
63
|
+
(`worker-1` → `worker-2`; the stem is cut to keep the name within 32
|
|
64
|
+
chars, and it gives up with exit 4 when no free name is near).
|
|
61
65
|
|
|
62
66
|
### Non-pi agents
|
|
63
67
|
|
|
@@ -103,9 +107,11 @@ mu workspace refresh worker-1 -w auth
|
|
|
103
107
|
```
|
|
104
108
|
|
|
105
109
|
It rebases onto the remote's main (`origin/HEAD`); without a remote,
|
|
106
|
-
pass `--from main`. It refuses on a dirty tree. On a conflict it exits 5
|
|
107
|
-
|
|
108
|
-
|
|
110
|
+
pass `--from main`. It refuses on a dirty tree. On a conflict it exits 5:
|
|
111
|
+
git and sl abort the rebase and leave the workspace as it was, while jj
|
|
112
|
+
keeps the rebase with the conflicts committed in place. After you resolve,
|
|
113
|
+
run refresh again so mu records the new base. To throw the workspace away
|
|
114
|
+
instead, run `mu workspace free worker-1 -w auth`.
|
|
109
115
|
|
|
110
116
|
Claim and send warn when the worker's workspace is 10 or more commits
|
|
111
117
|
behind main. `--strict-staleness` makes that a refusal (exit 4).
|
package/docs/guide/recovery.md
CHANGED
|
@@ -7,7 +7,7 @@ read the same rules in
|
|
|
7
7
|
[recipes/recovery.md](../../skills/mu/recipes/recovery.md).
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
mu doctor # exit 0 healthy, 5 on drift
|
|
10
|
+
mu doctor # exit 0 healthy (WARN rows too), 5 on drift or a FAIL row
|
|
11
11
|
mu doctor --deep # rebuild the ops log and diff every field
|
|
12
12
|
mu doctor --disk # add per-workspace byte usage
|
|
13
13
|
```
|
|
@@ -30,6 +30,8 @@ mu agent adopt %15 -w auth
|
|
|
30
30
|
mu agent adopt %15 -w auth --name investigator
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
On herdr, pass its pane id instead (`mu agent adopt w1:p2 -w auth`).
|
|
34
|
+
|
|
33
35
|
The pane title becomes the agent name. The pane must be in the
|
|
34
36
|
`mu-<workstream>` session. An adopted pi has no control socket until
|
|
35
37
|
you restart it with the `MU_CTL_SOCK` path that `adopt` prints.
|
|
@@ -86,7 +88,8 @@ mu undo 6380dd3d --yes # apply
|
|
|
86
88
|
- Any unique prefix of a group id works.
|
|
87
89
|
- The undo is its own group, so undoing it again is redo.
|
|
88
90
|
- If later work changed the same fields, undo exits 4 and names the
|
|
89
|
-
conflict.
|
|
91
|
+
conflict. A later write to `updated_at` alone, such as `mu task note`,
|
|
92
|
+
does not count. `--force --yes` overrides and discards the newer work.
|
|
90
93
|
- Undo restores rows only. Killed panes and freed workspace
|
|
91
94
|
directories do not come back.
|
|
92
95
|
|
|
@@ -113,21 +116,29 @@ command.
|
|
|
113
116
|
| `ws-rows` | a workspace row whose directory is gone; the next send fails in the VCS |
|
|
114
117
|
| `ws-dirs` | a workspace directory with no row; it blocks the next `--workspace` spawn with that name |
|
|
115
118
|
| `ws-empty` | a workstream directory with no checkouts |
|
|
116
|
-
| `db-copies` | stray `mu.db*` files that nothing reads |
|
|
119
|
+
| `db-copies` | stray `mu.db*` files that nothing reads, including the `pre-compact` / `pre-forget` backups |
|
|
117
120
|
| `exports` | output left by the removed export verb |
|
|
118
121
|
| `locks` | lock directories older than one hour, from a spawn or flush that died |
|
|
119
122
|
|
|
120
123
|
To list workspace orphans with cleanup commands, run
|
|
121
|
-
`mu workspace orphans -w auth`.
|
|
122
|
-
`
|
|
124
|
+
`mu workspace orphans -w auth`. An orphan has no row, so
|
|
125
|
+
`mu workspace free` cannot remove it. Remove a git-backed orphan with
|
|
126
|
+
`git worktree remove --force <path>` from the project root, and any
|
|
127
|
+
other with `rm -rf <path>`.
|
|
123
128
|
|
|
124
129
|
## Rename a workstream
|
|
125
130
|
|
|
126
|
-
There is no rename verb
|
|
131
|
+
There is no rename verb, and `mu sql` refuses to change
|
|
132
|
+
`workstreams.name`. The name is part of the key that every op is filed
|
|
133
|
+
under, so a raw rename leaves the ops log on the old name: `mu doctor`
|
|
134
|
+
reports drift, and sync or `mu rebuild` brings the old name back.
|
|
135
|
+
Re-create the workstream instead:
|
|
127
136
|
|
|
128
137
|
```bash
|
|
129
|
-
mu
|
|
130
|
-
|
|
138
|
+
mu db backup /tmp/mu-before-rename.db
|
|
139
|
+
mu workstream init auth-refactor
|
|
140
|
+
# re-add the tasks you still need, with -w auth-refactor
|
|
141
|
+
mu workstream teardown auth-refator --yes
|
|
131
142
|
```
|
|
132
143
|
|
|
133
144
|
Names start with a lowercase letter, use letters, digits, `_`, or
|
package/docs/guide/sql.md
CHANGED
|
@@ -20,12 +20,12 @@ mu task list -w auth --json | jq '.count'
|
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
Exit codes: `2` usage error, `3` not found, `4` conflict, `5`
|
|
23
|
-
timeout or a multiplexer, VCS,
|
|
24
|
-
`7` stall.
|
|
23
|
+
timeout, a locked DB, or a multiplexer, VCS, drift, or `mu doctor`
|
|
24
|
+
FAIL-row failure, `6` reaped pane, `7` stall.
|
|
25
25
|
|
|
26
26
|
## Read the schema
|
|
27
27
|
|
|
28
|
-
The schema has
|
|
28
|
+
The schema has 12 tables and 3 views (`ready`, `blocked`, `goals`).
|
|
29
29
|
Four tables travel between machines: `workstreams`, `tasks`,
|
|
30
30
|
`task_edges`, and `task_notes`. List them all:
|
|
31
31
|
|
|
@@ -66,7 +66,14 @@ mu sql "WITH RECURSIVE prereqs(id) AS (
|
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
`mu sql` runs writes too and prints the number of rows changed. Writes
|
|
69
|
-
are captured as ops, so `mu undo`
|
|
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
|
|
71
|
+
`ON DELETE CASCADE` too: deleting a task with one edge counts 2.
|
|
72
|
+
|
|
73
|
+
`mu sql` refuses to change a natural-key column (`workstreams.name`,
|
|
74
|
+
`tasks.workstream_id`, `tasks.local_id`, a note's task, or an edge's
|
|
75
|
+
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.
|
|
70
77
|
|
|
71
78
|
## Pass list flags
|
|
72
79
|
|
|
@@ -55,7 +55,9 @@ freed with it. If the workspace has edits or commits, close refuses
|
|
|
55
55
|
with exit 4. Then do one of these:
|
|
56
56
|
|
|
57
57
|
- Run `mu workspace free worker-1 -w auth --commit` to commit pending
|
|
58
|
-
changes and free the workspace, then close.
|
|
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>`.
|
|
59
61
|
- Run `mu agent close worker-1 -w auth --discard-workspace` to free
|
|
60
62
|
and close in one step. You lose the changes.
|
|
61
63
|
|