@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 CHANGED
@@ -13,8 +13,9 @@ write code. Follow the conventions below.
13
13
 
14
14
  1. **[docs/USAGE_GUIDE.md](docs/USAGE_GUIDE.md)** — what mu does
15
15
  from a user's perspective. ~10 minutes.
16
- 2. **[CHANGELOG.md](CHANGELOG.md)** — the v0.1.0 release entry.
17
- Single source of truth for the verb list, schema, env vars.
16
+ 2. **[CHANGELOG.md](CHANGELOG.md)** — the upcoming version's
17
+ entry (currently `[0.4.1] 2026-05-14`). Single source of truth
18
+ for the verb list, schema, env vars.
18
19
  3. **[docs/VISION.md](docs/VISION.md)** — the load-bearing pillars.
19
20
  The design principles you must not violate.
20
21
  4. **[docs/ROADMAP.md](docs/ROADMAP.md)** — what's next, with
@@ -24,13 +25,22 @@ write code. Follow the conventions below.
24
25
  **Source of truth for every word** in code, docs, and error
25
26
  messages. If you use a term not defined there, fix the docs first.
26
27
  6. **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)** — module layout,
27
- reconciliation algorithm, key seams.
28
+ reconciliation algorithm, TUI architecture, key seams.
28
29
 
29
30
  Design rationale for rejected and unbuilt features (DSL, snapshots,
30
31
  task_artifacts, ...) is now folded into
31
32
  [docs/ROADMAP.md](docs/ROADMAP.md) per item, alongside its
32
33
  promotion criteria.
33
34
 
35
+ > **If you are an orchestrator** — a coding agent driving a crew of
36
+ > pi worker agents on this repo via `mu` — read
37
+ > **[docs/HANDOVER.md](docs/HANDOVER.md)** instead of the order
38
+ > above. It is the goto reset doc for orchestrators: onboarding
39
+ > steps, the 8-phase dispatch loop, conflict-resolution playbook,
40
+ > known gotchas, and end-of-session checklist. AGENTS.md is for
41
+ > workers (and humans editing the repo directly); HANDOVER.md is for
42
+ > orchestrators.
43
+
34
44
  ---
35
45
 
36
46
  ## Repo layout
@@ -41,39 +51,53 @@ mu/
41
51
  ├── AGENTS.md # this file
42
52
  ├── CHANGELOG.md # release notes
43
53
  ├── docs/ # everything else
44
- │ ├── USAGE_GUIDE.md
45
- │ ├── ROADMAP.md # what's next; promotion criteria
46
- │ ├── VISION.md
47
- │ ├── VOCABULARY.md
48
- └── ARCHITECTURE.md
54
+ │ ├── USAGE_GUIDE.md # user-facing tour (§ 5b is the TUI reference)
55
+ │ ├── HANDOVER.md # orchestrator goto reset doc (8-phase loop + gotchas)
56
+ │ ├── ROADMAP.md # what's next; promotion criteria; anti-feature pledges
57
+ │ ├── VISION.md # load-bearing pillars
58
+ ├── VOCABULARY.md # canonical terms (single source of truth)
59
+ │ └── ARCHITECTURE.md # module layout, TUI architecture, key seams
49
60
  ├── src/ # all source (root files: SDK + shared infra; one
50
61
  │ # level of subdirs OK for cohesive clusters — see
51
62
  │ # `src/cli/`, `src/agents/`, `src/tasks/` below)
52
- │ ├── db.ts # SQLite schema + openDb (single CREATE-IF-NOT-EXISTS block)
63
+ │ ├── db.ts # SQLite schema + openDb (single CREATE-IF-NOT-EXISTS block; v8)
53
64
  │ ├── tmux.ts # tmux wrapper, send protocol, pane validation
54
- │ ├── detect.ts # pi-only status detector
65
+ │ ├── detect.ts # pi status detector + Braille-spinner fallback for other CLIs
55
66
  │ ├── reconcile.ts # ghost prune + status detect + orphan surface
56
67
  │ ├── agents.ts # CRUD + send/read/list/close/free + liveness + reaper hub (re-exports src/agents/*)
57
68
  │ ├── agents/ # cohesive cluster of agent-lifecycle internals
58
69
  │ │ ├── spawn.ts # spawnAgent + resolveCliCommand / awaitSpawnLiveness / pane create-or-reuse / prestage / rollback
