Repository navigation
mu 3.13.0
Faster everyday verbs. tmux-touching verbs no longer load execa (about
50-60 ms each), pure-JS dependencies are bundled, and a bare verb in a
mu-spawned pane skips the tmux workstream lookup. mu doctor counts ops by
key range (~250 ms less), peer sync resumes from where it stopped, and the
TUI launches faster and stops re-detecting the VCS every 10 s.
Upgrade with npm i -g @mu-crew/mu@3.13.0, then /reload in running pi
sessions to pick up the extension. No schema change. Task, track and peer
lists now sort names by code unit (a-b, a0, a_b; uppercase first).
Changed
-
Spawn warns on Claude's folder-trust dialog. The dialog defaults to
"No, exit", so the Enter that ends--sendquit claude. The tmux
startup scan now spots it (non-fatal): the pane and agent stay, the
initial--sendis skipped (send.errorTrustDialogPendingin
--json), and a warning plus next step says to answer it in the pane
and thenmu agent send. It does not guard a lateragent sendor the
herdr spawn path, and mu never presses a key for you. -
TUI dashboard fills its space. A card's
maxRowsnow caps only
the first pass of the row allocator: leftover rows go to cards whose
data still overflows, in proportion to the rows each hides, so a
40-item Ready list at 80 rows shows all 40 instead of 15 above 60
blank rows. Cards also move between columns:arrangeColumns
(replacingbalanceColumns, which only ran after a cull) searches
single moves and swaps for the fewest hidden rows, even with all ten
cards visible, and keeps the previous layout unless the new one hides
max(3, 10%) fewer rows. The search also descends from the previous
layout, so a retained layout never has a single move or swap that
beats it by more than that margin. Among layouts that hide the same rows, the search
prefers fewer cards away from their default column, then slot order. Footers and mouse hit regions read the same
budgets (columnRowBudgets). -
Skill: recipe wording from a prior runtime. An
unknownowner
is not a dead one: orchestrator-loop says to read the pane or run
mu doctorbefore releasing its task. Reviews with only
UNVERIFIED items go to the human, not into a REJECT round. findings § Record
grades severity strictly (critical needs a concrete scenario).
ultrathink and loop-until-done refute the umbrella's criteria on the
merged tree beforeclose --if-ready.
watcher caps attempts per item and parks it with every attempt listed.
long-run recordsDEAD-END:notes on the umbrella.
backlog-triage re-verifies an item against the code before starting.
dag.md states that every edge comes from a verb the caller ran (block,add/reparent --blocked-by) and no write adds edges on its own.
VISION cites a prior runtime's transport and sentinel-marker lessons. -
A changed peer segment is ingested from where the last read stopped.
Every append used to make each other machine hash the whole segment,
read it a second time as text and split it (~120 ms for this fleet's
64 MB segment, ~9 ms of it needed). A clean read now saves the byte
offset and running sha256 state next to the caught-up fingerprint; the
next ambient ingest reads only the appended bytes, and accepts them
only when their hash is the manifest's sha256 (~20 ms). A shrunk
file, a stale manifest, a defect (which saves no offset) andmu sync
take the full read and whole-file verify as before. The resumed hash
trusts the prefix it already read: a local edit to those earlier bytes
that keeps the file growing is caught only bymu sync, not by the
ambient ingest. No
schema change: the offset is extra fields in the existing
sync_fingerprintsvalue, which an older mu never matches and
ignores. -
mu workstream listreads mux liveness from onelist-sessions.
It also ranhas-sessiononce per workstream (6 spawns for 6
workstreams). The listing now reuses the session names it already
fetched, and falls back to per-workstreamhas-sessionwhen
list-sessionsfails, so a transient failure still never marks live
sessions dead. ~22 ms CLI CPU saved; output is identical. -
mu_delegatelists agents at most every 30 s while a failed delegate
lingers. Each spawn ranmu agent list(~150 ms of CPU) only to prune
the footer's failed count; the refresh is now throttled to once per
30 s. Spawning and safety checks are unchanged; the footer may show a
closed failure for up to 30 s longer. -
mu agent spawnsets the tmux pane border in one tmux call. The four
set-option -wcalls are;-chained into one invocation and target the
pane directly, which drops thedisplay-message #{window_id}lookup
(the border helper's 5 tmux processes become 1, ~33-80 ms per spawn). Options,-wscope,
MU_BANNER_QUIET, and the swallow-all-errors behaviour are unchanged. -
The TUI detects the VCS backend once, not every 10 s. The slow tick
ranjj root,sl root(twice) andgit rev-parsebefore every
git logjust to learn the same backend again. It now caches the
backend per project root for the TUI's lifetime (~4 of 5 processes per
tick; ticks after the first dropped from ~690 to ~175 ms best case on
a loaded machine). Anoneresult is not cached, nor is a detection
during which a root probe failed (spawn error, signal) rather than
cleanly answering "not this VCS": a transientjj rootfailure in a
colocated jj/git repo no longer pins git.mu stateis unchanged (it
never probes the VCS). -
The TUI bundles ink and react into its lazy chunk. Importing the TUI
resolved and compiled ~560 ESM files fromnode_modulesper launch. tsup
now inlines ink, react and their pure-JS deps intodist/tui-*.js(loaded
only by baremuandmu state --tui; other verbs are unchanged), using
the createRequire banner from the commander/cli-table3 bundling. NODE_ENV
is not defined at build time, sosrc/cli/tui-load.tsstill picks react's
production build at runtime and an explicitNODE_ENVstill wins. TUI
import ~470 -> ~67 ms, first Agents card ~890 -> ~440 ms (PTY, 20
alternating launches on a copied DB).react-devtools-corestays external
(ink loads it only underDEV=true). -
Task, track and sync-peer sorts use code-unit order, not
localeCompare. The firstlocaleComparecall initialised ICU
(~4 ms on nearly every list verb). Ties by name now sort by code unit,
soa-b, a0, a_breplaces the locale ordera_b, a-b, a0; uppercase
sorts before lowercase. Sort keys, ROI order and everything else are
unchanged. -
mu doctorandmu db forgetcount a workstream's ops by key range.
The per-workstreamsubstr(key, …)test scanned all ofopsonce per
torn-down workstream (27 scans of 61k rows, ~320 ms of SQL in
mu doctor's size hint). It now seeksidx_ops_entity_keywith a key
range per entity (~28 ms); counts and deleted rows are identical. -
A bare verb in a mu-spawned pane skips the tmux lookup. With no
-wand no$MU_SESSION,resolveWorkstreamrantmux display-message -p '#S'(and loaded execa) even though spawn already
put the workstream in$MU_WORKSTREAM. It now uses$MU_WORKSTREAM
when$MU_MANAGED_AGENT=1is also set: baremu task list~133 -> ~67
ms from an agent pane.-wand$MU_SESSIONstill win, and a
$MU_WORKSTREAMwithout$MU_MANAGED_AGENT(a stale export in a user
shell) is ignored. User-started orchestrator panes are unchanged. -
Spawn sees a pi agent's control socket within ~50 ms. The spawn
handshake polled every 250 ms, so a socket pi bound just after a poll
waited up to 250 ms to be noticed. It now stats the socket path every
50 ms (no fork) and probes as soon as it exists: readiness overshoot
132 -> 24 ms, ~100 ms less per pi spawn. The pane-alive check and
scrollback capture keep their 250 ms cadence, plus one final check at
the budget's deadline, so a pane dying in the last 250 ms keeps its
crash scrollback. -
mu_delegatecloses the pane and writes the record note in
parallel. A finished delegate withrecordwaited for
mu agent closebefore startingmu task note, though neither needs
the other. They now overlap: ~70 ms less before the answer arrives
(median of 25 pairs), on clean finishes that close and record. Failure
reporting is unchanged: a refused close and a failed note are each
named in the follow-up. -
The keep-driving nudge reads
mu task list, notmu state. At
each armed settle the pi extension ranmu state --jsononly to read
IN_PROGRESS task names, paying for the tmux pane listing and the
orphan and workspace views it never used. It now runs
mu task list --status IN_PROGRESS --jsonper dispatched workstream
(--sort recency, newest first asstatelisted them), in parallel,
deduplicated by task: ~56-69 ms less per armed settle (one
workstream). A task still owned by a dead agent shows in the nudge until the next
mu stateormu task waitreaps it. -
Faster tmux-touching verbs. The tmux, herdr, kick and murmur-status
executors run throughnode:child_processinstead of lazily importing
execa (~110 modules, 25-40 ms).mu state,mu agent list, and bare
verbs that resolve the workstream via tmux drop by ~50-60 ms per call
(state ~140 -> ~85 ms). Output and exit codes are unchanged; capture
keeps execa's 100 MiB buffer cap.
In-flight children are killed when mu exits or gets SIGTERM, SIGINT or
SIGHUP, as execa'scleanupdid, so no tmux or murmur process outlives mu. -
Faster startup: pure-JS dependencies are bundled. Every verb
resolved, read and compiled about 35node_modulesfiles (commander,
cli-table3, string-width and their dependencies).tsupnow bundles
commander, cli-table3, string-width and picocolors intodist/
(nativebetter-sqlite3,execa,inkandreactstay external; the TUI
chunk is still lazy). Measured on interleaved warm runs: 5-15 ms per
invocation (--version,task list,state). -
Skill: orchestrators clean up scratch files. Review panels and
other delegate jobs left briefs, diffs and outputs in/tmpor ad
hoc dirs under$HOME. SKILL.md's orchestrator rules now say to keep
briefs and answers in task notes andmu log(stdin for pure
delegate calls), keep any files in onemktemp -ddir per job,
and remove it before the turn ends. -
Skill: worker checkouts come from mu. Orchestrators made their
owngit worktrees for workers, which mu could not refresh, diff or
free, so they leaked. SKILL.md now says to use--workspace/
workspace: truefor every local agent; remote agents keep the
manual recipe.
Fixed
-
mu lognames who sent, spawned or aborted.agent.send,
agent.spawnand a newagent.abortop (written only when a busy agent
was actually aborted) carry the caller ($MU_AGENT_NAME, pane title,
$USER) as their actor instead ofsystem. The extension'sabortreply
now carrieswasBusy, so a run that settles between the status probe and
the abort is not logged. -
A rolled-back spawn leaves an
agent.spawn_failedlog entry with the
phase reached and the error class;agent.spawnnow carriesctl=. -
Spawned panes get the caller's
SSH_AUTH_SOCK. mu sessions are
created detached and can stay so, so tmux never refreshed the env and a
pane could inherit a stale socket. Spawn now forwards it when it is set
and a socket (file-type check only, no liveness probe). -
Detached tmux sessions start at 200x50, not 80x24.
mu agent read
captured wrapped lines at 80 columns on unattached sessions.
new-session -dnow passes-x 200 -y 50(attached sessions are
unchanged; noresize-window). herdr is unaffected. -
Nudges yield to a delegate answer on its way. The keep-driving and
close nudges continued a settling run even when amu_delegateanswer
sat in pi's follow-up queue (pi'shasPendingMessages()does not count
custom follow-ups), so the agent took an extra turn without the
answer. All three nudges now skip while an answer is being delivered or
queued, re-checking after theirmuread and again once the slowest
nudge's read finishes (the nudges run in parallel); keep-driving and close also
skip while an extension dialog is open. A skipped settle does not use
up the prompt's one nudge. -
Spawned agents use the spawner's database. Panes inherit the tmux
server's env, so underMU_DB_PATH=/tmp/x.dba worker'smuhit the
real DB. Local spawns now pinMU_DB_PATH(absolute, skipping
:memory:) andMU_STATE_DIRin the pane env. -
mu doctorreports dependency cycles made by sync. Two machines can
each add a locally valid edge that merge into a cycle, silently hiding its
tasks from ready/next. Adagsection (anddagCyclesin--json) lists
each cycle with amu task unblock … --by … -w …command that breaks it.
Report-only; thetask treeheader no longer claims the schema forbids cycles. -
mu agent send <agent> ""no longer fails with tmux "no buffer".
On the mux path an empty message now presses a bare Enter (dismisses a
"Press Enter" prompt). Over the pi control socket it is refused with
AgentEmptySendError, whose next step is--via mux. herdr refuses an
empty send (no verified bare-Enter primitive). -
paneExistsno longer reads a failed tmux probe as a dead pane.
Any nonzerodisplay-messageexit returned false, so a permission or
socket error looked like "gone" to liveness gates such as
workspace free. Only "can't find pane" / no-server answers mean
false now; other failures throw, and callers fail closed. -
Cycle errors name the full path.
CycleErrornow reads
adding edge c -> a would create a cycle: c -> a -> b -> cand its
next steps give a concretemu task unblock <b> --by <a> -w <ws>for
an edge on the path.findCyclePath(shortest path, safe on existing
cycles) is exported fromsrc/tasks/edges.ts. -
Reconcile no longer deletes a respawned agent. The ghost prune
deleted by name, so an agent closed and respawned under the same name
during a pass lost its new row and had its tasks reopened. The delete
now checks the snapshotted pane id inside the reaper transaction and
skips the row (and the ctl socket unlink) on a mismatch. -
mu agent sendno longer calls an ambiguous failure "refused".
When a send, fresh, interrupt or command request was already written
to the control socket and the reply then timed out or the connection
closed, pi may have queued the prompt. mu now raises
AgentSendIndeterminateError(exit 1, "delivery unknown") whose next
step ismu agent read <agent> -w <ws> -n 20before resending.
Failures before the write, dead panes and status/wait/hello keep their
old errors. -
Send waits for Codex's "model: loading" screen. The boot screen
has no spinner, soawaitPaneQuiescencesaw it as calm and pasted
into a booting Codex, concatenating text. "model: loading" is now a
busy marker (visible tail only). -
mu workspace freerefuses under a working agent. It ran
worktree remove --forcebeneath a running worker and lost its
pending changes. It now exits 4 when the owner is busy, needs
permission, or is unreadable with a live pane. Idle (needs_input),
a dead pane and a pane-less row are allowed.--forceskips only
this check.agent closeand spawn rollback are unchanged. -
A pi agent stuck on an extension dialog reads
needs_permission.
The control socket only knew busy / idle, so a pi waiting on a
confirm or question mid-run readbusy, andmu agent waitand
mu_delegateheld until their timeout (600 s / 3600 s). The mu pi
extension now tracks pi'sui_prompt_start/ui_prompt_endand adds
an optionalblocked: {kind, title, since}to the status reply;mu agent list,mu stateandmu agent showshowneeds_permission: dialog <kind>: <title>. A heldmu agent waiton a pi agent ends
early with outcomeblocked(not fired, run counter unchanged) and
exit 7. Themu_delegatefollow-up says the delegate is blocked and
has not finished, keeps the pane, and copies no partial answer. Older
extensions send no field and ignore the newreturnOnBlockedwait
flag, so they behave as before. A held wait whose connection drops
falls back to polling, and a dialog seen by that poll also ends the
waitblocked(exit 7) rather than counting busy → needs_permission
as a finished run. The blocked JSON row carriesafterRuns, the run
baseline the wait counted from, and the re-wait hint (CLI next steps
and themu_delegatefollow-up, blocked or timed out) passes it as
--after-runs, so a run that finishes after the dialog is answered
but before the re-wait starts is returned rather than missed. -
tmux panes keep their mu title. pi sets an OSC title
π - <cwd>, which overwrote the pane title mu uses as its identity
fallback. Spawn and adopt (including idempotent re-adopt) now set
allow-set-title offon the pane. herdr is unchanged.