Added
-
Pasted screenshots can reach the agent as real images. Pasting an
image-only clipboard into a pane used to write a temp PNG and type its path —
the only route an agent ever saw. Claude Code reads the clipboard itself when
it receives its image-paste key, so it now gets a real inline image instead of
a file path. Settings › Terminal › Image paste picks the route: Auto (the
default) uses it for panes running an agent known to read the clipboard, and
falls back to the file path everywhere else. Pasting text is unchanged. (#1209) -
Optional: put the workspaces that need you at the top of the sidebar. A new Appearance setting, "Needs-you rows first", lifts every workspace whose agent is blocked on you to the top of both the sidebar list and the collapsed rail. It is off by default, because a list that reorders itself while you are scanning costs more than the scan it saves. Turning it on changes the display only — Ctrl+N still numbers workspaces by their real position, and the stored order is untouched. Drag-to-reorder is paused while it is on, because a drop aimed at the order you can see would land at the order underneath it.
-
browser_typetakes aselector, for an element neither snapshot handed out a number for. It is CSS on every transport — a Playwright engine prefix (text=,xpath=,>>) is refused rather than accepted on one lane and not the others — and it must match exactly one element, the same uniqueness rule a ref carries. -
browser_typetakesnewline: 'enter' | 'shift-enter', which splits the text on\nand presses a real key between the lines instead of inserting a newline character a single-line input or a rich-text editor drops. Default staysliteral, today's behaviour. If a keypress submits the field, the remaining lines are not typed into whatever the next page focuses: the type stops and reports how many lines went in. -
browser_snapshottakesq, keeping only the nodes that match it (case-insensitive substring, or/pattern/flags) plus their ancestors — a way to ask a 250-option listbox one question instead of reading it whole. -
Task ledger: a status log keyed by WorkTask id (
~/.wmux/task-ledger.jsonl, WMUX_DATA_SUFFIX-scoped) recordingworking → input_required / review_requested → completed / failed / cancelledwith compare-and-swap revisions, actor authorization and a gate-pass requirement forcompleted. -
MCP:
ledger_update(every profile) lets a fan-out worker recordreview_requested/input_requiredon its own task;ledger_listis a commander-only tool (registered only under--commander, never in the full or core profile) that shows the brain the tasks it owns. Fan-out prompts now tell workers to report through the ledger instead of a chat "done". -
Orchestrator:
deck.ledgerGate(default off,~/.wmux/deck-ledger-gate.json) makes the brain's Stop gate hold a turn while the ledger lists open tasks it owns, with the existing consecutive-block cap and hysteresis; while on,deck_ask_decisionshows the human the open-task list. -
Every ledger transition is posted to the task's mission channel as
[ledger] <task> <from>→<to> <by> <summary>. -
An orchestrator can now finish the tasks it starts. Fanning out could open
N isolated tasks, and then nothing: running a task's completion gate, taking
its changes back into the parent repository, opening its pull request and
closing it were all reachable only from the desktop UI, so a supervising agent
could do nothing but ask you to click four times per task. It can now do all
four itself, and read a task's git status, recent commits and PR state as data
instead of guessing from a terminal screen. It can also stop a gate that is
still running, rather than waiting out a hung test suite for fifteen minutes. -
Closing a task or opening its PR asks you first. Those two are the ones
nothing can take back — one removes a git worktree, the other pushes a branch
to your remote — so each raises the same approval prompt a fan-out does, naming
the task, its branch and the exact commit it points at, and auto-denying if
nobody answers. If the worker pushes another commit while the prompt is up,
the pull request is refused rather than sending something you did not see.
Running a gate and adopting a task's changes do not prompt: a gate run is
reversible by ignoring it, and adopted changes land staged and uncommitted. -
Tasks can now be finished from an agent, not only from the GUI:
task_gate_run
runs a task's completion gate (its trusted verify script, or npm lint + test)
inside the task's own worktree and reports a structured verdict;task_adopt
takes all of a task's changes into the parent repository as a staged,
uncommitted patch;task_closeandtask_prclose a task or open its pull
request. Each refuses with a named reason — a dirty worktree, unpushed
commits, missing dependencies, a task branch that needs rebasing — instead of
failing silently, and an adopt that will not apply cleanly leaves the parent
repository untouched. -
git_status,git_logandgh_pr_viewread a task's worktree as data, so a
supervising agent no longer has to infer what a task produced from a terminal
screen. -
approval_press— an orchestrator brain can now answer an approval prompt on
a fan-out worker it delegated, instead of typing the digit1at whatever
happens to be on that worker's screen. The press resolves the approval record,
so the prompt is confirmed to still be there, the operator's autonomy policy
decides whether it may land, and the decision is written to the approval
history.decisionmust be given explicitly — there is no default, and an
unnamed decision is never taken as an approval. A pane holding more than one
pending approval is refused as ambiguous rather than guessed at, and the tool
hands back the ids to choose from. On a pane wmux holds an approval record
for, a brain'sterminal_send/terminal_send_keyis refused and points at
the tool — exceptctrl+candescape, which still go through so a runaway
worker can always be interrupted. If the press is refused because the operator
has autonomy or approval-press off for that worker, the block lifts so the
brain is never left with no move at all. Your own typing is unaffected. -
Approvals raised by a permission gate now carry
deadlineAt, the moment the
gate really stops waiting — reported by the gate broker that holds the timer,
so a surface can show an honest countdown. -
The Deck says what is delegated. A status panel pinned above the orchestrator conversation lists every open task from the task ledger — title, status, what the worker doing it is up to, its last ledger line and how long it has sat there. It refreshes the moment the ledger moves, collapses to nothing when no task is open, and opens the report rail once while work is outstanding. If the ledger cannot be read at all the panel says so rather than collapsing — "nothing is delegated" and "I cannot tell you what is delegated" are opposite facts.
-
deck.ledgerGatehas a switch. The ledger-backed Stop gate — hold the orchestrator's turn while the ledger still lists tasks it delegated, instead of guessing from pane activity — could previously only be turned on by hand-editingdeck-ledger-gate.json. It is now a toggle in Settings › Agents, labelled experimental, writing the same file the gate reads, so the choice survives a restart. -
The deck header names the approval deadline. An approval raised by the orchestrator's own delegation auto-rejects on a timer; with the Claude Code terminal filling the deck, the dialog could sit behind it and the expiry looked like the orchestrator stopping for no reason. The header now counts the deadline down, and turns red for the last ten seconds.
-
Turns say which brain wrote them. Switching the orchestrator brain mid-session left one log holding turns from two brains that share no transcript and no session. Each turn now carries a short tag for the brain that produced it, and a labelled break separates the runs. Turns from before this change show no tag rather than a guessed one.
-
Channel composer now tells you where an @mention will actually land: a mention
of a roster member is pinned to that member's own agent pane (the only shape
that reaches an idle agent), with a "will reach …" hint under the input. A
member with no live agent pane is offered as a badge-only mention and labelled
as one instead of silently reaching nobody. -
Channel messages you post now show what actually happened to them: delivered
as soon as any recipient got it, target gone when none did, and "no answer —
nudges exhausted" when the wake worker gave up on a mentioned agent. A post
that never gets an outcome now reads "delivery unconfirmed" instead of
claiming to still be sending forever. -
Fleet's approval inbox shows the auto-reject countdown on every approval that
has a deadline, and keeps a short "auto-rejected" log of the ones that expired
while you were away instead of letting them vanish like an answered prompt. -
The Missions section header now opens the task cleanup scan, and a close that
fails on a worktree still holding uncommitted work offers two next steps on
the row itself: "Commit & close", which types a ready-to-run
git -C <worktree> add -- <the changed paths> && git -C <worktree> commit -m "wip: <task>"into the task's own shell without running it, and "Open
worktree". The commit line is offered only when the task's pane is a shell (in
an agent's TUI it would become a chat message) and only on POSIX shells. -
task_adoptcan commit what it takes, so several tasks can be adopted in a
row. Adopt left its changes staged, which made the first adopt easy to review
and the second one impossible: the target repository was now dirty, and adopt
refuses a dirty target rather than mixing two authors' edits together. Passing
commit: truecommits exactly what was adopted, with a message naming the
task, and returns the commit's short hash. It commits only the adoption: if
anything else has been staged in the target since the adopt began, it refuses
and puts the adopted paths back rather than sweeping someone else's work into
the task's commit. The default is unchanged — staged and uncommitted — and
nothing is ever pushed.
Changed
-
The sidebar puts the workspace name first. The name is now Inter at the body size and gets the full width of its row; the project gear, the shortcut hint and the agent count fade in when you point at a row, and stay up on the active one. Before, all four fought over 240px and the name — 11px mono, the same weight as the metadata beside it — usually lost.
-
Amber in the sidebar now means only "an agent is running". A git dirty count is written as a muted
·Ninstead of an amber●N; on a working tree with 41 uncommitted files it used to be the loudest thing on the row while never meaning "look here". Clean green, ahead blue and behind red are unchanged. -
An agent waiting on you tints its row and says so. A workspace whose agent is blocked gets a faint red wash and a "Needs you" label, in place of the pause glyph that used to sit at the row's edge without saying what it was paused on. Red is also spent on errors, so an errored agent now draws a ✕ instead of a round dot — the shape, not the hue, tells the two apart.
-
A Claude Code pane turns amber the moment you submit a prompt. The
running dot used to come from a byte-rate guess — roughly 2 KB of output had
to flow before wmux would call the pane busy, so the first seconds of every
turn looked idle. wmux now registers Claude Code'sUserPromptSubmithook,
which fires once per turn, and the pane lights on the prompt itself. -
A hook-governed pane no longer flickers. While the hook speaks for a
pane, the byte heuristic stops writing its status in both directions: quiet
reasoning, a long web search, or a slowbashno longer drops the pane to
idle mid-turn, and a mid-turn redraw burst no longer overwrites a correct
"finished" or "needs you" with "running". Panes with no hook bridge — and
panes whose bridge only reports turn ends — keep the heuristic exactly as
it was. -
A turn that dies on an API error marks the pane errored (red ✕) instead of
leaving it amber. Claude Code firesStopFailureand noStopon that
path, so a pane the turn-start hook had lit stayed amber until the agent
process died. wmux now registersStopFailuretoo, and reports the failed
turn as its own notification rather than as "Task finished". -
A pane settles the moment its shell is back at its prompt. wmux reads
your shell's integration markers, and a shell that has drawn its prompt again
cannot have an agent working in it — so the agent you exited, or one that
died on a network error with no turn end at all, stops sitting there lit.
This is the settle that does not need wmux to identify the process that died,
which is why it fires where the others cannot.StopFailureis registered on
both install paths, but Claude Code does not always emit it — a turn that
fails on "API Error: Connection refused" after its last retry ends with no
hook — and panes whose shell emits no markers are unaffected: they keep the
settle paths they already had. A finished turn keeps its result either way. -
Interrupting an agent settles its pane right away. Ctrl+C — or a double
ESC — stops the turn, but Claude Code sends no turn-end hook for it and the
agent is still the pane's foreground command, so nothing else could see it.
wmux now reads the interrupt from the keystroke itself, on every path that
types into a pane (the terminal,terminal_send, the CLI), and the dot drops
to idle instead of staying amber. A Ctrl+C in a plain shell still changes
nothing. -
Existing installs need one action. Plugin users: run
/plugin updatein Claude Code to pick upwmux-claude-integration0.4.0, which registers the new hook. Plugin-less installs (wmux setup-hooks): re-runwmux setup-hooks, or just let the app do it — every launch refreshes the hooks. Then restart the Claude Code session.wmux setup-hooks --statusgained a turn-start signal row that says whether the hook is installed. If you are on a plugin older than 0.4.0, that row and the turn-end row now read STALE and point at/plugin update— the plugin owns those hooks, so re-runningwmux setup-hookscannot supply them. -
An agent that has said nothing for 30 minutes now says so. A pane whose
status is stillrunningbut which has reported nothing — no hook, no
activity, no output — for half an hour draws a hollow amber ring in place of
its filled dot, and its tooltip reads "No update for 30m+". Before, the same
pane either kept a confident, breathing amber dot for as long as the window
stayed open, or quietly slid to idle as if the work had finished. The ring
appears on the sidebar row, the collapsed rail, the sidebar agent roster and
the deck Fleet roster. It is a rendition, not a new status: the roll-up, the
needs-you ordering and the "N need you" chip are unaffected, any agent that
wants you still shows red and sorts above, and a pane whose shell is back at
its prompt or whose agent process has exited is idle as before. -
The sidebar's task list stays visible when it is empty, indents each task under
the workspace that started it, and summarises finished tasks in one line
instead of a list. -
Multi Task asks for confirmation before launching tasks with no prompt at all,
and reports the launch in a single notification rather than one per task. -
Settings names what the
claude-ptyorchestrator option changes: it switches
the Deck to a terminal interface. -
A pane that joined a channel over MCP or the CLI is now named the way the rest
of wmux names it — its rename, elsew<workspace>-<pane>(<agent>)— in the
members roster and on the transcript's sender chip, instead of the opaque
spawn-stamped member id it used to print. -
Every glyph in the chrome is now a target you can actually hit. An audit
of the packaged window found 66 controls under the 24px pointer floor: the
pane tab's close was a 7px glyph with no box at all, the workspace row's
hover actions were 11px icons in 13px boxes, the dock toggle was 20x20. They
now carry real 24x24 hit areas on the same drawing — and no box reaches over
its neighbour to do it, so the workspace row's close button cannot take a
click meant for Copy, and the tab close cannot take one meant for the end of
the tab's name. The hover actions also stop accepting clicks while they are
invisible, and appear for the keyboard when one of them is focused. -
Icon-only buttons say what they are. The titlebar's settings gear was an
unnamed button to a screen reader — atitletooltip is not an accessible
name when the button's only child is an SVG. The gear, the sidebar's collapse
chevron, the pane tab close (which now names the tab it closes, not just
"Close tab"), the company panel's add and destroy glyphs, the profile modal's
row remove and the mini sidebar's unread count all announce themselves now. -
Copy that repeated a control is gone. The orchestrator deck's empty state
was a three-line paragraph saying what the composer's own placeholder says one
row below it, so the thread simply stays empty until there is something in it.
The new-workspace menu's "Browse Folder…" row now tells you something ("Choose
any folder on disk") instead of restating its own label. The schedules empty
state keeps the fact you cannot infer — they survive reboots — and drops the
sentence describing what a schedule is. -
A task's status dot means one thing everywhere. The sidebar painted every open task green while the deck's task panel painted the same task from its worker — so one task could read as "done" on one edge of the window and "idle" on the other. Both now read the same rule: amber is running, green is complete, gray is idle, red needs you, and a task handed back for review is amber because it is waiting on the orchestrator, not finished. The dot's meaning is also read out to screen readers instead of living only in the colour.
-
The sidebar states your tasks in one line instead of listing them twice. It now reads
Tasks · 3 open, and clicking it takes you to the workspace whose ledger holds those tasks and opens the deck's Agent tab on them — previously the line counted every workspace's tasks and could open an empty panel. The line no longer repeats a "needs you" count: the titlebar chip already carries that, and it was reading it off a different source than the deck's red dots. With no tasks at all the line is gone entirely; the worktreeClean upscan is then reached from the command palette. -
The deck's task panel got the row's exits back. A row's title opens the task's own workspace and its
#opens the task's channel — the two things the deleted sidebar rows could do. Closed tasks are reachable again too, under a collapsedFinished (N)disclosure at the foot of the panel; before this their mission channels had no entry point at all once the sidebar rows went. -
The deck's task panel no longer pushes the conversation off the screen. It shows five tasks and keeps the rest behind
+N more; expanded or collapsed is remembered. When the ledger read itself is capped, the toggle saysshowing 20 of 34rather than implying the list is complete. -
An approval prompt is announced at most twice. The deck header's auto-reject countdown steps aside while the Fleet cockpit's Approvals tab is open and listing that workspace's prompt, since that row already counts the same deadline down. An inbox showing somebody else's prompts no longer silences it.
-
The titlebar stopped showing gauges that never said anything. The memory reading appears only when the footprint is large or has outgrown what the window started with, holds through a small dip instead of blinking on the threshold, and is polled rarely while hidden. The clock is off unless you turn it on in Settings → Appearance (your OS already draws one).
-
Fewer wasted redraws behind the deck. The 15-second task poll used to hand the interface a brand-new list every tick even when nothing had changed, redrawing the deck four times a minute for nothing. A task the orchestrator starts also gets its channel link immediately now, rather than up to fifteen seconds later.
The new strings ship in English, Korean and Polish; every other language falls back to English until it is translated.
-
The UI now renders in Inter, the typeface the design system has always
specified. It is bundled with the app under the SIL Open Font License (Latin +
Latin Extended, +131 KB, full 100–900 weight range), so it looks the same on
every machine instead of falling back to whatever the OS supplies. Text
outside those scripts, including Hangul, still uses the system face. -
Every UI text size now lands on one of the four design steps — 10px section
labels, 11px meta and tool lines, 13px body, 14px titles. 116 places were
drifting onto in-between sizes (8, 9, 10.5, 11.5, 12.5px) that blurred the
hierarchy without adding one. Terminal text is unaffected. -
Corner radii follow the design system again: 5px on buttons and controls,
7px on cards, panels, popovers and dialogs. 75 surfaces were rounder than the
chrome allows. -
Inline
codein the orchestrator's replies is now mono on a quiet surface
instead of amber, and the workspace Mode control is a plain label with a
status dot (red for danger, amber for assist, gray for off) instead of a
tinted pill. Amber is reserved for things that are alive or need you, and a
single screen was spending it on dozens of code spans and an idle control. -
Modal dimming, inset highlights and hairlines now derive from theme tokens
rather than hardcoded white and black. Light themes were getting a highlight
lit the wrong way, and 33 colours carried a hardcoded fallback from an
unrelated palette that would have surfaced if a theme token ever went missing. -
The two light themes now dim behind a dialog more gently than the dark ones,
where the previous single value read as a blackout rather than a dimming.
Fixed
-
browser_replaynow lets a step's navigation land before looking for the next element. A click that started a Turbo/SPA navigation used to have the next step measured against the old page and stopped with "no … on the page any more" while the element was there a moment later (#1193). -
browser_waitis recorded as a replay step (url, selector, text, network idle) and counts toward the ring and per-flow step limits like any other action. A wait on a JS predicate is not recorded: a replay never evaluates a script from the cache file. -
A WSL pane gets an image path it can actually open. The Windows temp
directory is reachable from the Linux side only under/mnt, so the pasted
path pointed at a file the agent could not read. WSL panes now receive the
/mntview; PowerShell and Git Bash panes keep the Windows path. (#1209) -
A pane whose agent died without a Stop now settles to idle. An agent
killed mid-turn (double Ctrl+C,/exit, a crash) sends no Stop hook, so a
pane the hook had lit could stay amber long after the agent was gone. The
agent process's death is now its own settle path — and when wmux cannot tell
which process died, or never sees the death at all, the pane settles anyway
30 minutes after its last hook signal. A turn that has already finished
keeps its result: an agent exiting after its turn ended no longer wipes the
"finished" or "needs you" state you had not read yet. -
The sidebar roster no longer contradicts the dot above it. A workspace
row could show a running agent while its own roster row called the same pane
"Idle": the row aged the turn out after two quiet minutes, which is exactly
what a long, silent turn looks like. Both now read the same open turn. -
A pane that changes agents no longer inherits the previous one's status.
Startingcodexin a pane whereclaudehad exited mid-turn left the new
agent's dot governed by the old one's turn. -
An orchestrator now wakes when a worker's turn dies on an API error.
Claude Code reports that asStopFailureand noStop, and wmux published
no lifecycle event for it — so a Deck brain or fan-out parent waiting on the
worker sat on the stop gate with nothing to react to. The failed turn is now
its ownagent.lifecyclekind (agent.stop_failure), pollable through
wmux_events_polland woken on like a stop, but with its own reason so the
brain can tell a turn that finished from one that died. -
A pane whose turn keeps dying no longer loops the orchestrator. After three
consecutive failed turns on the same pane, the wake tells the brain to raise
it with the human instead of resuming into the same rate limit again. -
A Claude Code session that has only just started, sitting at its prompt with
no turn yet, no longer shows as "needs you" — the red status dot, the
"Waiting" label and the titlebar's "N need you" count all stay quiet until
the agent actually wants something. -
The Deck fleet roster's status dots now derive "running" from the same
signals the sidebar roster uses, so the two can no longer disagree about
whether the same pane is working. -
An unanswered decision no longer silently kills the delegation loop. A
workspace with a pending decision is never auto-woken — that part is
deliberate — but until now the events it blocked were dropped without a
trace, so a decision left over from a previous session ate every fan-out
worker's "I'm done" and the orchestrator simply looked asleep. The block is
now logged (once per workspace per minute, and again straight away for a new
decision), naming how many events it blocked and which delegated tasks they
belonged to. A worker's own events are no longer thrown away either: they are
parked in the same durable backlog that already holds events for a workspace
with no orchestrator, so they survive an app restart — the decision does —
and answering the decision replays them into the turn that follows. Answering
also restores the auto-wake budget the wait may have eaten. Ambient chatter is
still dropped, as before. -
fanout_startnow warns you at the moment it accepts when the launching
workspace has a pending decision, instead of starting workers whose reports
will not reach anyone until it is answered. The warning leads the tool's
answer as its own line, not just a field an agent can skim past. -
A refused
task_adopt,task_closeortask_prnow says so in a sentence
before it says so in JSON. The answer opens withREFUSED (<reason>): <error>, states that nothing was adopted / closed / opened, and names the
one move that clears it — because an orchestrator reading an envelope for a
commit sha was skimming pastok: falseand reporting an adopt that had
never happened. Refusals from the permission gate (an approval declined or
expired, a task that is not yours) now name their real cause instead of
reading as "unknown", and the JSON itself is left untouched so anything that
parses it still works. -
The task ledger explains itself when it refuses a status change. An illegal
transition now lists the statuses that ARE reachable from the current one —
and only the ones the caller is actually allowed to set, so it never suggests
a move that would be refused a moment later. Theledger_updatetool text
states the whole table, says plainly that a task can only be completed from
review_requested, and that an orchestrator may set that itself when the
worker cannot — withforceand a reason as the way past a missing gate. -
browser_typeandbrowser_fillnow acceptsmartRef(frombrowser_smart_snapshot) as well asref, the waybrowser_clickalready did. A smart ref passed asrefno longer reads as a missing element: the error names which ref space the argument was read in and which parameter the number belongs to. -
contenteditablefields (a rich-text title or caption) now count as interactive inbrowser_snapshot, page-level and under aselector. They were absent fromfilter: 'interactive'entirely, so a dialog built out of them looked like it had no fields. -
On the RPC transport (a surface with no live Chrome page),
browser_fillandbrowser_typestop when the element they were pointed at did not take focus, instead of typing over whichever field the page had focused. AsmartRefis refused there rather than resolved through abrowser_snapshottag that numbers a different element. -
The
LIVE_CHROME_UNAVAILABLEhint names the "Remote debugging" item in thechrome://inspectsidebar. Its oldchrome://inspect/#remote-debugginglink opens the Devices tab, leaving the user on the wrong page. -
A fan-out worker no longer launches on whatever model your shell profile
exports. The worker's command is typed into the pane's interactive login
shell, so anANTHROPIC_MODELexported by~/.zshrc(or any other rc file)
overrode the environment wmux had set and every worker's first turn came back
"There's an issue with the selected model", indistinguishable from an idle
worker. Aclaudeworker's launch now unsets that variable in the pane's own
shell, which is the only place late enough to win — and it does it without
bypassing your shell, so an aliasedclaude(whatclaude migrate-installer
leaves behind) still resolves.Three things call it off, each because wmux would otherwise be overruling a
choice you made: a launch that already passes--model; a role bound to an
agent and a model in Settings, whose flag is spliced in first; and a shell
that routes claude through a gateway (ANTHROPIC_BASE_URL), where the model
name is exactly what the gateway needs.ANTHROPIC_BASE_URLand
ANTHROPIC_AUTH_TOKENare never touched — routing claude through your own
proxy is a whole-machine choice, and a worker that quietly bypassed it would
be talking to a different endpoint than every other pane you open. Panes whose
shell is fish, PowerShell or nushell are left alone entirely. -
A fan-out worker whose first turn fails on its model is now reported as
input_requiredin the task ledger, with the model it was refused and where
that model came from, instead of sitting inworkingnext to an idle-looking
pane. wmux reports that screen rather than typing at it, and looks once more a
few seconds after the pane goes quiet — the error only arrives after the agent
has painted its composer and been refused. -
Orchestrator: a brain that fanned out work now learns when its workers stop or wait for input — worker lifecycle events are copied to the owning workspace tagged with the task, bypass the owner's
nonewake policy, and are parked as a backlog when the owner has no brain yet. -
An approval prompt waiting behind another no longer expires unseen. Each
prompt's 30-second countdown started when it was created, so a second one
queued behind the first could auto-deny having never been on screen — a
refusal nobody made. The clock now starts when the prompt is actually shown. -
An agent with autonomy on can answer its own workers' prompts again. The
check that keeps automated approval presses inside delegated task panes had no
way to learn which panes those were, so it refused every one of them. It is now
told, and a refusal says whether it was policy or missing wiring. Presses into
a pane you opened yourself are still refused, and a person answering from the
phone or the web was never subject to any of this. -
terminal_send({ submit: true })now reports whether the prompt was actually
committed. The result carriesaccepted— true only when the pane was
observed to move (its turn started, or the input line cleared) — plus
agentStatusAfter, and the pane's last screen lines when it did not. The
Enter is re-sent once before giving up. Previouslysubmitted: truemeant
only "a carriage return was written", so an orchestrator reported progress on
panes whose prompt was still sitting uncommitted in the composer. -
An orchestrator brain can finally reach the agents in its own workspace. A
brain owns no pane, so every same-workspace A2A reply it sent to an ADDRESSED
pane was suppressed as an "unverified sender" and merely stored — the brain
was told the message landed while the worker sat waiting. A caller carrying
the daemon-validated commander binding, for the workspace that binding names,
now satisfies that one guard. An anchorless reply is still suppressed (it
would fall back to whichever pane happens to be focused), and the self-loop
protection for pane callers is unchanged. -
An automated approval press is now scoped to panes that were actually
delegated: the target's workspace must be a task workspace with autonomy on,
the prompt must have come from a hook rather than the screen-regex detector,
and a re-read must still show it. A fact the daemon cannot establish counts
as a refusal, and a refusal leaves the request live for a human to answer.
People are not subject to any of this — approving or denying from the phone
or the web works exactly as before — and a DENY is always allowed, from any
caller, because refusing one would keep a pane blocked in the name of safety. -
A channel wake nudge now carries the first line of the message it is waking
you for, so an agent no longer has to spend a turn reading just to find out
whether the nudge mattered. It rides only into panes wmux can name as an
agent TUI, with shell metacharacters stripped: the text comes from another
workspace and is committed with an Enter, so a pane that is really a shell
would run it. A nudge that never landed (the pane died mid-race) marks that
member's own rows, over the message range it announced,target_goneinstead
of leaving them looking pending forever — and a later ack promotes them back
todelivered. A nudge that DID land is still not a delivery receipt. -
wmux channel unreadand thechannel_unreadtool now answer from the same
daemon call and report the same set. The CLI no longer takes the member from
$WMUX_MEMBER_ID(only an explicit--member, matching the tool) and no
longer hides caught-up rows, so the two surfaces can no longer contradict
each other about what you owe. Each row names its member, and a workspace
holding several says so, since without--memberthe rows shown are the
whole workspace's and not only yours. -
Fan-out workers no longer freeze on Claude Code's first-run screens. A
claude
worker is launched with the environment flag that skips the workspace-trust
dialog (wmux writes nothing to your global Claude Code config), and a worker
left sitting on a known one-shot onboarding screen is dismissed automatically.
One that is still stuck is reported asinput_requiredin the task ledger
instead of looking idle forever. SetWMUX_AGENT_FIRST_RUN=offto turn both
behaviours off. -
A fan-out task workspace now inherits the autonomy of the workspace that
launched it, so a brain running indangercan actually act on its workers'
approvals. Previously every task workspace was created with no autonomy entry
at all, which reads asoff. An owner inassiststill gets workers whose
approvals must be answered by a human — that is whatassistmeans. -
The channel composer's "no channel or workspace identity" post failure and the
agent mention-loop warning are translated (en/ko/pl) instead of always English. -
The members roster's agent liveness dot carries an accessible label, so a
screen reader reports whether an agent's pane is live or gone. -
Two channel members of the same workspace that have no live pane can both be
@mentioned in one post — the second mention used to be dropped silently. -
A "no answer — nudges exhausted" mark applies only to the messages that were
already posted when the wake worker gave up, and clears once that member
catches up, leaves, or the channel is archived. It used to stick to every
message you ever posted in that channel. -
A project with no lint or test script can finish a task again. The
completion gate refuses to certify a repository it cannot grade, but it also
recorded nothing when there was nothing to run — and the task ledger will not
mark a task completed without a recorded pass. So any repository that declares
neitherscripts/verify.shnor npmlint/testscripts could only be closed
by forcing it. Running the gate on such a project now records an honest
verdict — a pass whose command isnoneand whose note says no gate exists —
and the task completes normally. The two skips that mean the gate could not
run (missing dependencies, a command that would not start) still record
nothing, because there a human should look. The waiver needs the parent
repository to agree: a task worktree that has lost the lint or test script its
project declares records a failing gate naming what is missing, not a pass.
This also unblocks projects that are not Node projects at all, which used to
be turned away for having nonode_modulesbefore anything asked whether they
had a gate to run. -
The orchestrator can read the repository it just adopted into.
git_status
andgit_logonly accepted a task id, so after taking a task's work into the
parent checkout there was no way to look at that checkout — the parent
repository is not a task. Both now work with no task id at all and answer for
the repository your own terminal is in. -
The orchestrator's Stop gate and
deck_complete_workno longer treat a plain shell pane as an outstanding worker. Typing a command into your own terminal made that pane reportrunning, which refused the brain's completion withworkers_outstandingand held its turn open on a pane only you could clear. Both gates now count a pane only when wmux has evidence it holds an agent, and their refusal text says "agent panes". A pane wmux is still unsure about keeps holding the turn, so a worker is never released by mistake. -
The auto-wake heartbeat no longer wakes the orchestrator about a pane it is not allowed to act on: a shell that is waiting or finished is skipped, exactly as the Stop gate skips it.
-
Your shells keep their kill protection. A busy shell no longer blocks the brain's turn, but the brain still cannot end that session with
exitor Ctrl+D — the two rules are now tracked separately. -
A fan-out task workspace no longer spawns an orchestrator brain of its own. Since the task workspace started inheriting its owner's Deck mode (so the owner's brain may press its approvals), that mode also made it brain-eligible: every worker got a brain that consumed the worker's own stop events before the owner ever saw them, one extra Claude session per worker, held open by the Stop gate. Task workspaces are now brain-less for as long as their task is open — the composer and every ambient driver answer
task_workspace— and a worker's stop reaches only the owner, tagged with its task.
What's Changed
- fix(browser-replay): let a step's navigation land before the next step, and record browser_wait by @openwong2kim in #1194
- chore(mcp): raise the commander tools/list budget to 60000 by @openwong2kim in #1195
- feat(deck): task ledger, worker-event routing to the owning brain, and a ledger-backed Stop gate by @openwong2kim in #1198
- feat(worktask): gate runner, task-level adopt/close/pr and read-only git tools for the orchestrator by @openwong2kim in #1197
- test(mcp): scan only the index.ts wiring point for reserved commander tool names by @openwong2kim in #1200
- feat(input): a real submit receipt, a verified brain sender, scoped approval presses and an honest channel nudge by @openwong2kim in #1199
- feat(orch): wire the task tools, gate close/PR behind human approval, and scope automated approval presses by @openwong2kim in #1201
- chore(release): 3.51.0 by @openwong2kim in #1202
- chore: revert the 3.51.0 release commit (not shipped) by @openwong2kim in #1203
- feat(orch): workers survive first-run screens, task workspaces inherit autonomy, and the brain presses approvals through the record by @openwong2kim in #1206
- feat(deck): task status panel, ledgerGate toggle, approval countdown and brain vendor stamps by @openwong2kim in #1204
- feat(channels): honest mention targets and delivery status, Fleet approval countdown, cleanup from Missions by @openwong2kim in #1205
- fix(orch): a no-gate repo can complete, adopt can commit, and git_status reads the caller's repo by @openwong2kim in #1207
- fix(browser): smartRef everywhere, contenteditable refs, multiline typing, snapshot text filter by @openwong2kim in #1208
- docs(internal): orchestrator wave 1 and wave 2 live dogfood record by @openwong2kim in #1211
- fix(orch): a fan-out task workspace never runs a brain of its own by @openwong2kim in #1212
- fix(orch): a pending decision no longer eats delegated wakes silently, refusals read REFUSED, and the ledger names its legal moves by @openwong2kim in #1214
- fix(deck): the stop gate and deck_complete_work only wait on agent panes by @openwong2kim in #1213
- docs(internal): wave 3 second dogfood run (findings 11-14 on the packaged build) by @openwong2kim in #1215
- fix(fanout): a worker's model is wmux's choice, not the operator's shell profile by @openwong2kim in #1216
- docs(internal): wave 3 third dogfood run (finding 15 on the packaged build) by @openwong2kim in #1217
- docs(design): decisions from the 2026-09-05 design audit by @openwong2kim in #1218
- feat(ui): bundle Inter, enforce the four-step type scale, and put every chrome colour on a token by @openwong2kim in #1220
- feat(ui): one status-dot vocabulary, sidebar tasks as a summary, two approval renditions, honest titlebar gauges by @openwong2kim in #1219
- feat(ui): 24 px hit areas, accessible names for every icon button, and a copy diet by @openwong2kim in #1221
- docs(internal): UX dogfood after the design-audit lanes (metrics, approval timeout, new-user path) by @openwong2kim in #1222
- feat(sidebar): name-first rows, amber only for running, needs-you rows say so by @openwong2kim in #1223
- feat(status): a Claude Code pane is running because its hook said so, not because it printed 2KB by @openwong2kim in #1224
- fix(status): failed worker turns wake the brain, a fresh session is not needs-you, deck dots match the sidebar by @openwong2kim in #1225
- Let the agent read a pasted image itself, and give WSL panes a path they can open by @openwong2kim in #1209
- chore(deps): bump softprops/action-gh-release from 3.0.2 to 3.0.3 in the release-actions group by @dependabot[bot] in #1154
- chore(deps): bump the npm-security group across 1 directory with 3 updates by @dependabot[bot] in #1189
- chore(release): 3.51.0 by @openwong2kim in #1226
Full Changelog: v3.50.1...v3.51.0