70
+ │ │ ├── kick.ts # reaper events + cleanup of dead agent rows
59
71
  │ │ ├── adopt.ts # adoptAgent: register an existing tmux pane as a managed agent
60
72
  │ │ └── errors.ts # typed agent error classes (AgentNotFoundError, AgentDiedOnSpawnError, …)
61
- │ ├── tasks.ts # task SDK hub (re-exports src/tasks/* + edit/edges/queries verbs)
73
+ │ ├── tasks.ts # task SDK hub (re-exports src/tasks/*)
62
74
  │ ├── tasks/ # cohesive cluster of task-graph internals
63
- │ │ ├── status.ts # TaskStatus enum + helpers (single source of truth for statuses)
64
- │ │ ├── claim.ts # claim/release + resolveActorIdentity (atomic CAS)
75
+ │ │ ├── status.ts # TaskStatus enum + helpers
76
+ │ │ ├── core.ts # core SDK funcs reused across cluster files
77
+ │ │ ├── id.ts # tryResolveTaskId / qualified-id helpers
78
+ │ │ ├── queries.ts # listTasks / nextTasks / owned-by
79
+ │ │ ├── edit.ts # addTask / setTaskTitle / etc (no edges)
80
+ │ │ ├── edges.ts # block / unblock / reparent / delete + dedupe
81
+ │ │ ├── claim.ts # claim / release + resolveActorIdentity (atomic CAS)
65
82
  │ │ ├── lifecycle.ts # setTaskStatus / closeTask / openTask / rejectTask / deferTask + cascade
66
83
  │ │ ├── wait.ts # waitForTasks: block until tasks reach a target status
84
+ │ │ ├── sort.ts # sortTasks (roi / recency / age / id)
67
85
  │ │ └── errors.ts # typed task error classes (TaskAlreadyOwnedError, CycleError, …)
68
86
  │ ├── tracks.ts # parallel-tracks union-find with diamond merge
69
87
  │ ├── workstream.ts # ensureWorkstream / list / summarize / destroy / export
70
- │ ├── archives.ts # cross-workstream archive buckets (create / add / remove / restore)
71
- │ ├── exporting.ts # unified bucket renderer (workstream + archive export)
72
- │ ├── importing.ts # inverse of exporting.ts: parse a v0.3 bucket dir → live DB rows
88
+ │ ├── archives.ts # cross-workstream archive bucket SDK hub (re-exports src/archives/*)
89
+ │ ├── exporting.ts # unified bucket renderer (workstream + archive export; read-only buckets)
90
+ │ ├── db-sync.ts # whole-DB export/import + drift detection + sidecar park SDK
91
+ │ ├── db-sync-replay.ts # manual replay of divergence sidecars parked by db import --force-source
73
92
  │ ├── logs.ts # agent_logs SDK (append, list, latestSeq, emitEvent)
74
- │ ├── vcs.ts # VcsBackend interface + jj/sl/git/none impls
75
- │ ├── workspace.ts # per-agent VCS workspaces (CRUD over vcs_workspaces)
76
- │ ├── snapshots.ts # whole-DB snapshots (VACUUM INTO) + auto-capture hook
93
+ │ ├── vcs.ts # VcsBackend hub (re-exports src/vcs/*: jj/sl/git/none impls)
94
+ │ ├── workspace.ts # per-agent VCS workspaces hub (re-exports src/workspace/*)
95
+ │ ├── snapshots.ts # whole-DB snapshots hub (re-exports src/snapshots/*)
96
+ │ ├── dag.ts # full-DAG forest builder (loadFullDag for `mu task tree` + DAG popup)
97
+ │ ├── state.ts # SDK seam for `mu state` (fast SQL tier + slow subprocess tier + merge)
98
+ │ ├── staleness.ts # WORKSPACE_STALE_THRESHOLD + isStaleWorkspace
99
+ │ ├── project-root.ts # detectProjectRoot for the TUI launch cwd ladder
100
+ │ ├── doctor-summary.ts # TUI-friendly slice of `mu doctor` checks + remediation helpers
77
101
  │ ├── output.ts # NextStep type + printNextSteps / errorNextSteps
78
102
  │ ├── cli.ts # commander wiring (buildProgram); re-exports format/handle for back-compat
79
103
  │ ├── cli/ # one file per verb-namespace; thin wrappers over the SDK
@@ -88,17 +112,52 @@ mu/
88
112
  │ │ │ ├── claim.ts # claim / release / wait
