@martintrojer/mu 0.3.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +188 -44
- package/README.md +80 -25
- package/dist/cli.js +16743 -7931
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1844 -1677
- package/dist/index.js +4992 -3667
- package/dist/index.js.map +1 -1
- package/docs/ARCHITECTURE.md +302 -59
- package/docs/HANDOVER.md +461 -0
- package/docs/ROADMAP.md +198 -497
- package/docs/USAGE_GUIDE.md +545 -223
- package/docs/VISION.md +48 -4
- package/docs/VOCABULARY.md +39 -8
- package/docs/img/tui-dashboard.png +0 -0
- package/package.json +12 -12
- package/skills/mu/SKILL.md +278 -469
package/docs/USAGE_GUIDE.md
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
# mu — Usage Guide
|
|
2
2
|
|
|
3
|
-
A practical, copy-pasteable tour of mu (current main; v0.
|
|
3
|
+
A practical, copy-pasteable tour of mu (current main; v0.4.1).
|
|
4
4
|
Everything below works against the built CLI. Terms are canonical
|
|
5
5
|
— see [VOCABULARY.md](VOCABULARY.md) for definitions; the complete
|
|
6
6
|
current verb list is in `## CLI — complete verb list` of
|
|
7
7
|
[skills/mu/SKILL.md](../skills/mu/SKILL.md).
|
|
8
8
|
|
|
9
|
-
> **Status:** v0.
|
|
9
|
+
> **Status:** v0.4.1 (pre-1.0). ~65 typed verbs across 9
|
|
10
10
|
> namespaces (`workstream`, `agent`, `task`, `workspace`, `log`,
|
|
11
|
-
> `snapshot`, `archive`, `me`) plus bare top-level verbs
|
|
12
|
-
> (`state`, `doctor`, `sql`, `undo
|
|
13
|
-
>
|
|
14
|
-
>
|
|
15
|
-
>
|
|
16
|
-
>
|
|
17
|
-
>
|
|
18
|
-
> `mu
|
|
19
|
-
>
|
|
20
|
-
>
|
|
21
|
-
>
|
|
11
|
+
> `snapshot`, `archive`, `db`, `me`) plus bare top-level verbs
|
|
12
|
+
> (`state`, `doctor`, `sql`, `undo`). Every verb accepts `--json`
|
|
13
|
+
> (one allow-listed exception, `mu agent attach`), per-agent VCS
|
|
14
|
+
> workspaces (jj/sl/git/none), activity log with `--tail`
|
|
15
|
+
> subscription, bare `mu` TTY dashboard, canonical static state card
|
|
16
|
+
> (`mu state` default / `--tui` render modes), whole-DB snapshots
|
|
17
|
+
> auto-captured before destructive verbs + `mu undo` /
|
|
18
|
+
> `mu snapshot {list,show}`, evidence on lifecycle verbs, schema v8
|
|
19
|
+
> (v5 surrogate INTEGER PKs + per-workstream UNIQUE on
|
|
20
|
+
> operator-facing names; v6 added the `archive_*` family additively;
|
|
21
|
+
> v7 dropped the dead `approvals` table; v8 adds `machine_identity`
|
|
22
|
+
> and `workstream_sync` for db sync).
|
|
22
23
|
> See [CHANGELOG.md](../CHANGELOG.md) for the release entry,
|
|
23
|
-
> and [§ Not in 0.
|
|
24
|
+
> and [§ Not in 0.4.1](#whats-not-in-041-and-how-to-work-around-it)
|
|
24
25
|
> at the bottom for the gaps that still need workarounds.
|
|
25
26
|
|
|
26
27
|
*If anything below disagrees with `mu --help`, trust `mu --help`.*
|
|
@@ -33,7 +34,8 @@ current verb list is in `## CLI — complete verb list` of
|
|
|
33
34
|
2. [Get oriented (`mu doctor`)](#2-get-oriented)
|
|
34
35
|
3. [Create a workstream (`mu workstream init`)](#3-create-a-workstream)
|
|
35
36
|
4. [Plan some work as a DAG (`mu task add`)](#4-plan-some-work-as-a-dag)
|
|
36
|
-
5. [See the graph (
|
|
37
|
+
5. [See the graph (dashboard + state API)](#5-see-the-graph-dashboard--state-api)
|
|
38
|
+
5b. [The TUI dashboard (interactive)](#5b-the-tui-dashboard-interactive)
|
|
37
39
|
6. [Spawn a crew (`mu agent spawn`)](#6-spawn-a-crew)
|
|
38
40
|
7. [Watch the crew live (`tmux attach`)](#7-watch-the-crew-live)
|
|
39
41
|
8. [Send work to an agent (`mu agent send`)](#8-send-work-to-an-agent)
|
|
@@ -45,9 +47,10 @@ current verb list is in `## CLI — complete verb list` of
|
|
|
45
47
|
14. [Recovery scenarios](#14-recovery-scenarios)
|
|
46
48
|
15. [Cleanup](#15-cleanup)
|
|
47
49
|
15.5. [Archives — cross-workstream preservation](#155-archives--cross-workstream-preservation-of-task-graphs)
|
|
50
|
+
15.6. [Multi-machine sync](#156-multi-machine-sync)
|
|
48
51
|
16. [One-shot demo script](#16-one-shot-demo-script)
|
|
49
52
|
17. [Mental model in three sentences](#mental-model-in-three-sentences)
|
|
50
|
-
18. [What's NOT in 0.
|
|
53
|
+
18. [What's NOT in 0.4.1](#whats-not-in-041-and-how-to-work-around-it)
|
|
51
54
|
19. [Where to go from here](#where-to-go-from-here)
|
|
52
55
|
|
|
53
56
|
---
|
|
@@ -140,6 +143,32 @@ tmux # if you're not already in one
|
|
|
140
143
|
|
|
141
144
|
## 2. Get oriented
|
|
142
145
|
|
|
146
|
+
For a human at an interactive terminal, bare `mu` is the home base:
|
|
147
|
+
it launches the read-only TUI with every workstream on the machine
|
|
148
|
+
loaded as tabs. Initial tab focus uses this ladder: `$MU_SESSION` when
|
|
149
|
+
it names a loaded workstream; then the current tmux session name when
|
|
150
|
+
it is `mu-<workstream>`; then best-effort cwd detection against
|
|
151
|
+
registered workspace paths; then cwd equal to the VCS-derived project
|
|
152
|
+
root of any loaded workstream's workspaces (ties broken by most-recent
|
|
153
|
+
workstream activity); then tab 0. If no workstream exists yet, it
|
|
154
|
+
prints help plus the one-paste start command:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
mu
|
|
158
|
+
# Get started: mu workstream init <name>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
For scripts, agents, CI, and pipes, bare `mu` deliberately does NOT
|
|
162
|
+
enter the TUI: when stdout is not a TTY it prints `mu --help`. Use
|
|
163
|
+
explicit typed verbs and `--json` for the API surface:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
mu state -w <workstream> --json
|
|
167
|
+
MU_NO_TUI=1 mu # force the non-TTY/help path even in a terminal
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Run the diagnostic once to check tmux + DB health:
|
|
171
|
+
|
|
143
172
|
```bash
|
|
144
173
|
mu doctor
|
|
145
174
|
```
|
|
@@ -222,9 +251,10 @@ $ mu task list -w foo --json
|
|
|
222
251
|
```
|
|
223
252
|
|
|
224
253
|
`count` is `items.length` pre-computed so `jq '.count'` is one less
|
|
225
|
-
hop than `jq '.items | length'`. Future siblings
|
|
226
|
-
|
|
227
|
-
|
|
254
|
+
hop than `jq '.items | length'`. Future siblings layer on without
|
|
255
|
+
breaking the existing two fields. Today `mu workspace commits --json`
|
|
256
|
+
also includes `vcs`, `baseRef`, and `workspacePath` siblings because
|
|
257
|
+
that verb already computes the workspace's fork metadata.
|
|
228
258
|
|
|
229
259
|
Applies to: `mu task list / next / owned-by / notes`,
|
|
230
260
|
`mu workstream list`, `mu workstream destroy --empty` (dry-run),
|
|
@@ -329,8 +359,8 @@ Tasks have **mandatory** `impact` (1–100) and `effort-days` (>0).
|
|
|
329
359
|
Edges are blocks-relationships, modelled as **`--blocked-by`** on `mu
|
|
330
360
|
task add` (and `mu task reparent`): `--blocked-by design` means "this
|
|
331
361
|
task is blocked by `design`; it can't start until `design` closes."
|
|
332
|
-
Tasks are **scoped to a workstream** —
|
|
333
|
-
tasks for the workstream you're
|
|
362
|
+
Tasks are **scoped to a workstream** — the dashboard and state views only show
|
|
363
|
+
tasks for the workstream you're viewing.
|
|
334
364
|
|
|
335
365
|
```bash
|
|
336
366
|
# --workstream can be omitted if you're inside the workstream's tmux
|
|
@@ -357,112 +387,299 @@ Each task validates its id (`/^[a-z][a-z0-9_-]{0,63}$/`) and rejects
|
|
|
357
387
|
duplicates. If you tried `mu task add x --blocked-by y` while `y`
|
|
358
388
|
already transitively depended on `x`, mu would refuse with a `CycleError`.
|
|
359
389
|
|
|
360
|
-
**Task ids are
|
|
361
|
-
|
|
362
|
-
|
|
390
|
+
**Task ids are per-workstream unique.** The same local id can exist in
|
|
391
|
+
multiple workstreams, so cross-workstream references use the qualified
|
|
392
|
+
form `<workstream>/<id>` when a global scope is needed. Blocks-edges
|
|
393
|
+
are always same-workstream — if a blocker resolves outside the target
|
|
363
394
|
workstream, mu refuses with a `CrossWorkstreamEdgeError`.
|
|
364
395
|
|
|
365
396
|
---
|
|
366
397
|
|
|
367
|
-
## 5. See the graph (
|
|
398
|
+
## 5. See the graph (dashboard + state API)
|
|
368
399
|
|
|
369
|
-
|
|
370
|
-
mu --workstream auth-refactor
|
|
371
|
-
# or, if your tmux session is mu-auth-refactor:
|
|
372
|
-
mu
|
|
373
|
-
```
|
|
400
|
+
`mu` exposes one logical "what's going on?" view with two renderers:
|
|
374
401
|
|
|
375
|
-
|
|
376
|
-
|
|
402
|
+
| Surface | Use it for |
|
|
403
|
+
| -------------------- | --------------------------------------------------------------- |
|
|
404
|
+
| **bare `mu`** | A human at a terminal — launches the interactive TUI dashboard. |
|
|
405
|
+
| **`mu state --tui`** | Same TUI, explicitly opt-in. Useful in scripts / aliases. |
|
|
406
|
+
| **`mu state`** | Static text card. JSON-friendly; pipeable; `watch`-able. |
|
|
407
|
+
| **`mu state --json`** | The canonical full snapshot. Agents and scripts read this. |
|
|
377
408
|
|
|
378
|
-
|
|
379
|
-
|
|
409
|
+
The interactive surface is large enough to deserve its own section —
|
|
410
|
+
see [§ 5b. The TUI dashboard](#5b-the-tui-dashboard-interactive)
|
|
411
|
+
immediately below. The rest of this section is the static / JSON
|
|
412
|
+
contract.
|
|
380
413
|
|
|
381
|
-
|
|
382
|
-
Track 1: review (3 tasks, 1 ready, track)
|
|
414
|
+
### Static state card (`mu state`)
|
|
383
415
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
found. When two goals share a prerequisite, mu collapses them into
|
|
399
|
-
ONE track ("merged") so two agents are never assigned tasks that
|
|
400
|
-
share a dependency
|
|
401
|
-
- **Ready** — actionable now, sorted by ROI (impact / effort)
|
|
402
|
-
|
|
403
|
-
### `mu state` render modes (default, `--hud`, `--mission`)
|
|
404
|
-
|
|
405
|
-
`mu state` is one verb with three render modes — same data set,
|
|
406
|
-
different presentation strategy. The flag picks the renderer; the
|
|
407
|
-
JSON shape (`--json`) follows render mode (full vs stripped).
|
|
408
|
-
|
|
409
|
-
```bash
|
|
410
|
-
mu state # default: full top-to-bottom card
|
|
411
|
-
mu state --hud # dynamic-fit budget renderer (watch / popup / status-bar)
|
|
412
|
-
mu state --mission # stripped 5-col glance card
|
|
413
|
-
mu # bare alias for `mu state --mission`
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
- **default (full card)** — every section: agents + orphans + tracks +
|
|
417
|
-
ready / in-progress / blocked / recent-closed + workspaces + recent
|
|
418
|
-
events. JSON-first by design (per Ilya's council critique: state
|
|
419
|
-
cards as the default attention surface; SQL/raw verbs as the
|
|
420
|
-
escape hatch underneath).
|
|
421
|
-
|
|
422
|
-
- **`--hud`** — dynamic table layout that fills the terminal (or tmux
|
|
423
|
-
pane) height + width with as much useful data as fits.
|
|
424
|
-
`watch -n 5 mu state --hud -w X` for a refreshing pane;
|
|
425
|
-
`tmux display-popup -E 'mu state --hud -w X'` for an on-demand
|
|
426
|
-
popup; `#(mu state --hud -w X --json) | jq ...` for tmux
|
|
427
|
-
status-bar interpolation. Sections (priority order):
|
|
428
|
-
header / agents / ready / in-progress / tracks / recent-events.
|
|
429
|
-
Truncated tables get a `… +N more (<verb>)` footer; lower-priority
|
|
430
|
-
sections that can't fit are skipped entirely. Drops blocked /
|
|
431
|
-
recent-closed / workspaces (not glanceable); operator drops
|
|
432
|
-
`--hud` to see them.
|
|
433
|
-
|
|
434
|
-
- **`--mission`** — stripped 5-col glance card (agents + orphans +
|
|
435
|
-
tracks + ready). The bare-`mu` muscle-memory orient call
|
|
436
|
-
("what's going on?"). The full card with blocked / recent-closed /
|
|
437
|
-
workspaces is too much for that intent; `--mission` is the
|
|
438
|
-
intentional minimum-viable orient view.
|
|
439
|
-
|
|
440
|
-
`--hud` and `--mission` are mutually exclusive.
|
|
441
|
-
|
|
442
|
-
Multi-workstream: pass `-w` multiple values to render N workstreams
|
|
443
|
-
in one card. `-w a,b,c`, `-w a -w b`, or any mix all work — see
|
|
444
|
-
[CLI conventions](#cli-conventions-multi-value-flags). `--all` is
|
|
445
|
-
sugar for "every workstream on this machine" (mutually exclusive with
|
|
446
|
-
`-w`). N≥2 in `--hud` mode unions the per-ws sections with a leading
|
|
447
|
-
`workstream` column; in default + `--mission` modes N≥2 stacks one
|
|
448
|
-
per-workstream card after another. The `--json` envelope wraps in
|
|
449
|
-
`{ workstreams: [...] }` when N≥2.
|
|
450
|
-
|
|
451
|
-
JSON shapes (per render mode):
|
|
416
|
+
For an agent/script or a static capture, use explicit `mu state`:
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
mu state -w auth-refactor # human-readable card
|
|
420
|
+
mu state -w auth-refactor --json # full snapshot
|
|
421
|
+
mu state --all --json # every workstream on this machine
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
The static card includes every section the TUI cards summarize:
|
|
425
|
+
agents + orphans + tracks + ready / in-progress / blocked /
|
|
426
|
+
recent-closed tasks + workspaces + recent events. `--json` emits the
|
|
427
|
+
same full snapshot shape regardless of `--tui`.
|
|
428
|
+
|
|
429
|
+
**JSON shapes**
|
|
452
430
|
|
|
453
431
|
- `mu state --json` (single-ws): flat `{ workstreamName, agents,
|
|
454
432
|
orphans, tracks, ready, blocked, inProgress, recentClosed,
|
|
455
433
|
workspaces, recent }`.
|
|
456
|
-
- `mu state --
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
434
|
+
- `mu state --json` (multi-ws): wrapped `{ workstreams: [{...}, ...] }`.
|
|
435
|
+
- bare `mu --json`: prints `--help` rather than entering the TUI;
|
|
436
|
+
use `mu state --json` for the full snapshot.
|
|
437
|
+
- `--tui` is render-only and incompatible with `--json` (the TUI
|
|
438
|
+
has no JSON shape).
|
|
439
|
+
|
|
440
|
+
**Multi-workstream**: pass `-w` multiple values, or `--all`. See
|
|
441
|
+
[CLI conventions](#cli-conventions-multi-value-flags). In static
|
|
442
|
+
mode N≥2 stacks one per-workstream card after another.
|
|
443
|
+
|
|
444
|
+
> **Migrating from old state surfaces**: `mu state --hud` and
|
|
445
|
+
> `mu state --mission` were removed in v0.4; use `mu state --tui`
|
|
446
|
+
> for the interactive surface and `mu state --json` for the full
|
|
447
|
+
> snapshot. `tmux display-popup -E 'mu state -w X'` keeps working
|
|
448
|
+
> unchanged for popup-card use.
|
|
461
449
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
## 5b. The TUI dashboard (interactive)
|
|
453
|
+
|
|
454
|
+
The interactive TUI is mu's flagship human surface. It is **read-only
|
|
455
|
+
by design** — every act-intent `y`anks the canonical `mu` command to
|
|
456
|
+
your clipboard so you run mutations from your shell, with one
|
|
457
|
+
documented escape (`t` in git-show drills runs `tuicr` in the project
|
|
458
|
+
root, see below).
|
|
459
|
+
|
|
460
|
+
### Launch
|
|
461
|
+
|
|
462
|
+
```bash
|
|
463
|
+
mu # bare; opens the TUI when stdout is a TTY
|
|
464
|
+
mu state --tui # explicit; same surface
|
|
465
|
+
mu state --tui -w a,b # restrict to specific workstreams
|
|
466
|
+
mu state --tui --all # all workstreams (default for bare `mu`)
|
|
467
|
+
MU_NO_TUI=1 mu # force the help path even in a TTY
|
|
468
|
+
mu --json # also forces help; pipe `mu state --json`
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
Quit with `q` or `Ctrl-C`. The dashboard restores your scrollback
|
|
472
|
+
on exit (alt-screen).
|
|
473
|
+
|
|
474
|
+
**Initial active tab** is picked from this ladder:
|
|
475
|
+
`$MU_SESSION` → current tmux session name (`mu-<ws>`) → cwd inside a
|
|
476
|
+
registered workspace → cwd at the VCS-derived project root of any
|
|
477
|
+
workstream (newest activity wins ties) → tab 0.
|
|
478
|
+
|
|
479
|
+
### Layout: 10 cards, responsive columns
|
|
480
|
+
|
|
481
|
+
The dashboard renders 10 toggleable cards with rounded borders and
|
|
482
|
+
section headers inset into the top border line (lazygit / btop / k9s
|
|
483
|
+
convention):
|
|
484
|
+
|
|
485
|
+
| Slot | Card | Toggle | Popup | Content |
|
|
486
|
+
| ---- | ------------- | ------ | --------- | ---------------------------------------------------- |
|
|
487
|
+
| 0 | Commits | `0` | `Shift+0` | Recent project-root commits (git / jj / sl) |
|
|
488
|
+
| 1 | Agents | `1` | `Shift+1` | Active agents + status + cli + role |
|
|
489
|
+
| 2 | Tracks | `2` | `Shift+2` | Parallel tracks (union-find clusters) |
|
|
490
|
+
| 3 | Ready (Tasks) | `3` | `Shift+3` | Ready-to-claim tasks (no open blockers) |
|
|
491
|
+
| 4 | Activity log | `4` | `Shift+4` | Recent `agent_logs` events |
|
|
492
|
+
| 5 | Workspaces | `5` | `Shift+5` | Per-agent VCS workspaces + behind/dirty status |
|
|
493
|
+
| 6 | In-progress | `6` | `Shift+6` | IN_PROGRESS tasks owned by agents |
|
|
494
|
+
| 7 | Blocked | `7` | `Shift+7` | Tasks with at least one open blocker |
|
|
495
|
+
| 8 | Recent | `8` | `Shift+8` | Recently CLOSED tasks |
|
|
496
|
+
| 9 | Doctor | `9` | `Shift+9` | Cheap health checks (schema, ghosts, orphans, …) |
|
|
497
|
+
| — | DAG | — | `g` | Full task DAG forest (keybind-only) |
|
|
498
|
+
| — | All tasks | — | `t` | Sortable / filterable list of every task |
|
|
499
|
+
|
|
500
|
+
Digit toggles HIDE / SHOW the card on the dashboard; `Shift+digit`
|
|
501
|
+
opens the matching fullscreen popup. **Single-popup invariant**: only
|
|
502
|
+
one popup is visible at a time; `Esc` / `q` returns to the dashboard
|
|
503
|
+
with all toggles + tick rate preserved.
|
|
504
|
+
|
|
505
|
+
**Responsive layout**: cards stack below 120 cols, then reflow into
|
|
506
|
+
pair-aware 2 / 3 / 4-column layouts at 120 / 180 / 240 cols. Each
|
|
507
|
+
visible card gets a dynamic row budget so a noisy list cannot crowd
|
|
508
|
+
out its siblings; overflow shows as `+N more · Shift+N` inset into
|
|
509
|
+
the bottom border. On very short panes, the dashboard culls
|
|
510
|
+
low-priority cards (Doctor → Recent → Workspaces → …) and shows
|
|
511
|
+
`+N cards hidden · resize taller` until the surviving cards fit.
|
|
512
|
+
|
|
513
|
+
Dashboard ordering is slot-stable: within each rendered column,
|
|
514
|
+
non-stream cards are ordered by toggle digit ascending; stream cards
|
|
515
|
+
(Commits, Activity log) sit as natural trailers, with slot 0 trailing
|
|
516
|
+
last.
|
|
517
|
+
|
|
518
|
+
### Multi-workstream tabs
|
|
519
|
+
|
|
520
|
+
When the TUI is launched with N≥2 workstreams (e.g. bare `mu` on a
|
|
521
|
+
machine with multiple workstreams, or `mu state --tui -w a,b,c`),
|
|
522
|
+
a compact tab strip renders above the cards:
|
|
523
|
+
|
|
524
|
+
```
|
|
525
|
+
workstreams: ▸ auth-refactor · ui-rewrite · demo (Tab / Shift-Tab)
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
- `Tab` cycles forward, `Shift-Tab` backward (suppressed inside
|
|
529
|
+
popups so the same key still navigates inside popups that bind
|
|
530
|
+
it locally).
|
|
531
|
+
- The active tab name appears in the status bar's right zone next
|
|
532
|
+
to the tick rate.
|
|
533
|
+
- Cards / popups always operate on the active tab — there's no
|
|
534
|
+
per-row workstream column.
|
|
535
|
+
- For N=1 the strip renders nothing (frame is byte-identical to
|
|
536
|
+
single-ws TUI).
|
|
537
|
+
- When the workstream set is wider than the terminal, the strip
|
|
538
|
+
windows around the active tab and shows `‹N` / `›N` counters
|
|
539
|
+
for hidden workstreams.
|
|
540
|
+
|
|
541
|
+
### Popup drills
|
|
542
|
+
|
|
543
|
+
`Enter` in any list popup drills into the focused row. Where the
|
|
544
|
+
row is itself an entity (a task), a further `Enter` chains into the
|
|
545
|
+
shared read-only task-detail leaf (notes timeline):
|
|
546
|
+
|
|
547
|
+
- **Tracks popup (`Shift+2`)**: list of tracks → `Enter` opens the
|
|
548
|
+
track's task list → `Enter` opens that task's notes timeline.
|
|
549
|
+
- **Ready / In-progress / Blocked / Recent / All-tasks**:
|
|
550
|
+
list of tasks → `Enter` opens notes; `y` yanks `mu task show <id>`
|
|
551
|
+
(or `mu task claim` / `mu task close` / `mu task tree` depending
|
|
552
|
+
on popup).
|
|
553
|
+
- **Activity log popup (`Shift+4`)**: list of events → `Enter`
|
|
554
|
+
drills into the full untruncated payload of the focused event;
|
|
555
|
+
`y` yanks `mu log --since <seq-1> -n 1 -w <ws>`.
|
|
556
|
+
- **Workspaces popup (`Shift+5`)**: list of workspaces → `Enter`
|
|
557
|
+
opens the commits-since-fork list → `Enter` on a commit opens
|
|
558
|
+
the inline `git show <sha> --stat -p` view; `y` yanks `git show
|
|
559
|
+
<sha>`; `t` launches `tuicr -r <sha>`.
|
|
560
|
+
- **Commits popup (`Shift+0`)**: project-root recent commits →
|
|
561
|
+
`Enter` opens the backend's show view; `y` yanks the show
|
|
562
|
+
command; `t` launches `tuicr`.
|
|
563
|
+
- **Doctor popup (`Shift+9`)**: list of checks → `Enter` opens
|
|
564
|
+
the remediation paragraph for the focused check.
|
|
565
|
+
- **DAG popup (`g`)**: keybind-only; renders the active workstream's
|
|
566
|
+
full task DAG forest (one ASCII subtree per root, diamond-collapse
|
|
567
|
+
marker on repeated nodes).
|
|
568
|
+
|
|
569
|
+
One `Esc` / `q` backs out per recursion level. Drills auto-refresh
|
|
570
|
+
in step with the dashboard tick (fast 1s for SQL-derived bodies
|
|
571
|
+
like notes, slow 10s for subprocess git-show / scrollback). Scroll
|
|
572
|
+
position is preserved across refreshes; subprocess loaders keep the
|
|
573
|
+
prior body visible until the new one arrives so there's no
|
|
574
|
+
blank-flash mid-refetch.
|
|
575
|
+
|
|
576
|
+
### Search / filter
|
|
577
|
+
|
|
578
|
+
`/` inside any list popup enters an incremental case-insensitive
|
|
579
|
+
substring filter (lazygit / k9s convention):
|
|
580
|
+
|
|
581
|
+
- Every printable character appends to the query; `Backspace` pops
|
|
582
|
+
one.
|
|
583
|
+
- `Esc` cancels (clears the query); `Enter` commits (keeps the
|
|
584
|
+
filter applied while letting `j/k` resume normal navigation).
|
|
585
|
+
- Press `/` again on a committed filter to refine.
|
|
586
|
+
- The filter blob is per-popup: agent name + status + cli + role;
|
|
587
|
+
track head id + title; task name + title + status + owner; log
|
|
588
|
+
verb + payload + source.
|
|
589
|
+
- Filter state is per-popup and dies with the popup.
|
|
590
|
+
|
|
591
|
+
Task-list popups also expose **per-status toggles** (`o` / `i` / `c`
|
|
592
|
+
/ `r` / `d` toggle OPEN / IN_PROGRESS / CLOSED / REJECTED / DEFERRED
|
|
593
|
+
visibility; default all-on).
|
|
594
|
+
|
|
595
|
+
The All-tasks popup adds **sort cycle** on `s`: `roi` → `recency`
|
|
596
|
+
→ `age` → `id`.
|
|
597
|
+
|
|
598
|
+
### Mouse
|
|
599
|
+
|
|
600
|
+
Navigation-in only:
|
|
601
|
+
|
|
602
|
+
- Double-click a dashboard card → opens its popup.
|
|
603
|
+
- Scroll-wheel inside a popup list / drill body → moves the focused
|
|
604
|
+
cursor / scrolls the body.
|
|
605
|
+
- Double-click a popup row → drills one level deeper.
|
|
606
|
+
|
|
607
|
+
There is intentionally **no mouse back binding** — use `Esc` / `q`
|
|
608
|
+
to back out predictably.
|
|
609
|
+
|
|
610
|
+
### Yank contract (`y`) and the `tuicr` escape (`t`)
|
|
611
|
+
|
|
612
|
+
Every popup row exposes one canonical `mu` command via `y`. The
|
|
613
|
+
command goes to your system clipboard (pbcopy / wl-copy / xclip /
|
|
614
|
+
xsel / clip.exe with OSC-52 fallback). You run it in your shell.
|
|
615
|
+
The TUI never executes a mutation.
|
|
616
|
+
|
|
617
|
+
The one user-driven escape from the read-only pledge is **`t`**
|
|
618
|
+
inside any `git show` drill (Workspaces popup or Commits popup):
|
|
619
|
+
mu suspends its alt-screen, runs `tuicr -r <sha>` in the project
|
|
620
|
+
root / workspace cwd, then restores the dashboard when tuicr exits.
|
|
621
|
+
This is a deliberate handoff — the operator drives another TUI
|
|
622
|
+
tool, not mu performing the mutation.
|
|
623
|
+
|
|
624
|
+
Task-list cards and popups colour-code status cells consistently
|
|
625
|
+
with the static CLI tables: OPEN cyan, IN_PROGRESS yellow, CLOSED
|
|
626
|
+
green, REJECTED red, DEFERRED dim/gray.
|
|
627
|
+
|
|
628
|
+
### Polling tiers
|
|
629
|
+
|
|
630
|
+
The dashboard has two refresh tiers:
|
|
631
|
+
|
|
632
|
+
- **Fast tick** (default 1s, adjustable with `+` / `-` / `=` /
|
|
633
|
+
`0`): SQL-only. Refreshes tasks, tracks, workspace registry rows,
|
|
634
|
+
and the activity log.
|
|
635
|
+
- **Slow tick** (10s, fixed): subprocess-backed. Refreshes
|
|
636
|
+
tmux-derived agent liveness / orphans, workspace dirty flags,
|
|
637
|
+
recent project commits, and the Doctor summary.
|
|
638
|
+
|
|
639
|
+
The last slow-tier result is merged into every fast render so cards
|
|
640
|
+
do not flicker through a loading state. `r` / F5 refreshes both
|
|
641
|
+
tiers immediately. Tab / Shift-Tab triggers an eager slow refresh
|
|
642
|
+
for the newly active workstream.
|
|
643
|
+
|
|
644
|
+
### Keymap reference
|
|
645
|
+
|
|
646
|
+
| Mode | Keys | Action |
|
|
647
|
+
| --------- | ---------------------------- | -------------------------------------------------------------- |
|
|
648
|
+
| dashboard | `0`-`9` | toggle card visibility |
|
|
649
|
+
| dashboard | `Shift+0`-`Shift+9` | open the matching popup |
|
|
650
|
+
| dashboard | `g` | open DAG popup (keybind-only) |
|
|
651
|
+
| dashboard | `t` | open All-tasks popup (keybind-only) |
|
|
652
|
+
| dashboard | `Tab` / `Shift-Tab` | cycle workstream tabs (N≥2) |
|
|
653
|
+
| dashboard | `+` / `-` / `=` / `0` | adjust fast tick rate (faster / slower / default / pause) |
|
|
654
|
+
| dashboard | `r` / `F5` | force refresh both tiers |
|
|
655
|
+
| dashboard | `?` / `F1` | open help overlay |
|
|
656
|
+
| any | `q` / `Ctrl-C` | quit (or back out of popup; quits at dashboard) |
|
|
657
|
+
| popup | `j` / `k` | move cursor / scroll |
|
|
658
|
+
| popup | `g` / `G` | jump top / bottom |
|
|
659
|
+
| popup | `Ctrl-D` / `Ctrl-U` | half-page down / up |
|
|
660
|
+
| popup | `PgDn` / `PgUp` | full page |
|
|
661
|
+
| popup | `Enter` | drill into focused row |
|
|
662
|
+
| popup | `Esc` / `q` | back out one level |
|
|
663
|
+
| popup | `y` | yank canonical `mu` command for focused row |
|
|
664
|
+
| popup | `/` | enter filter mode |
|
|
665
|
+
| filter | (printable) / `Backspace` | edit query |
|
|
666
|
+
| filter | `Esc` | cancel (clear query) |
|
|
667
|
+
| filter | `Enter` | commit (keep filter, return to nav) |
|
|
668
|
+
| task popup| `o` / `i` / `c` / `r` / `d` | toggle OPEN / IN_PROGRESS / CLOSED / REJECTED / DEFERRED |
|
|
669
|
+
| All-tasks | `s` | cycle sort key (roi → recency → age → id) |
|
|
670
|
+
| git-show | `t` | launch `tuicr -r <sha>` (alt-screen handoff) |
|
|
671
|
+
|
|
672
|
+
`?` shows the same table as a scrollable overlay (j/k/Ctrl-D/U/g/G
|
|
673
|
+
also work inside the overlay).
|
|
674
|
+
|
|
675
|
+
### Read-only invariant
|
|
676
|
+
|
|
677
|
+
The TUI never executes a mutation. This is not a feature of the
|
|
678
|
+
implementation; it's a load-bearing pledge in `docs/ROADMAP.md`. If
|
|
679
|
+
a future TUI gesture tempts you to call into the SDK to mutate
|
|
680
|
+
state, file a roadmap entry first — the yank-and-run pattern is the
|
|
681
|
+
intentional cost we pay to keep the TUI inspectable, scriptable, and
|
|
682
|
+
recoverable from any shell.
|
|
466
683
|
|
|
467
684
|
---
|
|
468
685
|
|
|
@@ -630,6 +847,28 @@ MU_SEND_DELAY_MS=300 mu agent send worker-1 "..." # faster, less safe
|
|
|
630
847
|
MU_SEND_DELAY_MS=1000 mu agent send worker-1 "..." # slow remote
|
|
631
848
|
```
|
|
632
849
|
|
|
850
|
+
If the target agent has a workspace that is **stale** (≥10 commits
|
|
851
|
+
behind main — the same red bucket shown in `mu workspace list` and the
|
|
852
|
+
TUI Workspaces card), `mu agent send` prints a yellow stderr warning
|
|
853
|
+
but still sends by default:
|
|
854
|
+
|
|
855
|
+
```bash
|
|
856
|
+
WARN: worker-1 workspace is 14 commits behind main (≥10 = stale)
|
|
857
|
+
Next:
|
|
858
|
+
Refresh first : mu workspace refresh worker-1 -w auth-refactor
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
Use `--strict-staleness` when a wrapper should refuse instead of
|
|
862
|
+
warning:
|
|
863
|
+
|
|
864
|
+
```bash
|
|
865
|
+
mu agent send worker-1 "..." -w auth-refactor --strict-staleness
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
Agents without workspaces are skipped (common for read-only roles).
|
|
869
|
+
`--json` output includes `staleness: null` or `{agentName,
|
|
870
|
+
workstreamName, commitsBehindMain, isStale}`.
|
|
871
|
+
|
|
633
872
|
---
|
|
634
873
|
|
|
635
874
|
## 9. Read what an agent did
|
|
@@ -714,6 +953,29 @@ workstream prefix, `AgentNotFoundError` (exit 3, message names the
|
|
|
714
953
|
workstream) when the named worker doesn't live there. Nothing is
|
|
715
954
|
written on either failure.
|
|
716
955
|
|
|
956
|
+
When `--for` targets an agent with a stale workspace (≥10 commits
|
|
957
|
+
behind main), `mu task claim` warns on stderr and appends a refresh
|
|
958
|
+
hint, but the claim still succeeds by default:
|
|
959
|
+
|
|
960
|
+
```bash
|
|
961
|
+
mu task claim build -w auth-refactor --for worker-2
|
|
962
|
+
# stderr: WARN: worker-2 workspace is 14 commits behind main (≥10 = stale)
|
|
963
|
+
# Next: Refresh first : mu workspace refresh worker-2 -w auth-refactor
|
|
964
|
+
```
|
|
965
|
+
|
|
966
|
+
Pass `--strict-staleness` to refuse the claim instead with typed
|
|
967
|
+
`TaskClaimStaleWorkspaceError` (exit 4). This is useful for scripts
|
|
968
|
+
that should never dispatch work onto a stale parent:
|
|
969
|
+
|
|
970
|
+
```bash
|
|
971
|
+
mu task claim build -w auth-refactor --for worker-2 --strict-staleness
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
`--json` output includes `staleness: null` or `{agentName,
|
|
975
|
+
workstreamName, commitsBehindMain, isStale}`. Bare in-pane claims and
|
|
976
|
+
`--self` claims do not run this check because they do not assign work
|
|
977
|
+
to a named agent via `--for`.
|
|
978
|
+
|
|
717
979
|
### The orchestrator pattern: `--self`
|
|
718
980
|
|
|
719
981
|
Not every action comes from a registered worker pane. Often the
|
|
@@ -723,7 +985,7 @@ a worker pane just for a 5-minute job. Two patterns split here:
|
|
|
723
985
|
|
|
724
986
|
- **Worker** — a pane mu spawned (or you adopted). Has a row in the
|
|
725
987
|
`agents` table. Identity = pane title. Claims with bare
|
|
726
|
-
`mu task claim <id>`. `tasks.
|
|
988
|
+
`mu task claim <id>`. `tasks.owner_id` points at the worker row.
|
|
727
989
|
|
|
728
990
|
- **Actor** — anything that *causes* a state change. Includes
|
|
729
991
|
workers, but also includes the orchestrator. May or may not have
|
|
@@ -745,13 +1007,13 @@ Three actionable next steps. Pick one based on intent:
|
|
|
745
1007
|
```bash
|
|
746
1008
|
# Orchestrator does the work itself (most common):
|
|
747
1009
|
mu task claim some-task --self --evidence "trivial 5-line fix"
|
|
748
|
-
# -> tasks.
|
|
1010
|
+
# -> tasks.owner_id stays NULL
|
|
749
1011
|
# -> agent_logs records 'task claim some-task by pi-mu --self (anonymous)'
|
|
750
1012
|
# -> mu task show surfaces it as 'owner: (self: pi-mu)'
|
|
751
1013
|
|
|
752
1014
|
# Orchestrator dispatches to a worker:
|
|
753
1015
|
mu task claim some-task --for worker-1
|
|
754
|
-
# -> tasks.
|
|
1016
|
+
# -> tasks.owner_id points at worker-1
|
|
755
1017
|
|
|
756
1018
|
# Orchestrator wants to BE a registered worker (rare):
|
|
757
1019
|
mu agent adopt %6441 -w <ws> # only if pane is in mu-<ws> session
|
|
@@ -767,7 +1029,7 @@ to pane title, or `$USER`, or `unknown`):
|
|
|
767
1029
|
mu task claim deploy --self --actor deploy-bot --evidence "prod release"
|
|
768
1030
|
```
|
|
769
1031
|
|
|
770
|
-
When `tasks.
|
|
1032
|
+
When `tasks.owner_id IS NULL` because of `--self`, `mu task show` looks
|
|
771
1033
|
up the most recent `task claim` event for that task and surfaces it:
|
|
772
1034
|
|
|
773
1035
|
```
|
|
@@ -817,7 +1079,12 @@ verb stays useful on un-claimed tasks).
|
|
|
817
1079
|
Or, for ad-hoc shape, the SQL escape hatch:
|
|
818
1080
|
|
|
819
1081
|
```bash
|
|
820
|
-
mu sql "SELECT author, content, created_at
|
|
1082
|
+
mu sql "SELECT n.author, n.content, n.created_at
|
|
1083
|
+
FROM task_notes n
|
|
1084
|
+
JOIN tasks t ON t.id = n.task_id
|
|
1085
|
+
JOIN workstreams w ON w.id = t.workstream_id
|
|
1086
|
+
WHERE t.local_id='design' AND w.name='auth-refactor'
|
|
1087
|
+
ORDER BY n.id"
|
|
821
1088
|
```
|
|
822
1089
|
|
|
823
1090
|
Convention for note content: `KEY: value` lines. Common keys are
|
|
@@ -845,6 +1112,18 @@ task re-enters the ready set (the canonical "hand it back to the
|
|
|
845
1112
|
pool" workflow). `--reopen` is the escape hatch for forcing `OPEN`
|
|
846
1113
|
from `CLOSED` / `REJECTED` / `DEFERRED`.
|
|
847
1114
|
|
|
1115
|
+
When the closing actor has a per-agent workspace and that workspace
|
|
1116
|
+
has uncommitted edits, a successful close adds one extra `Next:` hint
|
|
1117
|
+
reminding the actor to commit before the next wave:
|
|
1118
|
+
|
|
1119
|
+
```bash
|
|
1120
|
+
cd $(mu workspace path worker-1 -w auth-refactor) && git commit -am 'Design auth module'
|
|
1121
|
+
```
|
|
1122
|
+
|
|
1123
|
+
The hint is best-effort: no workspace, a clean workspace, the `none`
|
|
1124
|
+
backend, or a failed VCS dirty check simply omit it. The same
|
|
1125
|
+
`nextSteps` entry is present in `--json` output.
|
|
1126
|
+
|
|
848
1127
|
`--if-ready` is the umbrella-on-wave-done shape: an orchestrator
|
|
849
1128
|
fires `mu task close <umbrella> --if-ready` after each wave-task
|
|
850
1129
|
finishes (or unconditionally as a final action). It's a no-op while
|
|
@@ -885,8 +1164,9 @@ verbs don't cover: ad-hoc joins, manual recovery, exploring schema.
|
|
|
885
1164
|
The schema is 8 core tables (`workstreams`, `agents`, `tasks`,
|
|
886
1165
|
`task_edges`, `task_notes`, `agent_logs`, `vcs_workspaces`,
|
|
887
1166
|
`snapshots`), 5 archive tables (`archives`, `archived_tasks`,
|
|
888
|
-
`archived_edges`, `archived_notes`, `archived_events`),
|
|
889
|
-
(`schema_version`),
|
|
1167
|
+
`archived_edges`, `archived_notes`, `archived_events`), 2 meta tables
|
|
1168
|
+
(`schema_version`, `machine_identity`), 1 sync table
|
|
1169
|
+
(`workstream_sync`), plus three views (`ready`, `blocked`, `goals`):
|
|
890
1170
|
|
|
891
1171
|
```bash
|
|
892
1172
|
mu sql "SELECT name FROM sqlite_master WHERE type IN ('table','view') ORDER BY type, name"
|
|
@@ -936,21 +1216,29 @@ one verify, one workspace recycle:
|
|
|
936
1216
|
# The dispatch-pipeline recipe: cycle until in_flight is empty.
|
|
937
1217
|
in_flight=( mufeedback-v03/foo mufeedback-v03/bar roadmap-v0-3/baz )
|
|
938
1218
|
while (( ${#in_flight[@]} > 0 )); do
|
|
939
|
-
|
|
940
|
-
|
|
1219
|
+
res=$(mu task wait "${in_flight[@]}" --first --timeout 90 --json)
|
|
1220
|
+
closed=$(jq -r '.firing.qualifiedId // empty' <<<"$res")
|
|
941
1221
|
if [[ -z "$closed" ]]; then break; fi # timeout or exit 6 — see below
|
|
942
1222
|
|
|
943
|
-
|
|
944
|
-
# nextSteps array — or use `mu task show` to look up).
|
|
945
|
-
worker=$(mu task show "${closed##*/}" -w "${closed%%/*}" --json | jq -r .ownerName)
|
|
1223
|
+
worker=$(jq -r '.firing.owner // empty' <<<"$res")
|
|
946
1224
|
ws=${closed%%/*}
|
|
947
|
-
|
|
948
|
-
#
|
|
949
|
-
|
|
950
|
-
git cherry-pick
|
|
1225
|
+
|
|
1226
|
+
# 1. Inspect, then run, the sha-pinned apply hint from nextSteps.
|
|
1227
|
+
# When the worker has commits since its fork point, the command is
|
|
1228
|
+
# `git cherry-pick <sha>` (or `<first>^..<last>` for multiple
|
|
1229
|
+
# commits). When the worker closed without committing, nextSteps
|
|
1230
|
+
# says so and points at manual `git diff` / `git apply` rescue.
|
|
1231
|
+
apply=$(jq -r '.nextSteps[0].command' <<<"$res")
|
|
1232
|
+
printf 'apply hint: %s\n' "$apply"
|
|
1233
|
+
if [[ "$apply" == git\ cherry-pick* ]]; then
|
|
1234
|
+
eval "$apply"
|
|
1235
|
+
else
|
|
1236
|
+
echo "manual rescue required; inspect the worker workspace before continuing"
|
|
1237
|
+
break
|
|
1238
|
+
fi
|
|
951
1239
|
|
|
952
1240
|
# 2. Verify
|
|
953
|
-
npm run typecheck && npm run lint && npm run test && npm run build
|
|
1241
|
+
npm run typecheck && npm run lint && npm run test:fast && npm run test && npm run build
|
|
954
1242
|
|
|
955
1243
|
# 3. Refresh the workspace for the next dispatch (rebases onto
|
|
956
1244
|
# fresh main WITHOUT killing the worker's LLM context). Default
|
|
@@ -1058,12 +1346,14 @@ mu sql "UPDATE tasks SET status='IN_PROGRESS'
|
|
|
1058
1346
|
|
|
1059
1347
|
# What's blocking what (open tasks only) — same data as `mu task tree`
|
|
1060
1348
|
# but as a flat join when you want a wider report. task_edges is keyed
|
|
1061
|
-
# by tasks.id, not local_id.
|
|
1349
|
+
# by tasks.id, not local_id; join workstreams to scope the report.
|
|
1062
1350
|
mu sql "SELECT b.local_id AS blocked, t.local_id AS by_task
|
|
1063
1351
|
FROM tasks b
|
|
1352
|
+
JOIN workstreams w ON w.id = b.workstream_id
|
|
1064
1353
|
JOIN task_edges e ON e.to_task_id = b.id
|
|
1065
1354
|
JOIN tasks t ON t.id = e.from_task_id
|
|
1066
|
-
WHERE
|
|
1355
|
+
WHERE w.name='mufeedback-v03'
|
|
1356
|
+
AND t.status != 'CLOSED' AND b.status = 'OPEN'"
|
|
1067
1357
|
|
|
1068
1358
|
# Recursive CTE: every task that transitively blocks `launch` in a
|
|
1069
1359
|
# given workstream (or use `mu task tree launch --json` for the same
|
|
@@ -1348,8 +1638,10 @@ failure leaves the registry intact (you can retry); if you only want
|
|
|
1348
1638
|
the DB cleared, use `mu sql` directly:
|
|
1349
1639
|
|
|
1350
1640
|
```bash
|
|
1351
|
-
mu sql "DELETE FROM tasks
|
|
1352
|
-
|
|
1641
|
+
mu sql "DELETE FROM tasks
|
|
1642
|
+
WHERE workstream_id=(SELECT id FROM workstreams WHERE name='auth-refactor')" # cascades
|
|
1643
|
+
mu sql "DELETE FROM agents
|
|
1644
|
+
WHERE workstream_id=(SELECT id FROM workstreams WHERE name='auth-refactor')"
|
|
1353
1645
|
```
|
|
1354
1646
|
|
|
1355
1647
|
Or nuke the entire DB:
|
|
@@ -1376,7 +1668,7 @@ subdirectory per source workstream:
|
|
|
1376
1668
|
<bucket>/
|
|
1377
1669
|
README.md # bucket-level summary (every source-ws + dates + totals)
|
|
1378
1670
|
INDEX.md # union of all task tables; first column = source-ws
|
|
1379
|
-
manifest.json # bucketVersion: 2
|
|
1671
|
+
manifest.json # bucketVersion: 2, manifest_version: 2, per-source-ws task summaries + sha256s
|
|
1380
1672
|
<source-ws>/
|
|
1381
1673
|
README.md # per-source-ws (counts)
|
|
1382
1674
|
INDEX.md # per-source-ws (table of every task)
|
|
@@ -1386,12 +1678,21 @@ subdirectory per source workstream:
|
|
|
1386
1678
|
Bucket exports are **additive**: `mu workstream export -w X --out
|
|
1387
1679
|
<bucket>` creates the bucket scaffolding plus `X/` on first use,
|
|
1388
1680
|
and a follow-up call with `-w Y --out <same-bucket>` appends a
|
|
1389
|
-
sibling `Y/` subdirectory without touching `X/`.
|
|
1681
|
+
sibling `Y/` subdirectory without touching `X/`. The top-level
|
|
1682
|
+
`INDEX.md` is always the union from `manifest.sources`, so a later
|
|
1683
|
+
single-workstream refresh does not drop sibling workstreams from the
|
|
1684
|
+
bucket-wide task table. Re-running with
|
|
1390
1685
|
the same `-w` is sha256-idempotent: only changed task files are
|
|
1391
1686
|
rewritten (mtime preserved on identical files); tasks added since
|
|
1392
1687
|
the previous export get fresh files; tasks deleted from the DB
|
|
1393
1688
|
STAY on disk with a `> **Deleted from DB on <ts>**` banner so you
|
|
1394
|
-
never lose context that may already be git-blamed.
|
|
1689
|
+
never lose context that may already be git-blamed. `manifest_version:
|
|
1690
|
+
2` stores compact task summaries (`name`/`title`/`status`/`impact`/
|
|
1691
|
+
`effortDays`) beside the per-file sha256s; older v1 manifests are
|
|
1692
|
+
accepted on re-export; mu infers the missing summaries from existing
|
|
1693
|
+
per-task markdown when possible, falling back to placeholder values
|
|
1694
|
+
only if a task file is missing or unreadable, so the bucket remains
|
|
1695
|
+
appendable.
|
|
1395
1696
|
|
|
1396
1697
|
```bash
|
|
1397
1698
|
# One-shot dump (bucket happens to contain just one source-ws)
|
|
@@ -1427,75 +1728,20 @@ Markdown only by design — no HTML/PDF, no embedded VCS, no
|
|
|
1427
1728
|
cross-workstream merge. Operators can pandoc / `git init`
|
|
1428
1729
|
themselves.
|
|
1429
1730
|
|
|
1430
|
-
###
|
|
1731
|
+
### Bucket exports are read-only artifacts
|
|
1431
1732
|
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
task + edge + note locally.
|
|
1733
|
+
Bucket exports (`mu workstream export` and `mu archive export`) are
|
|
1734
|
+
now **read-only** artifacts for humans / git / docs. They are still
|
|
1735
|
+
excellent for grep, code review, project handoff, and historical
|
|
1736
|
+
write-ups, but they are no longer a load-bearing DB round-trip path.
|
|
1437
1737
|
|
|
1438
|
-
|
|
1439
|
-
# Machine A — author
|
|
1440
|
-
mu workstream export -w auth-refactor --out exports/auth
|
|
1441
|
-
(cd exports/auth && git init && git add . && git commit -m 'auth snapshot')
|
|
1442
|
-
git push origin main
|
|
1738
|
+
Use the typed surfaces for recovery and movement:
|
|
1443
1739
|
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
mu
|
|
1447
|
-
mu
|
|
1448
|
-
|
|
1449
|
-
mu workstream import exports/auth --json # machine-readable per-source-ws result
|
|
1450
|
-
|
|
1451
|
-
# Partial bucket import — multi-source bucket, but you only want
|
|
1452
|
-
# one (or a subset) restored. Two equivalent forms:
|
|
1453
|
-
mu workstream import exports/mu/roadmap-v0-2 # Form 1 — per-source-ws subdir path
|
|
1454
|
-
mu workstream import exports/mu --source-ws roadmap-v0-2 # Form 2 — bucket + filter
|
|
1455
|
-
mu workstream import exports/mu --source-ws auth,ui # Form 2 — X+Y, leave Z behind
|
|
1456
|
-
mu workstream import exports/mu --source-ws auth --source-ws ui # repeat OR comma-separate; or both
|
|
1457
|
-
```
|
|
1458
|
-
|
|
1459
|
-
Key properties:
|
|
1460
|
-
|
|
1461
|
-
- **Markdown-only.** `.db` files are never imported (binary +
|
|
1462
|
-
machine-specific). `mu undo` + snapshot files cover the
|
|
1463
|
-
same-machine case; this verb covers cross-machine + collab.
|
|
1464
|
-
- **Per-source-ws transactional.** Each source-ws subdirectory is
|
|
1465
|
-
imported in its own SQLite transaction. A failure in source A
|
|
1466
|
-
rolls back A; sibling source B is unaffected.
|
|
1467
|
-
- **Refuses silent merges.** If the target workstream already
|
|
1468
|
-
exists in the DB with tasks, the import errors with
|
|
1469
|
-
`WorkstreamAlreadyExistsError`. Recourse:
|
|
1470
|
-
`--workstream <new-name>` (single-source buckets only) or
|
|
1471
|
-
destroy the existing workstream first.
|
|
1472
|
-
- **Owners reset.** Agents aren't exported, so the imported tasks
|
|
1473
|
-
are unowned. The original owner name survives in the markdown
|
|
1474
|
-
frontmatter — that's the audit trail.
|
|
1475
|
-
- **Tombstones skipped.** Files starting with the
|
|
1476
|
-
`> **Deleted from DB on …**` banner (preserved by re-export of
|
|
1477
|
-
a deleted task) are counted as `tombstones_skipped` and not
|
|
1478
|
-
re-inserted.
|
|
1479
|
-
- **Forward edge refs are deferred.** `blocked_by` / `blocks`
|
|
1480
|
-
arrays are validated against the bucket's id-set up front, then
|
|
1481
|
-
inserted after every task in the source-ws is created.
|
|
1482
|
-
- **Partial import.** Multi-source buckets accept either a
|
|
1483
|
-
per-source-ws subdir path (auto-detected via
|
|
1484
|
-
`README.md` + `INDEX.md` + `tasks/` + a parent
|
|
1485
|
-
`manifest.json` listing the subdir as a source) OR a
|
|
1486
|
-
`--source-ws <names...>` filter on the bucket root
|
|
1487
|
-
(variadic per `cli_audit_plurality_uniformity`: repeat,
|
|
1488
|
-
comma-separate, or both). The two forms are equivalent for
|
|
1489
|
-
single-source restores. `--workstream <new-name>` is allowed
|
|
1490
|
-
whenever the resolved source-ws list has exactly one entry
|
|
1491
|
-
(Form 1; or Form 2 with a single name); rejected for
|
|
1492
|
-
multi-source filters. Passing `--source-ws` against a Form 1
|
|
1493
|
-
per-source-ws subdir is refused (the subdir already implies one
|
|
1494
|
-
source). A `--source-ws` name not in the bucket manifest raises
|
|
1495
|
-
`ImportSourceNotInBucketError` (exit 4) and lists the valid
|
|
1496
|
-
names. `--source-ws ',,'` (canonicalises to zero names) is a
|
|
1497
|
-
`UsageError` (exit 2) so a typo doesn't silently fall back to
|
|
1498
|
-
importing the entire bucket.
|
|
1740
|
+
| Need | Verb |
|
|
1741
|
+
| ---- | ---- |
|
|
1742
|
+
| Lossless un-archive | `mu archive restore <label> --as <new-ws> [--source <orig-ws>]` |
|
|
1743
|
+
| Laptop ↔ devserver handoff | `mu db export <file>` + `mu db import <file>` |
|
|
1744
|
+
| Manual recovery from a parked conflict | `mu db replay <sidecar>` |
|
|
1499
1745
|
|
|
1500
1746
|
---
|
|
1501
1747
|
|
|
@@ -1518,7 +1764,8 @@ mu archive add v0-3-wave -w roadmap-v0-3 --destroy # cascade: archive THEN des
|
|
|
1518
1764
|
mu archive list # label | tasks | sources | created | last_added
|
|
1519
1765
|
mu archive show v0-3-wave # detail card + per-source-workstream summary
|
|
1520
1766
|
mu archive search 'oauth' [--label v0-3-wave] # LIKE-search archived titles + note content (--limit N, --json)
|
|
1521
|
-
mu archive
|
|
1767
|
+
mu archive restore v0-3-wave --as restored-auth --source auth-refactor
|
|
1768
|
+
mu archive export v0-3-wave --out exports/v0-3-wave # read-only markdown bucket for humans/git/docs
|
|
1522
1769
|
```
|
|
1523
1770
|
|
|
1524
1771
|
Key properties:
|
|
@@ -1526,15 +1773,25 @@ Key properties:
|
|
|
1526
1773
|
- **Globally-unique labels.** Archive labels live in their own
|
|
1527
1774
|
namespace (separate from workstream names). Pick once, reuse
|
|
1528
1775
|
across years.
|
|
1529
|
-
- **
|
|
1530
|
-
idempotent at the (archive, source workstream) granularity
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1776
|
+
- **Snapshot-only accumulation.** `mu archive add <label> -w <ws>` is
|
|
1777
|
+
idempotent at the (archive, source workstream) granularity and is
|
|
1778
|
+
designed for end-of-milestone snapshot-and-destroy flows. Re-running
|
|
1779
|
+
on the same workstream is task-incremental: newly-created tasks are
|
|
1780
|
+
added, but notes and events for already-archived tasks stay pinned
|
|
1781
|
+
to the original snapshot and are NOT refreshed. If you need a full
|
|
1782
|
+
event-stream refresh for a source workstream, remove that source (or
|
|
1783
|
+
delete/re-create the archive label) and add it again. Two different
|
|
1784
|
+
workstreams under the same label coexist as separate
|
|
1785
|
+
`(source_workstream, original_local_id)` rows.
|
|
1535
1786
|
- **Outlives the source.** `archived_tasks.source_workstream` is
|
|
1536
1787
|
TEXT (not an FK), so the source workstream can be destroyed and
|
|
1537
1788
|
the archive's snapshot of it stays queryable forever.
|
|
1789
|
+
- **Lossless un-archive.** `mu archive restore <label> --as <new-ws>
|
|
1790
|
+
[--source <orig-ws>]` copies tasks, edges, and notes directly from
|
|
1791
|
+
`archived_*` tables into a fresh workstream. It refuses if `--as`
|
|
1792
|
+
collides and snapshots before writing. Archives do not snapshot live
|
|
1793
|
+
panes or the live event log, so agents, workspace paths, and
|
|
1794
|
+
`agent_logs` are not restored.
|
|
1538
1795
|
- **Reversible.** `mu archive delete <label> --yes` captures a
|
|
1539
1796
|
snapshot first; `mu undo --yes` brings the whole archive back.
|
|
1540
1797
|
`mu archive remove <label> -w <ws>` is the surgical version
|
|
@@ -1579,9 +1836,9 @@ mu archive add mu-v0-3 -w mufeedback-v03 --destroy
|
|
|
1579
1836
|
- **No "default" / auto-archive.** `mu workstream destroy` does
|
|
1580
1837
|
NOT auto-add to a fallback bucket. Either you picked a label
|
|
1581
1838
|
deliberately or you didn't want one.
|
|
1582
|
-
- **No re-import.** The archive IS the workstream's afterlife.
|
|
1583
|
-
If you need an archived
|
|
1584
|
-
`mu
|
|
1839
|
+
- **No bucket re-import.** The archive IS the workstream's afterlife.
|
|
1840
|
+
If you need an archived source workstream back as live work, use
|
|
1841
|
+
`mu archive restore <label> --as <new-ws> [--source <orig-ws>]`.
|
|
1585
1842
|
- **No archive→archive merge / rename.** Operator-managed via
|
|
1586
1843
|
`mu sql` if it ever matters.
|
|
1587
1844
|
- **Snapshots vs archives are separate concerns.** Snapshots are
|
|
@@ -1591,6 +1848,71 @@ mu archive add mu-v0-3 -w mufeedback-v03 --destroy
|
|
|
1591
1848
|
|
|
1592
1849
|
---
|
|
1593
1850
|
|
|
1851
|
+
## 15.6 Multi-machine sync
|
|
1852
|
+
|
|
1853
|
+
Use `mu db {export,import,replay}` when one user alternates a
|
|
1854
|
+
workstream between two machines (for example laptop ↔ devserver) over
|
|
1855
|
+
multi-day stretches. You own the transport: `rsync`, `scp`, Dropbox,
|
|
1856
|
+
git-lfs, USB, whatever moves a SQLite file plus its manifest.
|
|
1857
|
+
|
|
1858
|
+
**Hard rule / user contract:** do not edit the same workstream on two
|
|
1859
|
+
machines concurrently. Other workstreams may keep moving locally, but
|
|
1860
|
+
for one workstream, finish or release in-flight claims before export:
|
|
1861
|
+
`mu agent list -w <ws>` shows current owners. `mu db import` does not
|
|
1862
|
+
carry owners because `owner_id` points at the machine-local `agents`
|
|
1863
|
+
table.
|
|
1864
|
+
|
|
1865
|
+
```bash
|
|
1866
|
+
# Machine A — export the whole DB copy + ~/Dropbox/mu.db.manifest.json
|
|
1867
|
+
mu db export ~/Dropbox/mu.db --force
|
|
1868
|
+
# ship file (rsync / scp / Dropbox / git-lfs / USB)
|
|
1869
|
+
|
|
1870
|
+
# Machine B — preview first, then commit
|
|
1871
|
+
mu db import ~/Dropbox/mu.db # dry-run preview
|
|
1872
|
+
mu db import ~/Dropbox/mu.db --apply # commits FAST_FORWARD / IMPORT rows
|
|
1873
|
+
```
|
|
1874
|
+
|
|
1875
|
+
Dry-run output is a per-workstream decision table:
|
|
1876
|
+
|
|
1877
|
+
```
|
|
1878
|
+
workstream decision delta
|
|
1879
|
+
----------- ------------ -------------------------------
|
|
1880
|
+
auth FAST_FORWARD source 42, local 39, last_synced 39
|
|
1881
|
+
docs IDENTICAL source 12, local 12, last_synced 12
|
|
1882
|
+
local-only LOCAL_AHEAD source 0, local 7, re-export from this machine
|
|
1883
|
+
experiment CONFLICT source 55, local 58, needs --force-source
|
|
1884
|
+
```
|
|
1885
|
+
|
|
1886
|
+
(The actual CLI also prints the numeric columns separately:
|
|
1887
|
+
`source_seq`, `local_seq`, `last_synced`, and `needs`.)
|
|
1888
|
+
|
|
1889
|
+
Five case branches exist: `IDENTICAL` / `FAST_FORWARD` /
|
|
1890
|
+
`LOCAL_AHEAD` / `CONFLICT` / `IMPORT` (source-only or clean-machine
|
|
1891
|
+
import). `LOCAL_AHEAD` means the incoming file is stale for that
|
|
1892
|
+
workstream; re-export from this machine instead of applying it.
|
|
1893
|
+
`CONFLICT` means both sides advanced since the last sync and mu
|
|
1894
|
+
refuses by default.
|
|
1895
|
+
|
|
1896
|
+
Recovery from an accidental concurrent edit is intentionally sharp:
|
|
1897
|
+
|
|
1898
|
+
```bash
|
|
1899
|
+
mu db import ~/Dropbox/mu.db --apply --force-source
|
|
1900
|
+
# prints a parked loser like:
|
|
1901
|
+
# <state-dir>/divergence/auth-2026-05-14T10:00:00.000Z-a1b2c3d4.db
|
|
1902
|
+
|
|
1903
|
+
mu db replay <state-dir>/divergence/auth-2026-05-14T10:00:00.000Z-a1b2c3d4.db
|
|
1904
|
+
mu db replay <state-dir>/divergence/auth-2026-05-14T10:00:00.000Z-a1b2c3d4.db --task local_fix --apply
|
|
1905
|
+
mu db replay <state-dir>/divergence/auth-2026-05-14T10:00:00.000Z-a1b2c3d4.db --all --apply
|
|
1906
|
+
```
|
|
1907
|
+
|
|
1908
|
+
`--force-source` replaces the whole local workstream from the source
|
|
1909
|
+
file, but first parks the local divergent state as a divergence
|
|
1910
|
+
sidecar. `mu db replay` is the manual cherry-pick tool for that
|
|
1911
|
+
sidecar; it is dry-run by default, idempotent, and refuses when the
|
|
1912
|
+
same `local_id` exists locally with diverged content.
|
|
1913
|
+
|
|
1914
|
+
---
|
|
1915
|
+
|
|
1594
1916
|
## 16. One-shot demo script
|
|
1595
1917
|
|
|
1596
1918
|
Copy-pasteable, end-to-end. Wipes any prior `~/.local/state/mu/mu.db`.
|
|
@@ -1611,8 +1933,8 @@ mu agent spawn worker-1 --workstream demo --cli sh
|
|
|
1611
1933
|
mu agent spawn worker-2 --workstream demo --cli sh
|
|
1612
1934
|
|
|
1613
1935
|
# Assign + observe
|
|
1614
|
-
mu
|
|
1615
|
-
mu
|
|
1936
|
+
mu task claim design -w demo --for worker-1 --evidence "demo assignment"
|
|
1937
|
+
mu state -w demo
|
|
1616
1938
|
|
|
1617
1939
|
# Watch live (Ctrl+b d to detach)
|
|
1618
1940
|
tmux attach -t mu-demo
|
|
@@ -1638,8 +1960,8 @@ rm -f ~/.local/state/mu/mu.db
|
|
|
1638
1960
|
shared deps.
|
|
1639
1961
|
|
|
1640
1962
|
3. **Agents claim tasks via their pane title — zero config.**
|
|
1641
|
-
`mu task claim foo` from inside `worker-1`'s pane sets
|
|
1642
|
-
atomically. mu reads the pane title via
|
|
1963
|
+
`mu task claim foo` from inside `worker-1`'s pane sets the task's
|
|
1964
|
+
`owner_id` to the `worker-1` agent row atomically. mu reads the pane title via
|
|
1643
1965
|
`tmux display-message -t $TMUX_PANE -p '#{pane_title}'`, set on
|
|
1644
1966
|
spawn. Two agents cannot claim the same task.
|
|
1645
1967
|
|
|
@@ -1649,9 +1971,9 @@ service of those three.
|
|
|
1649
1971
|
|
|
1650
1972
|
---
|
|
1651
1973
|
|
|
1652
|
-
## What's NOT in 0.
|
|
1974
|
+
## What's NOT in 0.4.1 (and how to work around it)
|
|
1653
1975
|
|
|
1654
|
-
<a id="whats-not-in-
|
|
1976
|
+
<a id="whats-not-in-050-and-how-to-work-around-it"></a>
|
|
1655
1977
|
|
|
1656
1978
|
The full roadmap with promotion criteria lives in
|
|
1657
1979
|
[ROADMAP.md](ROADMAP.md). The short list of gaps you might hit
|
|
@@ -1660,13 +1982,13 @@ in real use:
|
|
|
1660
1982
|
| Want | Workaround | Status |
|
|
1661
1983
|
| --------------------------------------------- | ----------------------------------------------------------------------- | ------------- |
|
|
1662
1984
|
| Multi-CLI status detection (per-CLI prompts) | Braille spinner fallback (`f68838f`) covers pi/pi-meta + every TUI wrapper using standard spinner glyphs. Per-CLI permission-prompt patterns still pi-only. | partially shipped |
|
|
1663
|
-
| Pi extension (typed tools, HUD, wakeups) | `mu state --
|
|
1985
|
+
| Pi extension (typed tools, HUD, wakeups) | `mu state --tui` (interactive) covers the dashboard use-case; plain `mu state` (static) is the `watch` / `tmux display-popup` / `status-right` substrate. Other extension tools deferred. | partially shipped |
|
|
1664
1986
|
| Markdown agent-definition discovery | Spawn accepts `--cli` and `--command` directly; no template registry | dropped |
|
|
1665
1987
|
| `mu run script.ts` (JS DSL) | Use `--json` + bash + jq | rejected |
|
|
1666
1988
|
| Sync to GitHub Issues / Linear / Asana | Not in scope; explicitly rejected | — |
|
|
1667
|
-
| ~~`mu task blocked`~~ (removed; the `blocked` SQL view is the abstraction) | `mu sql "SELECT local_id, status, title FROM blocked WHERE
|
|
1668
|
-
| ~~`mu task goals`~~ (removed; same shape as `blocked` — view is the abstraction) | `mu sql "SELECT local_id, status, title FROM goals WHERE
|
|
1669
|
-
| ~~`mu task search <pat>`~~ (removed; case-insensitive LIKE is one SQL line) | `mu sql "SELECT local_id, status, title FROM tasks WHERE
|
|
1989
|
+
| ~~`mu task blocked`~~ (removed; the `blocked` SQL view is the abstraction) | `mu sql "SELECT b.local_id, b.status, b.title FROM blocked b JOIN workstreams w ON w.id=b.workstream_id WHERE w.name='X'"` | removed-with-recipe |
|
|
1990
|
+
| ~~`mu task goals`~~ (removed; same shape as `blocked` — view is the abstraction) | `mu sql "SELECT g.local_id, g.status, g.title FROM goals g JOIN workstreams w ON w.id=g.workstream_id WHERE w.name='X'"` | removed-with-recipe |
|
|
1991
|
+
| ~~`mu task search <pat>`~~ (removed; case-insensitive LIKE is one SQL line) | `mu sql "SELECT t.local_id, t.status, t.title FROM tasks t JOIN workstreams w ON w.id=t.workstream_id WHERE w.name='X' AND LOWER(t.title) LIKE '%pat%'"` (add `LEFT JOIN task_notes` for the old `--in-notes`; drop the workstream join/filter for the old `--all`) | removed-with-recipe |
|
|
1670
1992
|
|
|
1671
1993
|
Anything in this table that bites you in real use is a candidate
|
|
1672
1994
|
for **promotion**. Criteria: proven friction in ≥2 real workflows +
|