89
113
  │ │ │ ├── tree.ts # tree rendering
90
114
  │ │ │ └── wire.ts # Commander glue
91
- │ │ ├── workspace.ts # workspace create / list / free / path / orphans
115
+ │ │ ├── workspace.ts # workspace create / list / free / path / orphans / refresh / commits
92
116
  │ │ ├── log.ts # log read / write / tail
93
- │ │ ├── archive.ts # archive create / list / show / add / remove / delete
94
- │ │ ├── state.ts # `mu state` (canonical state card) + bare `mu` (mission control / hud render mode)
117
+ │ │ ├── archive.ts # archive create / list / show / add / restore / remove / delete
118
+ │ │ ├── state.ts # `mu state` (canonical state card); --tui dispatches to src/cli/tui/
119
+ │ │ ├── staleness.ts # shared workspace-staleness CLI helpers + warn formatter
120
+ │ │ ├── tui-launch-focus.ts # initial-tab focus ladder for bare `mu` and `mu state --tui`
121
+ │ │ ├── tui/ # interactive ink-based TUI cluster; ONLY place ink/react are imported
122
+ │ │ │ ├── index.ts # runTui entrypoint; alt-screen + mouse-mode lifecycle
123
+ │ │ │ ├── escapes.ts # pure ANSI escape constants (ALT_SCREEN_*, mouse-mode bytes) — no ink imports
124
+ │ │ │ ├── app.tsx # <App> root (popup state machine + global keymap + footer + tick + tabs)
125
+ │ │ │ ├── state.ts # poll-loop hook (useDashboardSnapshot; fast/slow tick split)
126
+ │ │ │ ├── keys.ts # pure dispatchGlobalKey + dispatchPopupKey + shouldSwallowGlobalKey
127
+ │ │ │ ├── keymap-spec.ts # canonical keymap source-of-truth (drives help overlay + dispatch)
128
+ │ │ │ ├── yank.ts # clipboard probe + write (pbcopy/wl-copy/xclip/xsel/clip.exe + OSC-52)
129
+ │ │ │ ├── mouse.ts # vendored SGR mouse layer (parser + double-click + useMouse hook)
130
+ │ │ │ ├── layout.ts # responsive multi-column dashboard + per-card row budgets
131
+ │ │ │ ├── columns.ts # column-aligned row layout with protect/clip clipping
132
+ │ │ │ ├── wrap-ansi.ts # ANSI-aware visual-width line wrapper + SGR close-on-end
133
+ │ │ │ ├── glyphs.ts # superscript digit + status glyphs
134
+ │ │ │ ├── format-helpers.ts # shared TUI formatters (relTime, sinceClaim, ROI, etc.)
135
+ │ │ │ ├── titled-box.tsx # rounded border with section header inset into top border + bottomLabel
136
+ │ │ │ ├── popup-shell.tsx # popup outer chrome (cyan TitledBox)
137
+ │ │ │ ├── list-row.tsx # centralised non-selected row primitive (width pin + gutter + truncate)
138
+ │ │ │ ├── padded-rows.tsx # per-card body padder
139
+ │ │ │ ├── help.tsx # ?/F1 keymap overlay (scrollable on short panes)
140
+ │ │ │ ├── status-bar.tsx # bottom status bar (mode + active ws + tick + footer flash)
141
+ │ │ │ ├── tab-strip.tsx # multi-workstream tab switcher (N≥2)
142
+ │ │ │ ├── tab-strip-layout.ts # pure window-around-active layout helper for the tab strip
143
+ │ │ │ ├── tuicr.ts # `t` shortcut: alt-screen handoff to tuicr -r <sha>
144
+ │ │ │ ├── use-popup-filter.tsx # shared '/' substring filter hook + applyFilter + FilterPrompt
145
+ │ │ │ ├── use-status-filter.tsx # task-status toggles for task-list popups (o/i/c/r/d)
146
+ │ │ │ ├── use-notes-drill.ts # shared notes-drill memo (5 task popups consume it)
147
+ │ │ │ ├── use-popup-action-queue.ts # consume mouse PopupAction queue once per render
148
+ │ │ │ ├── cards/{agents,tracks,ready,log,workspaces,inprogress,blocked,recent,commits,doctor}.tsx + _placeholder.tsx
149
+ │ │ │ └── popups/{agents,tracks,ready,log,workspaces,inprogress,blocked,recent,commits,doctor,dag,all-tasks}.tsx
150
+ │ │ │ # plus drill.tsx (DrillScrollView), task-detail.tsx (TaskDetailDrill),
151
+ │ │ │ # cursor-row.tsx, scroll.ts (applyCursor/applyScroll), viewport.ts,
152
+ │ │ │ # show-loader.ts (shared subprocess-preserving loader)
95
153
  │ │ ├── snapshot.ts # undo / snapshot list / snapshot show
154
+ │ │ ├── db.ts # db export / import / replay
96
155
  │ │ ├── sql.ts # sql escape hatch
97
156
  │ │ ├── doctor.ts # doctor diagnostic
98
157
  │ │ ├── format.ts # pure rendering helpers (table renderers, status colourers, truncate/relTime)
99
158
  │ │ └── handle.ts # typed-error → exit-code map + handle() wrapper
100
159
  │ └── index.ts # SDK entrypoint (re-exports)
101
- ├── test/ # 60 files / 57 *.test.ts / ~996 it()/test() calls; many use real tmux/git/jj/sl
160
+ ├── test/ # ~165 *.test.ts files / ~2000 it()/test() calls; many use real tmux/git/jj/sl
102
161
  ├── skills/mu/SKILL.md # what the LLM running inside an agent pane sees
103
162
  ├── package.json # bin: { mu: ./dist/cli.js }, type: module
104
163
  ├── tsconfig.json # strict + noUncheckedIndexedAccess + verbatimModuleSyntax
@@ -116,15 +175,20 @@ mu/
116
175
  ```bash
117
176
  npm install
118
177
  npm run build # tsup → dist/
119
- npm run typecheck # tsc --noEmit
178
+ npm run typecheck # source + test TypeScript checks
120
179
  npm run lint # biome check src test
121
- npm run test # vitest run
180
+ npm run test:fast # fast unit/dev-loop tier (excludes *.integration.test.ts / *.smoke.test.ts)
181
+ npm run test # full vitest suite, including integration tests
122
182
  npm run test:watch # vitest in watch mode
183
+ npm run test:watch:fast # fast-tier watch mode
123
184
  ```
124
185
 
125
- All four must pass before any commit. Tests include real-tmux
126
- integration tests that need `$TMUX` set. If you're not in tmux,
127
- those tests skip themselves; CI runs inside tmux.
186
+ Use `npm run test:fast` for the inner dev loop and concurrent
187
+ worker checks; it is the concurrency-safe tier that avoids real
188
+ tmux/VCS subprocess integration fixtures. All four green gates still
189
+ include `npm run test` (the full suite) before any commit. Full tests
190
+ include real-tmux integration tests that need `$TMUX` set. If you're
191
+ not in tmux, those tests skip themselves; CI runs inside tmux.
128
192
 
129
193
  ### Commits
130
194
 
@@ -170,17 +234,74 @@ those tests skip themselves; CI runs inside tmux.
170
234
 
171
235
  - Unit tests: real SQLite (in-temp-dir), mocked tmux executor via
172
236
  `setTmuxExecutor()`. Fast, deterministic.
173
- - Integration tests: real tmux server. File suffix
174
- `.integration.test.ts` (e.g. `tmux.integration.test.ts`). Skipped
175
- when `$TMUX` is unset. These tests typically opt out of the spawn
237
+ - TUI popup/card behaviour tests should follow `test/README.md`:
238
+ prefer the `test/_ink-render.ts` CaptureStream seam over
239
+ `readFileSync` source-greps except for narrow structural guards.
240
+ - Fast tier: `npm run test:fast` runs `test/**/*.test.ts` while
241
+ excluding `*.integration.test.ts` and `*.smoke.test.ts`. Keep this
242
+ tier pure/in-process: mocked tmux/VCS, real SQLite only in per-test
243
+ temp DBs, no real tmux/git/jj/sl subprocess fixtures, no
244
+ filesystem-heavy export/import/snapshot paths, and no fixed sleeps
245
+ above 50ms.
246
+ - Integration tests: full-only tests use the `.integration.test.ts`
247
+ suffix (e.g. `tmux.integration.test.ts`). They may touch real tmux
248
+ servers, git/jj/sl fixture repos, subprocess-backed smoke paths,
249
+ filesystem-heavy export/import/snapshot flows, or intentionally
250
+ slower in-process CLI flows. Real-tmux tests are skipped when
251
+ `$TMUX` is unset. These tests typically opt out of the spawn
176
252
  liveness check via `process.env.MU_SPAWN_LIVENESS_MS = "0"` in
177
253
  `beforeEach` since the long-running sh subprocesses they spawn are
178
254
  intentionally alive.
179
255
  - Each test gets its own temp DB and (for integration) a unique
180
256
  tmux session like `mu-test-<pid>-<ts>-<rand>` to avoid colliding
181
257
  with the user's panes or with parallel test runs.
182
- - The acceptance test in `test/acceptance.test.ts` is the
258
+ - Dogfood reality: multiple pi worker agents often run `npm run test`
259
+ concurrently on the same machine from different workspaces. Treat
260
+ flakes that pass in isolation but fail under load as concurrency
261
+ bugs first (shared `/tmp` cleanup, tmux socket/session collisions,
262
+ leaked subprocesses, VCS background file activity). Use
263
+ `npm run test:stress` for the pre-release/stability gate; it runs
264
+ the suite repeatedly with a per-run timeout and can simulate
265
+ parallel full-suite runs via
266
+ `MU_TEST_STRESS_MODE=parallel MU_TEST_STRESS_PARALLEL=2`.
267
+ - The acceptance test in `test/acceptance.integration.test.ts` is the
183
268
  "everything works" gate. Keep it passing.
269
+ - **DB baseline**: `openDb()` refuses to open the user's REAL
270
+ default DB (`<HOME or XDG_STATE_HOME>/mu/mu.db`) when
271
+ `process.env.VITEST` is set or `NODE_ENV === "test"`. Tests
272
+ MUST use a per-test temp DB — either via MU_DB_PATH (which
273
+ `test/_runCli.ts` sets automatically) or an explicit `{ path }`
274
+ argument to `openDb`. A regression that forgets either one
275
+ fails loudly at the offending openDb() call site instead of
276
+ silently writing to the dev box's live state. Production never
277
+ sets VITEST, so the guard is a no-op outside the test runner.
278
+ - **Env baseline**: `test/_setup.ts` (vitest `setupFiles`) clears
279
+ every `MU_*` env var inherited from the parent shell at the start
280
+ of each fork, so SDK-level overrides (`MU_PI_COMMAND`,
281
+ `MU_IDLE_THRESHOLD_MS`, `MU_SEND_DELAY_MS`, …) can't silently
282
+ change behaviour underneath tests. Allowlist: `MU_TMUX_SOCKET`
283
+ (set by `_global-teardown.ts` at MODULE LOAD time — BEFORE vitest
284
+ spawns the worker pool — for Layer-3 isolation; see round-3
285
+ Part A in the file's header comment for why module-load not
286
+ setup()). Tests that need a specific value opt IN per-test via
287
+ `process.env.X = "..."` or `withEnv()` from `test/_env.ts`.
288
+ - **Default-socket sweep philosophy**: `_global-teardown.ts` runs
289
+ an ALLOWLIST sweep of `mu-*` sessions on the user's default tmux
290
+ socket at suite setup AND teardown. The allowlist is DB-rooted:
291
+ (1) `mu-<name>` for every workstream in the user's REAL DB
292
+ (read-only via better-sqlite3, bypassing the `openDb()` test
293
+ guard) and (2) `mu-$MU_SESSION` if the orchestrator runs the suite
294
+ inside a tmux pane. Anything else is, by elimination, test residue
295
+ and is killed. Replaces the old regex-prefix sweep that missed
296
+ bare-name leftovers like `mu-alpha` / `mu-demo` / `mu-ws`. Round-4
297
+ removed the previous "pre-existing sessions snapshot" source
298
+ (`bug_test_flake_round_4_self_heal`): leftover test residue at
299
+ module-load time was getting grandfathered in as protected forever,
300
+ defeating the self-healing intent. Cost: an ad-hoc
301
+ `tmux new-session -t mu-foo` with no DB row gets killed; workaround
302
+ is `mu workstream init foo` first. New tests can hardcode any
303
+ workstream name they want — if they accidentally bypass the
304
+ private socket the sweep catches them by default.
184
305
 
185
306
  ### When you change behaviour, update VOCABULARY first
186
307
 
@@ -208,12 +329,18 @@ meeting these criteria, **stop**. Add an entry to
208
329
  The "anti-feature pledges" in ROADMAP.md are firm:
209
330
 
210
331
  - No config file
211
- - No daemon
332
+ - No daemon / background process beyond what tmux + SQLite give us
212
333
  - No anticipatory abstractions (no traits with zero implementors)
213
334
  - No wrappers around wrappers
214
- - No codegen
215
- - An agent template/discovery system requires explicit promotion
216
- - No render layer beyond `cli-table3` + `picocolors`
335
+ - No codegen / embedded JS engine / workflow DSL
336
+ - No template/discovery system for agent roles (spawn flags + first
337
+ message ARE the definition)
338
+ - No render layer beyond `cli-table3` + `picocolors`, EXCEPT `ink`
339
+ confined to `src/cli/tui/`. NO second TUI stack alongside `ink`
340
+ (no `blessed` / `terminal-kit` etc.); if `ink` ever stops paying
341
+ off, REPLACE it, don't stack stacks.
342
+ - No plugin runtime, web UI, RPC, chat/docs integrations, memory
343
+ system, workflow engine
217
344
  - Don't bundle pi (it's a peer dep)
218
345
 
219
346
  ### When in doubt: be small
@@ -252,11 +379,27 @@ real friction proves itself.
252
379
 
253
380
  ### "Update the schema"
254
381
 
255
- 1. 0.1.0 has no migration layer. Schema lives in `src/db.ts` as a
256
- single CREATE-IF-NOT-EXISTS block. The first non-additive
257
- change should land alongside a `schema_version` table.
258
- 2. Update tests that exercise the schema (`test/db.test.ts`).
259
- 3. Update [CHANGELOG.md](CHANGELOG.md) §"Schema" snapshot.
382
+ 1. Current schema version is **v8** (see `CURRENT_SCHEMA_VERSION`
383
+ in `src/db.ts`). The schema lives in `src/db.ts` as the
384
+ `applySchema(db)` block, which is idempotent CREATE-IF-NOT-EXISTS
385
+ plus targeted `DROP TABLE IF EXISTS` for retired tables
386
+ (e.g. v7's `DROP TABLE IF EXISTS approvals`). v8 is additive:
387
+ `machine_identity` and `workstream_sync` are
388
+ `CREATE TABLE IF NOT EXISTS`, and `openDb` seeds the single
389
+ `machine_identity` row on first open. `openDb` rejects pre-current
390
+ DBs with `SchemaTooOldError` (exit 4) and a migration hint.
391
+ 2. Bump `CURRENT_SCHEMA_VERSION` in `src/db.ts` and mirror the new
392
+ shape in `CURRENT_SCHEMA`. Three of the last four bumps were
393
+ script-free: v5 → v6 was purely additive (existing
394
+ CREATE-TABLE-IF-NOT-EXISTS picked up new tables); v6 → v7 was a
395
+ destructive-but-idempotent `DROP TABLE` block; v7 → v8 is
396
+ additive plus the `machine_identity` seed. Reach for a one-shot
397
+ migration script only when the change can't be expressed that way
398
+ (the v4 → v5 surrogate-PK substrate switch was the canonical
399
+ example).
400
+ 3. Update tests that exercise the schema (`test/db.test.ts`).
401
+ 4. Update [CHANGELOG.md](CHANGELOG.md) under the upcoming version's
402
+ `### Changed` section.
260
403
 
261
404
  ### "Add a new tmux operation"
262
405
 
@@ -322,11 +465,12 @@ processes need time to settle. Use:
322
465
  Before opening a PR or marking a task complete:
323
466
 
324
467
  ```bash
325
- npm run typecheck && npm run lint && npm run test && npm run build
468
+ npm run typecheck && npm run lint && npm run test:fast && npm run test && npm run build
326
469
  ```
327
470
 
328
- All four green. The acceptance test (`test/acceptance.test.ts`)
329
- must pass — it's the "end-to-end works" gate.
471
+ All four green gates, plus the fast dev-loop tier. The acceptance
472
+ test (`test/acceptance.integration.test.ts`) must pass — it's the
473
+ "end-to-end works" gate.
330
474
 
331
475
  Checklist for any non-trivial change:
332
476
 
package/README.md CHANGED
@@ -2,28 +2,39 @@
2
2
 
3
3
  **A small, opinionated control plane for a crew of AI coding agents
4
4
  working in parallel.** One tmux session, a typed task DAG, isolated
5
- VCS workspaces per agent, an audit log — and a hard refusal to
6
- grow into another bloated agent framework.
5
+ VCS workspaces per agent, an audit log — and a dashboard for seeing
6
+ what the crew is doing right now.
7
+
8
+ ![mu dashboard](docs/img/tui-dashboard.png)
9
+
10
+ *`mu` (no args) — read-only dashboard: agents, tracks, ready /
11
+ in-progress / blocked tasks, log tail, workspaces, doctor.*
7
12
 
8
13
  ```bash
9
14
  mu workstream init auth-refactor
10
15
  mu task add --title "Design auth" --impact 80 --effort-days 2
11
- mu task add --title "Build auth" --impact 80 --effort-days 5 --blocks design_auth
12
- mu task add --title "Review auth" --impact 60 --effort-days 1 --blocks build_auth
16
+ mu task add --title "Build auth" --impact 80 --effort-days 5 --blocked-by design_auth
17
+ mu task add --title "Review auth" --impact 60 --effort-days 1 --blocked-by build_auth
13
18
 
14
19
  mu agent spawn worker-1 --workspace
15
20
  mu agent spawn reviewer-1 --workspace --role read-only
16
21
  mu agent send worker-1 "Pick up the next ready task and design the auth module."
17
22
 
18
23
  tmux a -t mu-auth-refactor # watch the whole crew live
19
- mu # mission control: ready tasks, parallel tracks, agent status
24
+ mu # human home base: TUI with every workstream loaded
25
+ mu state -w auth-refactor --json # agent/script API: typed static state for jq
20
26
  mu log --tail # subscribe to every state change
21
27
  ```
22
28
 
23
- The crew is real (tmux panes you can attach to), the work graph is
24
- real (SQLite + a parallel-tracks algorithm with diamond-merge), the
25
- workspaces are real (jj workspace / sl share / git worktree), and
26
- **mu does not get in your model's way.**
29
+ Crew, graph, and workspaces are all real things you can poke at:
30
+ tmux panes (`tmux attach`), a SQLite DAG with parallel-tracks +
31
+ diamond-merge, and jj workspace / sl share / git worktree on disk.
32
+ **mu persists state and coordinates handoffs; the model still
33
+ decides what to do.** Workers reach for the bundled
34
+ [SKILL.md](skills/mu/SKILL.md) when they need in-pane ground truth.
35
+ Bare `mu` on a TTY launches the read-only multi-workstream TUI;
36
+ piped/scripted calls print help, so use typed verbs + `--json` (or
37
+ `MU_NO_TUI=1`) for agent/API flows.
27
38
 
28
39
  ---
29
40
 
@@ -34,17 +45,14 @@ workspaces are real (jj workspace / sl share / git worktree), and
34
45
  task DAG with **deterministic parallel-track detection +
35
46
  diamond-merge** so two agents are never assigned tasks that share
36
47
  a prerequisite.
37
- - **Get out of the model's way.** Mu coordinates agents; it does
38
- not reason about them. No model selection, no thinking-effort
39
- knobs, no system-prompt templating, no tool routing. `--cli <key>`
40
- uppercases to `$MU_<KEY>_COMMAND` your shell rc owns the
41
- mapping. Swap your whole stack in one line.
42
- - **A deliberate refusal to over-engineer.** One CLI. One SQLite
43
- file. ~60 typed verbs. No daemon, no config file, no plugin
44
- runtime, no DSL, no codegen, no web UI, no chat integration, no
45
- hosted service. Each missing piece is an
46
- **[anti-feature pledge](docs/ROADMAP.md#anti-feature-pledges-still-in-force-reinforced-by-an-internal-critique)**,
47
- not an oversight.
48
+ - **Stay out of the model's way.** Mu coordinates handoffs; it does
49
+ not make choices on behalf of the model. `--cli <key>` uppercases
50
+ to `$MU_<KEY>_COMMAND`, so your shell rc owns model/provider
51
+ selection and thinking-effort flags. The orchestrator and workers
52
+ drive; mu records, scopes, and yanks the next command.
53
+ - **A local typed state surface.** One CLI, one SQLite registry,
54
+ typed verbs, `--json` everywhere useful, and a read-only dashboard
55
+ for humans.
48
56
 
49
57
  ## What mu is NOT
50
58
 
@@ -110,7 +118,31 @@ npx skills add ./skills/mu # local-path source format
110
118
  ```
111
119
 
112
120
  More install patterns (alias-to-dist for fastest dev iteration) in
113
- [docs/USAGE_GUIDE.md § 1 Setup](docs/USAGE_GUIDE.md#1-setup).
121
+ [docs/USAGE_GUIDE.md § 1 Setup](docs/USAGE_GUIDE.md#1-setup). After
122
+ installing, run bare `mu` (or `mu state --tui`) for the dashboard tour.
123
+
124
+ ---
125
+
126
+ ## TUI dashboard
127
+
128
+ Bare `mu` in a TTY launches the flagship read-only dashboard across
129
+ all workstreams; `mu state --tui -w <workstream>` is the explicit
130
+ single/multi-workstream form. Non-TTY callers and scripts keep the
131
+ static/help path, and `mu state --json` is the API.
132
+
133
+ The dashboard has ten cards: Commits, Agents, Tracks, Ready, Activity
134
+ log, Workspaces, In-progress, Blocked, Recent, and Doctor. Drill into
135
+ any numbered card fullscreen with `Shift+0`-`Shift+9`; `g` opens the
136
+ full DAG and `t` opens the all-tasks list. `?` shows the complete
137
+ keymap. Keyboard and mouse both work: navigate with keys, double-click
138
+ cards or rows to drill, and scroll popup bodies with the mouse wheel.
139
+
140
+ The TUI is read-only by design. `y` yanks the canonical `mu` command
141
+ for the focused row to your clipboard; you run it in a shell, so every
142
+ mutation still goes through a short-lived typed CLI invocation. The
143
+ one exception is user-driven: `t` inside a commit/show drill suspends
144
+ mu's alt-screen and hands off to `tuicr -r <sha>`, then restores the
145
+ dashboard when tuicr exits.
114
146
 
115
147
  ---
116
148
 
@@ -125,16 +157,20 @@ mu workstream init auth-refactor
125
157
 
126
158
  # Plan the work as a DAG. IDs auto-derive from titles.
127
159
  mu task add --title "Design auth module" --impact 80 --effort-days 2
128
- mu task add --title "Build auth" --impact 80 --effort-days 5 --blocks design_auth_module
129
- mu task add --title "Review auth" --impact 60 --effort-days 1 --blocks build_auth
160
+ mu task add --title "Build auth" --impact 80 --effort-days 5 --blocked-by design_auth_module
161
+ mu task add --title "Review auth" --impact 60 --effort-days 1 --blocked-by build_auth
130
162
 
131
163
  # Spawn a crew with isolated workspaces.
132
164
  mu agent spawn worker-1 --workspace
133
165
  mu agent spawn reviewer-1 --workspace --role read-only
134
166
 
135
- # Mission control: parallel tracks, ready tasks, agent status.
167
+ # Human home base: interactive read-only TUI across every workstream
168
+ # (same dashboard is explicit with: mu state --tui -w auth-refactor).
136
169
  mu
137
170
 
171
+ # Agent/script API: static state stays explicit and JSON-friendly.
172
+ mu state -w auth-refactor --json
173
+
138
174
  # Inside an agent's pane, the agent claims and closes tasks
139
175
  # without ever knowing its own name (mu reads $TMUX_PANE).
140
176
  mu task claim design_auth_module
@@ -148,7 +184,26 @@ mu log --tail
148
184
  mu workstream destroy --yes
149
185
  ```
150
186
 
151
- Full tour: [docs/USAGE_GUIDE.md](docs/USAGE_GUIDE.md).
187
+ Full tour: [docs/USAGE_GUIDE.md](docs/USAGE_GUIDE.md), including the
188
+ expanded [dashboard/state guide](docs/USAGE_GUIDE.md#5-see-the-graph-dashboard--state-api).
189
+
190
+ ---
191
+
192
+ ## Portability and handoff
193
+
194
+ State lives in one SQLite DB, so it travels. `mu db export <file>`
195
+ writes a consistent whole-DB copy plus a manifest sidecar; `mu db
196
+ import <file>` ships it back, with per-workstream drift detection
197
+ (dry-run by default; `--apply` commits) and a sharp `--force-source`
198
+ that parks the loser to a sidecar before clobbering. Hard rule: don't
199
+ edit the same workstream on two machines concurrently.
200
+
201
+ For humans / git / docs, `mu workstream export` and `mu archive
202
+ export` render a workstream (or an archive bucket) as Markdown:
203
+ per-task `.md` files plus an `INDEX.md`, suitable for committing,
204
+ reviewing, or pasting. Bucket exports are read-only artifacts; the
205
+ lossless un-archive path back into a live workstream is `mu archive
206
+ restore <label> --as <new-ws>`.
152
207
 
153
208
  ---
154
209