Skip to content

cmux-next: honest Claude Reasoning menu: resolved default, Ultracode switch, no Ultrathink (cx-ervhd) - #20592

Merged
teamleaderleo merged 14 commits into
feat-cmux-nextfrom
op-next-parity-surface/honest-pickers
Oct 15, 2026
Merged

teamleaderleo merged 14 commits into
feat-cmux-nextfrom
op-next-parity-surface/honest-pickers

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

The Reasoning menu for Claude didn't match what Claude Code does with each choice. On a fresh chat the chip said Low with no row checked, even though Claude was really running its default level. Ultrathink was listed as an effort level, but all it did was prefix every prompt with a keyword. Ultracode was listed as a level too, though it's a separate Claude Code setting (workflow orchestration at Extra High). Muse, a terminal-only harness, also showed up in the model rail. The full audit is cx-ervhd.

After this change:

  • Default resolves to a real level. When a session runs Claude's default, the chip names that level (for example "Medium") and the menu checks that row with a small "Default" hint. The level comes from the model's catalog row, which is now found through aliases and variant ids like claude-opus-5-5[1m]. Picking a level after that sends it as before. The menu no longer falls back to the first row, and a label that couldn't be resolved is never saved to chip memory. That fallback was where "Low" came from; Low was never actually sent.
  • Ultracode is an On/Off switch in the Reasoning menu. It's shown only when the model offers Extra High. acpmux reports its state as a separate ultracode option and sends apply_flag_settings {effortLevel: xhigh, ultracode: true}. The pane switches it through the free effort option (ultracode for On, xhigh for Off), so the app never confirms it with a mode sheet. Picking a plain level turns it off, and a respawn keeps it on. Turning it off leaves the chat on Extra High.
  • Ultrathink is gone from the effort list, and acpmux no longer rewrites prompts. A saved ultrathink pick runs Claude's default level instead. People can still type the keyword themselves.
  • Fast mode is shown only for models whose catalog row allows it.
  • New chats show the level acpmux will start them on before the session reports back. The same hunk is in the New Tab fix cmux-next: a new chat's effort chip names the level acpmux starts it on (Lawrence: "we lost effort") #20577; whichever of the two lands second will pick it up in its base merge.
  • Terminal harnesses keep kind through normalizeCatalog, so Muse leaves the rail. Greying out signed-out harnesses needs acpmux auth detection and is tracked separately (cx-ae9eb).

Evidence

  • cmux-tui/crates/acpmux/tests/claude_reasoning.rs runs against fake Claude Code and records the argv and every apply_flag_settings it receives.

    • Red on c500a31 (tests only): lists_effort_levels_and_ultracode_is_its_own_switch and a_saved_ultrathink_pick_runs_the_default_level_unrewritten failed. Hosted run 38543681183.
    • Green on e7c109a: all 3 pass. Hosted run 38554360275, test (linux).
  • webviews/test/claude-reasoning-default.test.ts plays two gallery variants in the browser lane:

    • the default effort on a [1m] model id reads "Medium" and is checked with a Default hint, with no Ultrathink row;
    • a new chat shows "Medium" before its session attaches.

    This PR's react-apps-check runs it on the head. I didn't record a separate red run for this file.

  • webviews/test/ultracode-rides-effort.test.tsx (jsdom) mounts the real picker and records what it sends. It was red on 14df3d7 (ultracode=on, ultracode=off) and is green on 463b34d (effort=ultracode, effort=xhigh). That commit came from the review subagent: the first version picked acpmux's own ultracode id, which the Swift transport confirms with a "Switch mode" sheet.

  • The crash ratchet and the Rust god-file check pass on the head.

  • Leo (via coordinator): a correctness fix, merge on the gate.

Fast mode now follows the catalog row's fast flag. A live model with probed efforts and no catalog row of its own keeps no Fast row unless fast mode is already on.

Beads for follow-ups: cx-ae9eb (signed-out rail), cx-z3dra (fast-mode cooldown), cx-42wql (Opus 4.7 default effort and the gallery xhigh fixture), cx-ch019 / cx-5n4f5 (Codex service tier and plan mode).

Changelog

  • Fixed: The Claude Reasoning menu now shows the real default level instead of "Low", has Ultracode as an On/Off switch, and no longer lists Ultrathink as an effort level.

Tagged DEV builds on macOS 27 (cosmic-gray), each run with an isolated fake home: stub harnesses only (a fake Claude Code listing made-up models, a fake codex that reports "Not logged in", a terminal-only "muse"), no real credentials or chats. The location chip shows the build host's name. Base is feat 8ae8e3b; head is this PR at c144f6d (before its last base merges, which don't touch these surfaces). The pair also differs in base-only ways: base's rail has "Add harness…", and head's composer has the "Trusted this chat's…" chip that squeezes the effort chip to "Medi…".

Light: base | head. Reasoning menu: base lists Ultracode and Ultrathink as levels; head checks "Medium · Default", has an Ultracode On/Off group and no Ultrathink. Model rail: base has Muse; head doesn't.
light base vs head

Dark: base | head.
dark base vs head

Clip (light, head): open Reasoning, turn Ultracode On (the chip names Ultracode), then Off (the chat stays on Extra High).
reasoning ultracode
Full quality: reasoning-ultracode-light-head.mp4

Earlier dark capture with real Claude Opus 5.5 chats (base b35b1c6 above, head 3dc336b below):
before after

🤖 Generated with Claude Code

teamleaderleo and others added 7 commits October 14, 2026 02:24
…own switch (cx-ervhd)

Red: the claude_stdio Reasoning select still lists Ultracode and Ultrathink
as levels and Ultrathink rewrites every prompt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hub_integration.rs is over the god-file budget; it does not grow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l (cx-ervhd)

Red: a chat on Claude Code's own level on claude-opus-5-5[1m] shows Low with
no row checked (local Chromium play: chip=Low, waitFor did not hold).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ode as its own switch, no Ultrathink (cx-ervhd)

acpmux claude_stdio: Reasoning lists Claude Code's effort levels only.
Ultracode (a Claude Code setting, xhigh plus workflow orchestration) is its
own On/Off option; Ultrathink, which only prefixed every prompt, is gone and a
remembered pick of it runs the default level. Respawns, starts and saved
picks keep Ultracode.

webviews: the agent's own level names the level it resolves to (the running
model's default effort, through an alias, a [1m] variant or a resolved default
model), checked with a Default hint; never the first level, and an unresolved
label is not remembered. Fast mode shows only where the model honors it.
Terminal harnesses (Muse Code) stay off the provider rail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…easoning and Fast mode (cx-ervhd)

Lawrence 2026-10-14, "we lost effort": the New Tab composer showed only the
model chip. Red (local Chromium play on the base logic): chips = model only,
waitFor did not hold.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s it on (cx-ervhd)

Before a chat's session reports its options, the effort chip drew only a
level a previous chat on that harness had remembered, so a fresh profile or
harness showed the model chip alone. It now falls back to _acpmux/harnesses
start.effort.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…x-ervhd)

Ultracode, like Fast mode, is a switch the person gate keeps for the app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 14, 2026 11:24 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 525788ec-ea9d-41a6-8f43-928b74ddad18

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

teamleaderleo and others added 2 commits October 14, 2026 04:34
A pick on acpmux's own ultracode id is a mode switch the app confirms with a
sheet and refuses a non-person pane (review of #20592).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (cx-ervhd)

The Reasoning menu's Ultracode switch sends effort=ultracode (On) and
effort=xhigh (Off), which acpmux already honors, so the app never confirms it
with a mode sheet. A new chat that starts on Ultracode names it on its pending
chip, and the default-level play lists an older acpmux's Ultrathink so it
proves the menu drops it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 14, 2026 11:34 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

all web/size checks green

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Passes: CI passes on 115d4da777.

CI passes on 115d4da777 (run 38662649423 attempt 1).

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

CI fast guards passes on 115d4da777 (https://github.com/manaflow-ai/cmux/actions/runs/38662649423). This covers only the fast guards, not CI: CI's result is the ci-status check, and the CI failure attribution comment names any failing test.

@blacksmith-sh

This comment has been minimized.

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

cmux-next tour of c144f6d137bc1d2f7c686c1205c03e6c7bff9031 (run)

The tour job failed before the tour ran (build or setup); the run log has the output.

The tour produced no media.

Tours are data in scripts/cmux-next/media/tours: add a step or a tour file to show your feature.

…parity-surface/honest-pickers

# Conflicts:
#	webviews/src/agent-session/acpmux/ComposerPickers.tsx
@cursor

cursor Bot commented Oct 14, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 14, 2026 16:51 — with GitHub Actions Inactive
…parity-surface/honest-pickers

# Conflicts:
#	cmux-tui/crates/acpmux/tests/fake_claude.py
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 14, 2026 23:13 — with GitHub Actions Inactive
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Visual preview of c144f6d: in the maintainers' private review feed.

- vp fmt webviews/src/pages/home-channels/cloudHome.ts
- cargo fmt in Native/OptChat/optchat-chief

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38652714129
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 15, 2026 00:54 — with GitHub Actions Inactive
@teamleaderleo
teamleaderleo merged commit 4107f35 into feat-cmux-next Oct 15, 2026
176 of 189 checks passed
@teamleaderleo
teamleaderleo deleted the op-next-parity-surface/honest-pickers branch October 15, 2026 03:57
teamleaderleo added a commit that referenced this pull request Oct 15, 2026
…-fit

- composerLocation.css: the 220 px step is a container query that
  overrides #20785's --acpmux-row-group-gap and --acpmux-row-edge.
- ComposerPickers.tsx: keeps #20592's effortShown memory; an Ultracode
  pick shows as the model chip's suffix.
- composer-row-rhythm waits on the model chip (effort folds into it);
  composer-row-steps warms its dev server (browser-lanes rule).
- Localizable.xcstrings rebuilt from base plus the five effort.short.*
  keys; generated strings regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 15, 2026
…r tests

check-browser-test-lanes (#20733) requires warmViteServer after listen;
claude-reasoning-default (#20592) landed without it and fails the base check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 15, 2026
#20884)

#20592 landed after #20873 without warmViteServer, so check:browser-lanes is
red on base again.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 15, 2026
#20880)

* cx-tgf4h red: cmux chief -p under ACPMUX_AGENT_LAUNCH=deny fails at once with a typed refusal

* cx-tgf4h: the Chief says at once that agents are disabled in this build session

With ACPMUX_AGENT_LAUNCH=deny (the nx-remote gui/input default since HQ
#2130) or CMUX_AGENTS_DISABLED=1, no Chief turn can start an agent.
`cmux chief -p` started a daemon and a brain and waited 240 s for a reply
that could not come.

- cmux-tui-core agent_launch: one test of the kill switch (acpmux
  launch_policy.rs), the reason agent_launch_disabled.
- `cmux chief` (pipe and chat) refuses before it starts or sends anything:
  operation.failed, details.reason agent_launch_disabled, exit 1.
- The daemon's supervisor starts no Chief brain; chief.engine.* and
  chief.stop answer the typed refusal at once.
- optchat-chief host does not start (logs why, exit 0).
- Home: the local Chief conversation's notice says agents are disabled;
  the engine panel maps the typed refusal (localized, all Home languages).
- Every Chief proof script refuses at once under deny and names
  `nx-remote --allow-agents`.

* test: code pages follow the terminal font; font variables resolve

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cx-tgf4h review: forward chief control to a running brain first; daemon-side deny test; engine/stop CLI text; guard before side effects; fmt

* acpmux tests: every acpmux process a test starts gets a hermetic environment (cx-s8wux)

One helper, tests/support/daemon_env.rs, starts the acpmux binary for every
integration test: env_clear, a bare PATH (python3 plus named host tools,
then the system directories), a scratch HOME, and ACPMUX_AGENT_LAUNCH=deny
unless the test drives a stand-in agent (Agents::StandIn keeps the bare
PATH). The 26 test files that started the binary with the host environment
use it; tests/hermetic_daemon_guard.rs refuses a test file that names
CARGO_BIN_EXE_acpmux anywhere else.

Root cause of cx-w014j: a fleet host's PATH holds real codex/claude, which a
test daemon discovered and ran (probes, sign-in checks, auto-updates).

* cx-tgf4h: fmt and clippy

* acpmux tests: the hermetic HOME is <scratch>/home, never a chat's folder

* acpmux tests: review fixes for the hermetic environment (cx-s8wux)

python3 links the interpreter itself (sys.executable), not a version
manager's shim; stale tool links from an earlier run are replaced; the guard
also flags daemon_env::bin() outside a listed use; the live codex test probes
only codex; codex_bridge_config's discovery test runs with launches denied.

* acpmux tests: router_install_secret starts the router through daemon_env (cx-s8wux)

* fix: code pages draw in the terminal's font; agent pane defines --font-mono

The diff, markdown and code editor pages were written to follow the terminal font
(appearance.ts, editorFont), but no cmux NEXT host ever sent it, so code always drew
in Menlo. Every page theme now carries the terminal's family (cmux.json
terminal.fontFamily, else the Ghostty config's font-family), resent when it changes.

The agent pane's mono text read --font-mono, which only Tailwind's theme layer
emitted because a source file happened to name it; conversation.css defines it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: fix two Chief profile relay base reds (concurrency check, Release compile crash) (no bead) (#20876)

* cmux-next: mark the Chief profile relay retry loop as bounded (no bead)

check-concurrency flags its while true as a loop that can spin. It retries
once, only after a lost connection, and every other error throws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: nonisolated deinit for PageCallDeadline.Once (no bead)

The Release compile crashes swift-frontend 6.3.3 in EarlyPerfInliner on
the isolated deinit of this main-actor generic class (exit 65, no error
line), the same crash #20408 fixed elsewhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cmux-next): the Chief profile relay's retry is a two-attempt for loop, not while true with a wakeup-allow (cx-ebm.59.5)

* cmux-next sidebar perf round 3: Release-build hitch bench with real wheel input; O(1) hover-card hit

- scripts/cmux-next/sidebar-rows-perf-release.py: runs in a Release tagged app (no debug.* methods) inside
  nx-remote --needs input: CLI workspaces (50 with a terminal, 950 empty), HID-tap wheel events, Animation
  Hitches per gesture, A/B of two apps in one job (side by side, interleaved), traces and the recorder's ktrace
  files removed after each export, PTY count logged.
- SidebarListView+HoverCard: the hover-card hit reads the list's row dictionary instead of
  SidebarModel.isPlaceholder, an O(workspaces) section walk on every scroll frame (7-12% of main-thread samples
  while scrolling 1,000 rows in Release).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels: a primary or danger button keeps its fill under the pointer (a soft ring shows the hover); gallery play primary-hover

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels gallery: primary-hover asserts the hover ring and the unchanged fill; a budget with a reason for the Create frame

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-motion: Chrome tab strip tween (200 ms easeOutQuad, restart from value), tabStrip geometry tokens, damping <= 0 takes the fallback (hq-1d, no bead)

Additive: MotionTween {TabOpen, TabClose, TabMove}, TweenCurve::EaseOutQuad,
ease_out_quad, TweenParameters, TweenValue (f64 core: retarget restarts the
duration from the current value with no velocity; the same target does not
restart), Tween (f32, token-driven), MotionPolicy::tween (0 under speed off,
Reduce Motion, or tabOpen/tabClose with ui.animations.newTab off),
TabStripGeometry (design-tokens.json motion.tabStrip: max 200, open/close
18, min 56 active / 32 inactive / 64 pinned, reorder 16 x width / 200,
closing mode +40 below / +60 trailing / 0.3 s). cmux-app-ffi ABI 2 exports
cmux_motion_tween_{base,seconds,retarget,step}, cmux_motion_ease and
cmux_motion_tab_strip_geometry. SpringParameters::finite_or now treats a
damping fraction <= 0 like NaN (hq-4e nit). The parity corpus example
prints tween paths (60 Hz: 12 frames for 200 ms).

* fix: read the font from the files the Ghostty config loaded, includes too

Review: a font-family in a config-file include won in the terminal but not on pages.
Read the runtime's loaded files (with includes, in load order) instead of guessing
paths, repaint after the last override is set, and fix a stale doc comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: record a session's git branch and HEAD at start (hq#1920) (#20840)

* test(acpmux): record a session's git branch and head at start

A session should keep its folder's branch and HEAD commit as they were at
session/new, and a fork its own folder's state at fork time (hq#1920), so
agent spend can be joined to the pull request a session produced. These
fail today: neither the saved meta nor _acpmux/info carries gitBranch or
gitHead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(acpmux): record a session's git branch and head at start

session/new and session/fork now store the cwd's checked-out branch and
full HEAD commit in the session meta (gitBranch, gitHead), so a session
can later be joined to the pull request it produced (hq#1920). The values
are read once, as the session starts, from the repository's own files
(HEAD, loose refs, packed-refs, a linked worktree's gitdir and commondir),
never a git process, and off the async runtime. A detached HEAD records
only the head; a folder outside a repository records neither; a lookup
that fails never fails the session. A fork reads its own folder at fork
time instead of copying the parent's. _acpmux/info carries both fields.
Old session.json files load unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(acpmux): git state skips non-files, bad branch names and missing folders; follows symlinks

Review of #20840: a FIFO HEAD hung session/new, a symlinked folder was walked
lexically, a missing fork folder took its parent repository's state, and an
invalid ref name was stored as the branch. The test git also runs without the
machine's global or system config.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(acpmux): git state reads only small regular files, the physical folder and valid ref names

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* cx-tgf4h: keep CmuxNextApp line count (App size cap)

* acpmux: a pooled agent host no longer spins on Linux reading its own record (cx-7z8yx)

The record watch used notify's inotify backend, whose watch mask always
includes open, close-write and modify, and ran its callback on every event.
The callback reads the host record; on Linux that read queued an open and a
close in the watched directory, which ran the callback again. Every pooled
host spun at 100% CPU (about 200,000 read calls a second).

Linux now has its own inotify watch, like the macOS kqueue watch: a mask of
entry changes only (create, delete, moved from/to) and the directory's own
delete or move, one thread in poll with no timeout, an eventfd to stop it.
A read of the record never queues an event. The notify fallback (other
platforms) ignores access events. macOS is unchanged.

Behavior test (tests/host_lifetime.rs, Linux): a real daemon pools a host;
idle for 3 s it makes fewer than 100 read calls and 30 CPU ticks (before:
599,042 reads, 298 ticks), a rename rewrite of its record keeps it, and the
record removed ends it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui smoke: sidebar drag checks the drawn sidebar document order

Since sidebar-v2 S6 the TUI draws the daemon's sidebar document, where a new
workspace goes to the top (workspaces.newPlacement default), and a workspace
drop is one sidebar.move that changes the document only. The smoke still
assumed the list-workspaces order: it dragged the drawn top row (the new
workspace) below the original, which the product did, and then read
list-workspaces, which a sidebar.move never changes.

The smoke now reads the order from sidebar.get, checks that the rail draws
it, drags the top row below the second (a real reorder), and drags again when
needed so the original workspace ends below the new one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* PR dock chips; tooltips only where nothing else names the control (#20375)

* test: no tooltip on an element whose text shows

Red on base: 105 elements with visible text carry title/data-tooltip.
Command: bun test test/tooltip-rules.test.ts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: PR dock chips; tooltips only where nothing else names the control

The pull request dock's bars become content-sized chips (icon, #N, repo, title cut off at
360 px) with the dismiss button inside and no tooltip. Across the pages, a row, chip, link,
path or thought whose text shows carries no title tooltip any more; tooltips stay on
icon-only controls, shortcuts and text shown nowhere else (why a control is disabled or
failed, who reacted, policy detail), each marked tooltip-allow. Tooltips wait the native
hover delay (about a second) and show at once when moving straight on to the next one.

Green: bun test test/tooltip-rules.test.ts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: review of tooltip audit

Keep the PR dock's state colours, drop four more tooltips the lint now finds inside
components (a setting's summary, the branch and folder pickers, a video), let a click or
scroll end the quick next-tooltip window, and move two tests to the no-tooltip rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: Backdrop Art's line has no tooltip

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 047c247

- vp fmt webviews/src/pages/history/HistoryPage.tsx

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38502564051

* test(mock): the worked chat opens its pull request, so mock mode shows the PR dock

Captures of the agent pane in a real window (--showcase runs the in-page
mock) now show the pull request dock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the PR dock takes its own space; the transcript ends above it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 39f3ca9

- vp fmt webviews/test/ime-composition-keys.test.ts
- vp fmt webviews/test/new-tab-omnibar-pointer.test.ts

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38567629461

* chore(next): PR autofix at c7cb261

- regenerated Packages/macOS/CmuxNext/ci-target-graph.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38592980576

* fix: a channel image keeps its name tooltip (the name shows only when it fails)

Base added title={part.name} on channel image attachments (AttachmentViews.tsx).
A loaded image shows no name, so the tooltip is its only label; mark it with
tooltip-allow. tooltip-rules.test.ts: red before, green after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at f221864

- regenerated Packages/macOS/CmuxNext/ci-target-graph.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38633116142

* fix: keep the tool card scope hint and Steer shortcut tooltips

Both explain something shown nowhere else, so they stay under tooltip-allow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 022cc8e

- vp fmt webviews/src/pages/home-channels/cloudHome.ts

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38647981824

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>

* ci: nightly-next recovery initializes the notices submodule before continuing accepted submissions

The recovery job's checkout had no ghostty-next submodule, so the bundle
notice check that notarize-nightly-dmg.sh runs after Apple accepts failed,
and run 38669958906 skipped five accepted builds as 'Apple did not accept'.
Initialize the submodule like the signing job does, and make the skip notice
say that a post-acceptance check can also be the cause.

* ci: the cmux NIGHTLY-identity job also initializes the notices submodule

Run 38669958906's publish-nightly-identity-legacy job saw Apple accept,
then failed the bundle notice check for lack of ghostty-next and retired
the accepted submission of run 38609024986.

* agent pane: progress and stop for a running Claude subagent or workflow (cx-liswe) (#20718)

* test: a running subagent or workflow shows what it does and what it has spent

Red on base: both rows read only "Running" (bun test
test/subagent-progress.test.tsx).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* webviews: a running subagent or workflow shows what it does and what it has spent

The subagent fold keeps acpmux's subagent_progress fields (action, tokens,
tool uses, a workflow's agents done of started), and the transcript's
subagent row adds them to its status line through one shared helper
(subagentProgress.ts) the summary list and the Background tasks pane can use.
Three plural strings, translated for every pane locale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: a running subagent row has a stop button

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: stop a running Claude subagent or workflow from its row

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: restore the catalog's base order after the merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: stopping a subagent leaves its row closed, and a refused stop says so

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: a subagent's stop button waits for Claude and says when the stop failed

The button is off while Claude has the request and until the subagent
ends; a refused or unanswered stop (10 s) turns it into "Couldn't stop
<name>" and it can be tried again. The pane no longer drops the error.
Also moves the stop wiring above showSubagent's doc comment and corrects
two comments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style: format the subagent progress test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: stop rejects without English the pane would never show

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: restore the catalog's base order after the merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 2fe6cc4

- vp fmt webviews/src/pages/home-channels/HomeChannelsPage.tsx

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38637059713

* test: the agent pane relays a subagent stop for its chat (cx-liswe)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: the agent pane may stop a subagent of its chat (cx-liswe)

Adds _acpmux/subagent_stop to the pane's method allowlist: session-scoped,
params sessionId and subagentSessionId, no gesture (it stops work, like
_acpmux/kill). Without it the host refused every stop click.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: mirror the subagent stop into the shared pane policy (cx-liswe)

The Swift allowlist took _acpmux/subagent_stop; policy.json, its parity
source for Rust hosts, gets the same request, session scope and params,
with cases for an in-scope stop, another chat and an unknown param.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* gallery: a light window with no theme named renders light (#20794)

* test(gallery): a light window with no theme named should show the light default

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(gallery): a light window with no theme named shows Apple System Colors Light

The gallery defaulted the theme to the dark side of the app's default pair
whatever the window appearance, so colorScheme=light (or Appearance: Light)
rendered every stage in dark colors under a light media query. The default
now follows the appearance, as the app's does with no theme set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>

* Channels view: gallery plays for a message link with no link read (red)

A Cloud conversation has no link resolver (G4), so the Messages view's switch hand-off
#/conversation/<id>/message/<id> showed an error. Two plays: a thread reply and a timeline
message, opened through the store's openLink with an owner that has no resolver. cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: open a known conversation's message link without the owner's link read

When conversation-link-resolve fails (a Cloud conversation, an older daemon) and the page knows
the conversation, it opens it, pages the timeline until the message shows, opens its thread when
it is a reply, and marks it. The Messages view's switch hands off Cloud selections this way.
cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: format parseConversationLink (vp fmt)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: link fallback review follow-ups

Ids in a known conversation's link may hold '-' (the gallery's sample ids); the scheme check
follows the daemon (cmux[-<tag>], any case); the fallback runs only for a Cloud conversation or
an owner without a link read, so a refused local link (a deleted message) still shows its error.
cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* acpmux: host_lifetime idle test cleans up from the start and never signals a reused pid (cx-7z8yx review)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui: move the build identity into a leaf crate so an edit no longer recompiles cmux-remote

cmux-remote's build script hashed the dirty source tree into
CMUX_TUI_BUILD_IDENTITY, so every edit in the workspace reran it and
recompiled cmux-remote and every crate above it (about 8 s of a 12.6 s
one-line edit loop on rbx). The same build script now lives in
cmux-build-identity, which only cmux-tui reads; cmux-remote takes the
identity as an argument (SshBootstrapConfig::defaults,
pinned_linux_payload). The identity value, its inputs and format, and
every skew and SSH re-bootstrap comparison are unchanged.

* test: warm the dev server in the Claude reasoning default browser test (#20884)

#20592 landed after #20873 without warmViteServer, so check:browser-lanes is
red on base again.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: frame-strip report as an NSWindow method, not a namespace enum (no bead) (#20887)

The package conventions lint fails every PR on the new all-static public
enum WindowFrameStrip (cx-me3xd). The report reads its window, so it is an
extension on NSWindow; same body, same report.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Home channels: the run card is an inline disclosure (no frame, steps on an indented rail)

The live and done run cards under a Chief's reply lose their box: "Working
for" / "Worked for", the step count, cost and Open sit inline under the
message, and the opened steps hang on a thin rail under the status mark. The
narrow rules stay: the first line never wraps, and below 480 px of card width
the step count and cost move under it with no leading separator.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Home channels: the run rail and columns follow the author's picture

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Home channels: the run sheet keeps its error line; run card columns in logical properties

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Revert "cmux-motion: Chrome tab strip tween ..." (8a3ee62) until the app-ffi release App 403 is fixed (run 38682698310) (hq-1d, chief go 2026-10-15, no bead)

The release App could not publish cmux-app-ffi-854febb26527 (HTTP 403 on
POST /releases), so the app FFI pin check stays red on the tip. The tween
re-lands with the publish and repin after the App is fixed.

* test(ci): nightly jobs that run the license check must init ghostty-next (#20893)

* test(ci): nightly jobs that run the license check must init ghostty-next

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ci): nightly recovery initializes ghostty-next before the license check

Apple accepted, stapled and launch-tested each recovered nightly-next build,
then verify-app-bundle-licenses.sh failed on the missing
ghostty-next/build.zig.zon and the build was skipped as not accepted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: terminal-face repaint lives in DesignSettings, not the App target

Keeps CmuxNextApp under its size cap: setting either terminal family
repaints pages when the face the terminal draws in changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
Co-authored-by: safe-push <safe-push@cmux.local>
teamleaderleo added a commit that referenced this pull request Oct 15, 2026
#20759)

* test: the strip scrollbar's thumb stays above a browser page

A Chromium page at the bottom of the column strip is a child window above
the scrollbar band, so it hid the thumb and took its clicks (cx-6so.44,
dock-column.md Known gaps).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: the strip scrollbar's thumb stays above a browser page

While shown, the thumb's band is an occluder of the window's overlay
host, so a Chromium page under it leaves the thumb showing and its clicks
to the bar. Updates dock-column.md's known gaps: reveal under an overlay
and the strip clip were already fixed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: a hidden screen's strip scrollbar leaves no hole in a browser page

Review of #20759: a screen switch hides the screen's view, not the bar,
so the old screen's thumb stayed an occluder and the new one's never came
back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: a hidden or sliding screen's strip scrollbar keeps its occluder current

The thumb's occluder now follows an ancestor's hide and unhide
(viewDidHide/viewDidUnhide), and each screen's origin as the screen switch
moves it (applyScreenFrames).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cx-tgf4h red: cmux chief -p under ACPMUX_AGENT_LAUNCH=deny fails at once with a typed refusal

* cx-tgf4h: the Chief says at once that agents are disabled in this build session

With ACPMUX_AGENT_LAUNCH=deny (the nx-remote gui/input default since HQ
#2130) or CMUX_AGENTS_DISABLED=1, no Chief turn can start an agent.
`cmux chief -p` started a daemon and a brain and waited 240 s for a reply
that could not come.

- cmux-tui-core agent_launch: one test of the kill switch (acpmux
  launch_policy.rs), the reason agent_launch_disabled.
- `cmux chief` (pipe and chat) refuses before it starts or sends anything:
  operation.failed, details.reason agent_launch_disabled, exit 1.
- The daemon's supervisor starts no Chief brain; chief.engine.* and
  chief.stop answer the typed refusal at once.
- optchat-chief host does not start (logs why, exit 0).
- Home: the local Chief conversation's notice says agents are disabled;
  the engine panel maps the typed refusal (localized, all Home languages).
- Every Chief proof script refuses at once under deny and names
  `nx-remote --allow-agents`.

* cx-tgf4h review: forward chief control to a running brain first; daemon-side deny test; engine/stop CLI text; guard before side effects; fmt

* cx-tgf4h: fmt and clippy

* cx-tgf4h: keep CmuxNextApp line count (App size cap)

* acpmux: a pooled agent host no longer spins on Linux reading its own record (cx-7z8yx)

The record watch used notify's inotify backend, whose watch mask always
includes open, close-write and modify, and ran its callback on every event.
The callback reads the host record; on Linux that read queued an open and a
close in the watched directory, which ran the callback again. Every pooled
host spun at 100% CPU (about 200,000 read calls a second).

Linux now has its own inotify watch, like the macOS kqueue watch: a mask of
entry changes only (create, delete, moved from/to) and the directory's own
delete or move, one thread in poll with no timeout, an eventfd to stop it.
A read of the record never queues an event. The notify fallback (other
platforms) ignores access events. macOS is unchanged.

Behavior test (tests/host_lifetime.rs, Linux): a real daemon pools a host;
idle for 3 s it makes fewer than 100 read calls and 30 CPU ticks (before:
599,042 reads, 298 ticks), a rename rewrite of its record keeps it, and the
record removed ends it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui smoke: sidebar drag checks the drawn sidebar document order

Since sidebar-v2 S6 the TUI draws the daemon's sidebar document, where a new
workspace goes to the top (workspaces.newPlacement default), and a workspace
drop is one sidebar.move that changes the document only. The smoke still
assumed the list-workspaces order: it dragged the drawn top row (the new
workspace) below the original, which the product did, and then read
list-workspaces, which a sidebar.move never changes.

The smoke now reads the order from sidebar.get, checks that the rail draws
it, drags the top row below the second (a real reorder), and drags again when
needed so the original workspace ends below the new one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci: nightly-next recovery initializes the notices submodule before continuing accepted submissions

The recovery job's checkout had no ghostty-next submodule, so the bundle
notice check that notarize-nightly-dmg.sh runs after Apple accepts failed,
and run 38669958906 skipped five accepted builds as 'Apple did not accept'.
Initialize the submodule like the signing job does, and make the skip notice
say that a post-acceptance check can also be the cause.

* ci: the cmux NIGHTLY-identity job also initializes the notices submodule

Run 38669958906's publish-nightly-identity-legacy job saw Apple accept,
then failed the bundle notice check for lack of ghostty-next and retired
the accepted submission of run 38609024986.

* agent pane: progress and stop for a running Claude subagent or workflow (cx-liswe) (#20718)

* test: a running subagent or workflow shows what it does and what it has spent

Red on base: both rows read only "Running" (bun test
test/subagent-progress.test.tsx).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* webviews: a running subagent or workflow shows what it does and what it has spent

The subagent fold keeps acpmux's subagent_progress fields (action, tokens,
tool uses, a workflow's agents done of started), and the transcript's
subagent row adds them to its status line through one shared helper
(subagentProgress.ts) the summary list and the Background tasks pane can use.
Three plural strings, translated for every pane locale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: a running subagent row has a stop button

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: stop a running Claude subagent or workflow from its row

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: restore the catalog's base order after the merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: stopping a subagent leaves its row closed, and a refused stop says so

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: a subagent's stop button waits for Claude and says when the stop failed

The button is off while Claude has the request and until the subagent
ends; a refused or unanswered stop (10 s) turns it into "Couldn't stop
<name>" and it can be tried again. The pane no longer drops the error.
Also moves the stop wiring above showSubagent's doc comment and corrects
two comments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style: format the subagent progress test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: stop rejects without English the pane would never show

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: restore the catalog's base order after the merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 2fe6cc4

- vp fmt webviews/src/pages/home-channels/HomeChannelsPage.tsx

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38637059713

* test: the agent pane relays a subagent stop for its chat (cx-liswe)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: the agent pane may stop a subagent of its chat (cx-liswe)

Adds _acpmux/subagent_stop to the pane's method allowlist: session-scoped,
params sessionId and subagentSessionId, no gesture (it stops work, like
_acpmux/kill). Without it the host refused every stop click.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: mirror the subagent stop into the shared pane policy (cx-liswe)

The Swift allowlist took _acpmux/subagent_stop; policy.json, its parity
source for Rust hosts, gets the same request, session scope and params,
with cases for an in-scope stop, another chat and an unknown param.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* gallery: a light window with no theme named renders light (#20794)

* test(gallery): a light window with no theme named should show the light default

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(gallery): a light window with no theme named shows Apple System Colors Light

The gallery defaulted the theme to the dark side of the app's default pair
whatever the window appearance, so colorScheme=light (or Appearance: Light)
rendered every stage in dark colors under a light media query. The default
now follows the appearance, as the app's does with no theme set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>

* Channels view: gallery plays for a message link with no link read (red)

A Cloud conversation has no link resolver (G4), so the Messages view's switch hand-off
#/conversation/<id>/message/<id> showed an error. Two plays: a thread reply and a timeline
message, opened through the store's openLink with an owner that has no resolver. cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: open a known conversation's message link without the owner's link read

When conversation-link-resolve fails (a Cloud conversation, an older daemon) and the page knows
the conversation, it opens it, pages the timeline until the message shows, opens its thread when
it is a reply, and marks it. The Messages view's switch hands off Cloud selections this way.
cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: format parseConversationLink (vp fmt)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: link fallback review follow-ups

Ids in a known conversation's link may hold '-' (the gallery's sample ids); the scheme check
follows the daemon (cmux[-<tag>], any case); the fallback runs only for a Cloud conversation or
an owner without a link read, so a refused local link (a deleted message) still shows its error.
cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* acpmux: host_lifetime idle test cleans up from the start and never signals a reused pid (cx-7z8yx review)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui: move the build identity into a leaf crate so an edit no longer recompiles cmux-remote

cmux-remote's build script hashed the dirty source tree into
CMUX_TUI_BUILD_IDENTITY, so every edit in the workspace reran it and
recompiled cmux-remote and every crate above it (about 8 s of a 12.6 s
one-line edit loop on rbx). The same build script now lives in
cmux-build-identity, which only cmux-tui reads; cmux-remote takes the
identity as an argument (SshBootstrapConfig::defaults,
pinned_linux_payload). The identity value, its inputs and format, and
every skew and SSH re-bootstrap comparison are unchanged.

* test: warm the dev server in the Claude reasoning default browser test (#20884)

#20592 landed after #20873 without warmViteServer, so check:browser-lanes is
red on base again.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: frame-strip report as an NSWindow method, not a namespace enum (no bead) (#20887)

The package conventions lint fails every PR on the new all-static public
enum WindowFrameStrip (cx-me3xd). The report reads its window, so it is an
extension on NSWindow; same body, same report.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Home channels: the run card is an inline disclosure (no frame, steps on an indented rail)

The live and done run cards under a Chief's reply lose their box: "Working
for" / "Worked for", the step count, cost and Open sit inline under the
message, and the opened steps hang on a thin rail under the status mark. The
narrow rules stay: the first line never wraps, and below 480 px of card width
the step count and cost move under it with no leading separator.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Home channels: the run rail and columns follow the author's picture

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Home channels: the run sheet keeps its error line; run card columns in logical properties

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Revert "cmux-motion: Chrome tab strip tween ..." (8a3ee62) until the app-ffi release App 403 is fixed (run 38682698310) (hq-1d, chief go 2026-10-15, no bead)

The release App could not publish cmux-app-ffi-854febb26527 (HTTP 403 on
POST /releases), so the app FFI pin check stays red on the tip. The tween
re-lands with the publish and repin after the App is fixed.

* test(ci): nightly jobs that run the license check must init ghostty-next (#20893)

* test(ci): nightly jobs that run the license check must init ghostty-next

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ci): nightly recovery initializes ghostty-next before the license check

Apple accepted, stapled and launch-tested each recovered nightly-next build,
then verify-app-bundle-licenses.sh failed on the missing
ghostty-next/build.zig.zon and the build was skipped as not accepted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
Co-authored-by: safe-push <safe-push@cmux.local>
teamleaderleo added a commit that referenced this pull request Oct 15, 2026
* feat(cx-gakp): unify slash commands, skills and prompts

Keep the existing Milkdown editor and its URL, mention and attachment behavior. Label ACP command sources so skills and prompts share the same searchable picker.

* chore(next): PR autofix at cade19a

- vp fmt webviews/src/agent-session/acpmux/composer.gallery.ts

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38535762014

* fix(cx-gakp): localize slash picker labels and cmux command descriptions

The Skill/Prompt labels and the four cmux command descriptions were
hard-coded English, which pane-english rejects. They now come from the
pane's table in all 21 languages; cmux commands carry a descriptionKey.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cx-gakp): keep Codex skills' $ prefix in the slash picker

codex-acp names each skill `$name` and passes `/$name` through to
Codex as a skill mention. Stripping the $ inserted `/name`, which ran a
same-named builtin or nothing, and deduped the skill behind cmux's own
commands. Keep the name and use the $ as the skill signal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at f4ed982

- regenerated schemas/defaults/cmux-next.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38541336447

* fix(cx-gakp): read the skill tag from ACP _meta in the slash picker

acpmux will tag Claude skills as _meta.kind (ACP keeps extensions in
_meta); read it alongside codex-acp's $ prefix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cx-gakp): slash picker play finds its rows by name; harness stays harness

- The gallery play waited for exactly 3 rows, but cmux's /import shares
  the menu; it now finds the fixture rows by name, asserts Skill and
  Prompt on their own rows and no label on a harness command, and clears
  the "/" before typing the draft.
- The comment no longer claims acpmux tags Claude skills.
- /continue's description keeps "harness" as the catalog does elsewhere
  (17 locales said "tool").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: regenerate pane strings after the base merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* webviews: regenerate pane strings after the base merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Chief registry: chief.profile.adopt makes an existing agent participant a Chief record under its own id

The owner (a trusted local Unix caller acting as the person) adopts an agent that is already in a
conversation: the record keeps the participant's id and display name, so its DMs, history and
credential stay its own, and gets chief.profile.create's defaults and only the fields given.
A page (WebSocket or the host's page relay, even with a gesture), a paired or remote device and a
bound agent get origin.forbidden; there is no page route. agent_mux is default_chief, a live record
already_chief, a deleted Chief deleted_chief (an adopt never revives its tombstone or kept memory),
and a person or an unknown id not_an_agent_participant. The write goes through the daemon's
registry ledger (idempotent per key) and the same chief_written hook as a create.
Capability chief-profile-adopt-v1; CLI cmux chief adopt AGENT [PROFILE], refused for an agent
caller. Real-daemon tests for each refusal and for an adopt that keeps the DM and its history.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chief.profile.adopt review fixes: owner gate first, any page origin refused, brain directory collisions refused, tests for the event and the limit

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chief.profile.adopt: refuse a bound participant, revoke its earlier credentials in the same write, and only the brain (or, with no supervisor, the person's terminal) mints the Chief's credential

The adopt claims the participant inside the registry write: under the bindings lock it refuses a
participant with a bound connection (participant_bound, "stop its current agent session first")
and otherwise revokes every credential issued for it. conversation-bind now checks the credential
and binds under the same lock, so a revoked credential never binds after the adopt. For a Chief
record other than agent_mux, conversation-agent-token is refused while a brain supervisor runs
(the supervisor mints the brain's credential in process); with no supervisor only the person's
own terminal mints it, never a page, a paired or remote device or an agent. Tests: a bound
participant is refused; after it unbinds the adopt succeeds and the brain starts; the old
credential is refused; the brain's credential binds; the person mints the post-adopt credential
on a daemon with no supervisor; a page, a device and an agent cannot mint it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chief.profile.adopt: the Chief-credential check and the mint run under the registry lock

Review finding: a mint that passed the Chief check before an adopt could overwrite the brain's
credential after it. conversation-agent-token now checks and mints inside one registry-locked step
(registry, then the conversation store, the adopt's order).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Regenerate bindings after merge

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chief.profile.adopt: clippy (one write struct for mutate, no redundant clone)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* resource_attach: chief.profile.adopt is the 54th connection operation

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-next: CmuxNextApp cap back to 113692 (frame-strip proof analysis and animation palette toggles leave the App target) (cx-me3xd)

The debug.new_tab frame_strip analysis (frame comparisons, PNGs, report fields) moves to
WindowFrameStrip in CmuxNextDesign beside WindowFrame and MotionFrameProgress; the drag_new
gesture moves to TabStripDebug.dragFirstTab in CmuxNextTabs. DebugFrameStrip keeps only the
wiring (record, send key or gesture, spans), so plus, drag_new and every report field stay.
The ui.animations.<kind> toggles and bounce bind through AnimationKindsSetting.bindActions
(CmuxNextSettings) on the shared SettingChoiceActions save, as ChromeStyleSetting does (ported
from nx-grow-land-fallback b1e0c989). CmuxNextApp 113810 -> 113692; the cap follows.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cx-tgf4h red: cmux chief -p under ACPMUX_AGENT_LAUNCH=deny fails at once with a typed refusal

* cx-tgf4h: the Chief says at once that agents are disabled in this build session

With ACPMUX_AGENT_LAUNCH=deny (the nx-remote gui/input default since HQ
#2130) or CMUX_AGENTS_DISABLED=1, no Chief turn can start an agent.
`cmux chief -p` started a daemon and a brain and waited 240 s for a reply
that could not come.

- cmux-tui-core agent_launch: one test of the kill switch (acpmux
  launch_policy.rs), the reason agent_launch_disabled.
- `cmux chief` (pipe and chat) refuses before it starts or sends anything:
  operation.failed, details.reason agent_launch_disabled, exit 1.
- The daemon's supervisor starts no Chief brain; chief.engine.* and
  chief.stop answer the typed refusal at once.
- optchat-chief host does not start (logs why, exit 0).
- Home: the local Chief conversation's notice says agents are disabled;
  the engine panel maps the typed refusal (localized, all Home languages).
- Every Chief proof script refuses at once under deny and names
  `nx-remote --allow-agents`.

* Channels view: gallery plays for the host's home.currentSelection (red: the page does not serve it yet)

The host-callable command type (hostActions.ts) and two plays: a conversation, the keyboard's
current message and the open thread; the docked list pane answers null. cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: home.currentSelection for the Home dock's view switch

The page serves window.cmuxHomeActions["home.currentSelection"] (the agent page's
cmuxAcpmuxActions pattern; the host calls it with callAsyncJavaScript): {conversation,
message?, thread?} = the open conversation, the keyboard's current message or the message a
link marked, and the open thread's root; null on the narrow list pane or with nothing open.
The host never reads the page hash for the hand-off. cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: serve home.currentSelection on the page bridge, not a window global

The host calls it with PageRouter.callPage (the typed host-to-page call the page bridge already
has; PageClient.handle on the page): {} -> {conversation, message?} | null. message = the
keyboard's current message, else the message a link marked, else the open thread's root. The
gallery plays drive a real BridgePageClient through __cmuxPageReceive, as the app does. cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: home.currentSelection review follow-ups

Layout effect for the answered selection; null before the page's first measure (a docked list
pane never reports a hidden conversation); the gallery host uses a private bridge target; the
thread case is its own play (the gallery opens the thread). cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* acpmux: Codex picks run the in-process bridge, not the npx codex-acp adapter (#20800)

* test(acpmux): the npx codex-acp adapter is offered beside the Codex bridge

Red: with codex and npx on PATH and no codex-acp, discovery keeps the
pinned npx adapter (@agentclientprotocol/codex-acp) as `codex-acp` next
to the in-process bridge. That adapter reads the account through codex's
account/read, which fails for a ChatGPT login behind a routing proxy
(subrouter): its sessions never start ("Authentication required").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: Codex picks run the in-process bridge, not the npx codex-acp adapter

With codex installed and no codex-acp on PATH, discovery kept the pinned
npx adapter (@agentclientprotocol/codex-acp@1.10.0) as `codex-acp` beside
the in-process bridge. The adapter reads the account through codex's
account/read; with codex 0.162 a ChatGPT login behind a routing proxy
(subrouter) fails that ("selected workspace missing from routing
discovery"), so its chats never started ("Authentication required"),
while the bridge works on the same login.

Discovery no longer offers the npx adapter: an installed codex is the
bridge, and `codex-acp` names the bridge too (like `codex-app-server`), so
sessions, presets and model picks that recorded it run the bridge. A
codex-acp on PATH, an ~/.acpx entry and a config-written launch are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: one description for the bridge's aliases (rustfmt)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* cx-tgf4h review: forward chief control to a running brain first; daemon-side deny test; engine/stop CLI text; guard before side effects; fmt

* test: warm the dev server in three agent pane browser tests (#20873)

check:browser-lanes (#20733) requires warmViteServer after listen; #20752,
#20780 and #20785 landed tests without it, so react-apps-check is red on base.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux tests: every acpmux process a test starts gets a hermetic environment (cx-s8wux)

One helper, tests/support/daemon_env.rs, starts the acpmux binary for every
integration test: env_clear, a bare PATH (python3 plus named host tools,
then the system directories), a scratch HOME, and ACPMUX_AGENT_LAUNCH=deny
unless the test drives a stand-in agent (Agents::StandIn keeps the bare
PATH). The 26 test files that started the binary with the host environment
use it; tests/hermetic_daemon_guard.rs refuses a test file that names
CARGO_BIN_EXE_acpmux anywhere else.

Root cause of cx-w014j: a fleet host's PATH holds real codex/claude, which a
test daemon discovered and ran (probes, sign-in checks, auto-updates).

* cx-tgf4h: fmt and clippy

* acpmux tests: the hermetic HOME is <scratch>/home, never a chat's folder

* acpmux tests: review fixes for the hermetic environment (cx-s8wux)

python3 links the interpreter itself (sys.executable), not a version
manager's shim; stale tool links from an earlier run are replaced; the guard
also flags daemon_env::bin() outside a listed use; the live codex test probes
only codex; codex_bridge_config's discovery test runs with launches denied.

* cmux-tui: agent-session-attach takes the agent pane's whole kind list (up to 32; the pane asks 9 since prompt bookmarks), so a remote chat shows after Trust instead of falling back to New Tab with bad request (cx-kpd0v)

* agent pane: the attach asks at most 8 kinds again (session offers come with transcript), so an older remote cmux-tui shows the chat (cx-kpd0v)

* cmux-tui: test fake needs no mut (cx-kpd0v)

* acpmux tests: router_install_secret starts the router through daemon_env (cx-s8wux)

* cmux-next composer: keep base's catalog order; regenerate strings on the merged base

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: one typed protocol registry; generated web types and drift checks (hq#2025, no bead) (#20576)

* acpmux: typed protocol registry and IR (hq#2025, no bead)

One registry names every acpmux method and notification; sessions and
permissions carry serde/schemars types, and the daemon builds
SessionSummary and PermissionGroup from them. ir() emits
spec/acpmux-protocol.json for the web codegen. Tests fail when the
committed IR is stale or the dispatcher serves a name the registry lacks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* webviews: acpmux protocol codegen, typed request helper and drift check (hq#2025, no bead)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux protocol: committed IR, generated web types; permissions and session_changed use them (hq#2025, no bead)

The permission group client and permission_respond go through the typed
request; session_changed and permission_pending are narrowed by the
registry's notification map. The IR declares a peer summary's optional
fields and peer key, permission_pending's always-sent keys, and closed
param objects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* webviews: format direct.ts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* webviews: acpmux codegen lives beside the pane protocol codegen, outside the pane sources

Its developer-facing IR errors tripped pane-english, which scans
src/agent-session/acpmux for user-visible English.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 1bb7576

- cargo fmt in Native/OptChat/optchat-chief

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38569912312

* chore(next): PR autofix at c601de5

- regenerated Packages/macOS/CmuxNext/ci-target-graph.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38593588859

* chore(next): PR autofix at 7a7fb66

- regenerated Packages/macOS/CmuxNext/ci-target-graph.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38606761975

* acpmux protocol: register offer_session and offer_respond

Base (#20305) serves two new methods; the registry's completeness test
caught them. Both are untyped requests for now. Spec and generated
TS are regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux protocol: register prompt_withdraw

Base serves _acpmux/prompt_withdraw; the completeness test caught it. Spec and
generated TS are regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 4e1d3c9

- vp fmt webviews/src/pages/home-channels/HomeChannelsPage.tsx

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38637598208

* acpmux protocol: register the process methods

Base serves _acpmux/process/list and process/kill and sends
_acpmux/process_update; the completeness test caught them. Spec and generated
TS are regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 64ca080

- vp fmt webviews/src/pages/home-channels/cloudHome.ts
- cargo fmt in Native/OptChat/optchat-chief

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38649959912

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>

* cmux-next: fix two Chief profile relay base reds (concurrency check, Release compile crash) (no bead) (#20876)

* cmux-next: mark the Chief profile relay retry loop as bounded (no bead)

check-concurrency flags its while true as a loop that can spin. It retries
once, only after a lost connection, and every other error throws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: nonisolated deinit for PageCallDeadline.Once (no bead)

The Release compile crashes swift-frontend 6.3.3 in EarlyPerfInliner on
the isolated deinit of this main-actor generic class (exit 65, no error
line), the same crash #20408 fixed elsewhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cmux-next): the Chief profile relay's retry is a two-attempt for loop, not while true with a wakeup-allow (cx-ebm.59.5)

* cmux-next sidebar perf round 3: Release-build hitch bench with real wheel input; O(1) hover-card hit

- scripts/cmux-next/sidebar-rows-perf-release.py: runs in a Release tagged app (no debug.* methods) inside
  nx-remote --needs input: CLI workspaces (50 with a terminal, 950 empty), HID-tap wheel events, Animation
  Hitches per gesture, A/B of two apps in one job (side by side, interleaved), traces and the recorder's ktrace
  files removed after each export, PTY count logged.
- SidebarListView+HoverCard: the hover-card hit reads the list's row dictionary instead of
  SidebarModel.isPlaceholder, an O(workspaces) section walk on every scroll frame (7-12% of main-thread samples
  while scrolling 1,000 rows in Release).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels: a primary or danger button keeps its fill under the pointer (a soft ring shows the hover); gallery play primary-hover

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels gallery: primary-hover asserts the hover ring and the unchanged fill; a budget with a reason for the Create frame

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-motion: Chrome tab strip tween (200 ms easeOutQuad, restart from value), tabStrip geometry tokens, damping <= 0 takes the fallback (hq-1d, no bead)

Additive: MotionTween {TabOpen, TabClose, TabMove}, TweenCurve::EaseOutQuad,
ease_out_quad, TweenParameters, TweenValue (f64 core: retarget restarts the
duration from the current value with no velocity; the same target does not
restart), Tween (f32, token-driven), MotionPolicy::tween (0 under speed off,
Reduce Motion, or tabOpen/tabClose with ui.animations.newTab off),
TabStripGeometry (design-tokens.json motion.tabStrip: max 200, open/close
18, min 56 active / 32 inactive / 64 pinned, reorder 16 x width / 200,
closing mode +40 below / +60 trailing / 0.3 s). cmux-app-ffi ABI 2 exports
cmux_motion_tween_{base,seconds,retarget,step}, cmux_motion_ease and
cmux_motion_tab_strip_geometry. SpringParameters::finite_or now treats a
damping fraction <= 0 like NaN (hq-4e nit). The parity corpus example
prints tween paths (60 Hz: 12 frames for 200 ms).

* acpmux: record a session's git branch and HEAD at start (hq#1920) (#20840)

* test(acpmux): record a session's git branch and head at start

A session should keep its folder's branch and HEAD commit as they were at
session/new, and a fork its own folder's state at fork time (hq#1920), so
agent spend can be joined to the pull request a session produced. These
fail today: neither the saved meta nor _acpmux/info carries gitBranch or
gitHead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(acpmux): record a session's git branch and head at start

session/new and session/fork now store the cwd's checked-out branch and
full HEAD commit in the session meta (gitBranch, gitHead), so a session
can later be joined to the pull request it produced (hq#1920). The values
are read once, as the session starts, from the repository's own files
(HEAD, loose refs, packed-refs, a linked worktree's gitdir and commondir),
never a git process, and off the async runtime. A detached HEAD records
only the head; a folder outside a repository records neither; a lookup
that fails never fails the session. A fork reads its own folder at fork
time instead of copying the parent's. _acpmux/info carries both fields.
Old session.json files load unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(acpmux): git state skips non-files, bad branch names and missing folders; follows symlinks

Review of #20840: a FIFO HEAD hung session/new, a symlinked folder was walked
lexically, a missing fork folder took its parent repository's state, and an
invalid ref name was stored as the branch. The test git also runs without the
machine's global or system config.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(acpmux): git state reads only small regular files, the physical folder and valid ref names

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* cx-tgf4h: keep CmuxNextApp line count (App size cap)

* acpmux: a pooled agent host no longer spins on Linux reading its own record (cx-7z8yx)

The record watch used notify's inotify backend, whose watch mask always
includes open, close-write and modify, and ran its callback on every event.
The callback reads the host record; on Linux that read queued an open and a
close in the watched directory, which ran the callback again. Every pooled
host spun at 100% CPU (about 200,000 read calls a second).

Linux now has its own inotify watch, like the macOS kqueue watch: a mask of
entry changes only (create, delete, moved from/to) and the directory's own
delete or move, one thread in poll with no timeout, an eventfd to stop it.
A read of the record never queues an event. The notify fallback (other
platforms) ignores access events. macOS is unchanged.

Behavior test (tests/host_lifetime.rs, Linux): a real daemon pools a host;
idle for 3 s it makes fewer than 100 read calls and 30 CPU ticks (before:
599,042 reads, 298 ticks), a rename rewrite of its record keeps it, and the
record removed ends it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui smoke: sidebar drag checks the drawn sidebar document order

Since sidebar-v2 S6 the TUI draws the daemon's sidebar document, where a new
workspace goes to the top (workspaces.newPlacement default), and a workspace
drop is one sidebar.move that changes the document only. The smoke still
assumed the list-workspaces order: it dragged the drawn top row (the new
workspace) below the original, which the product did, and then read
list-workspaces, which a sidebar.move never changes.

The smoke now reads the order from sidebar.get, checks that the rail draws
it, drags the top row below the second (a real reorder), and drags again when
needed so the original workspace ends below the new one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* PR dock chips; tooltips only where nothing else names the control (#20375)

* test: no tooltip on an element whose text shows

Red on base: 105 elements with visible text carry title/data-tooltip.
Command: bun test test/tooltip-rules.test.ts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: PR dock chips; tooltips only where nothing else names the control

The pull request dock's bars become content-sized chips (icon, #N, repo, title cut off at
360 px) with the dismiss button inside and no tooltip. Across the pages, a row, chip, link,
path or thought whose text shows carries no title tooltip any more; tooltips stay on
icon-only controls, shortcuts and text shown nowhere else (why a control is disabled or
failed, who reacted, policy detail), each marked tooltip-allow. Tooltips wait the native
hover delay (about a second) and show at once when moving straight on to the next one.

Green: bun test test/tooltip-rules.test.ts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: review of tooltip audit

Keep the PR dock's state colours, drop four more tooltips the lint now finds inside
components (a setting's summary, the branch and folder pickers, a video), let a click or
scroll end the quick next-tooltip window, and move two tests to the no-tooltip rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: Backdrop Art's line has no tooltip

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 047c247

- vp fmt webviews/src/pages/history/HistoryPage.tsx

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38502564051

* test(mock): the worked chat opens its pull request, so mock mode shows the PR dock

Captures of the agent pane in a real window (--showcase runs the in-page
mock) now show the pull request dock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the PR dock takes its own space; the transcript ends above it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 39f3ca9

- vp fmt webviews/test/ime-composition-keys.test.ts
- vp fmt webviews/test/new-tab-omnibar-pointer.test.ts

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38567629461

* chore(next): PR autofix at c7cb261

- regenerated Packages/macOS/CmuxNext/ci-target-graph.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38592980576

* fix: a channel image keeps its name tooltip (the name shows only when it fails)

Base added title={part.name} on channel image attachments (AttachmentViews.tsx).
A loaded image shows no name, so the tooltip is its only label; mark it with
tooltip-allow. tooltip-rules.test.ts: red before, green after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at f221864

- regenerated Packages/macOS/CmuxNext/ci-target-graph.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38633116142

* fix: keep the tool card scope hint and Steer shortcut tooltips

Both explain something shown nowhere else, so they stay under tooltip-allow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 022cc8e

- vp fmt webviews/src/pages/home-channels/cloudHome.ts

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38647981824

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>

* ci: nightly-next recovery initializes the notices submodule before continuing accepted submissions

The recovery job's checkout had no ghostty-next submodule, so the bundle
notice check that notarize-nightly-dmg.sh runs after Apple accepts failed,
and run 38669958906 skipped five accepted builds as 'Apple did not accept'.
Initialize the submodule like the signing job does, and make the skip notice
say that a post-acceptance check can also be the cause.

* ci: the cmux NIGHTLY-identity job also initializes the notices submodule

Run 38669958906's publish-nightly-identity-legacy job saw Apple accept,
then failed the bundle notice check for lack of ghostty-next and retired
the accepted submission of run 38609024986.

* agent pane: progress and stop for a running Claude subagent or workflow (cx-liswe) (#20718)

* test: a running subagent or workflow shows what it does and what it has spent

Red on base: both rows read only "Running" (bun test
test/subagent-progress.test.tsx).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* webviews: a running subagent or workflow shows what it does and what it has spent

The subagent fold keeps acpmux's subagent_progress fields (action, tokens,
tool uses, a workflow's agents done of started), and the transcript's
subagent row adds them to its status line through one shared helper
(subagentProgress.ts) the summary list and the Background tasks pane can use.
Three plural strings, translated for every pane locale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: a running subagent row has a stop button

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: stop a running Claude subagent or workflow from its row

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: restore the catalog's base order after the merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: stopping a subagent leaves its row closed, and a refused stop says so

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: a subagent's stop button waits for Claude and says when the stop failed

The button is off while Claude has the request and until the subagent
ends; a refused or unanswered stop (10 s) turns it into "Couldn't stop
<name>" and it can be tried again. The pane no longer drops the error.
Also moves the stop wiring above showSubagent's doc comment and corrects
two comments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style: format the subagent progress test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: stop rejects without English the pane would never show

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: restore the catalog's base order after the merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 2fe6cc4

- vp fmt webviews/src/pages/home-channels/HomeChannelsPage.tsx

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38637059713

* test: the agent pane relays a subagent stop for its chat (cx-liswe)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: the agent pane may stop a subagent of its chat (cx-liswe)

Adds _acpmux/subagent_stop to the pane's method allowlist: session-scoped,
params sessionId and subagentSessionId, no gesture (it stops work, like
_acpmux/kill). Without it the host refused every stop click.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: mirror the subagent stop into the shared pane policy (cx-liswe)

The Swift allowlist took _acpmux/subagent_stop; policy.json, its parity
source for Rust hosts, gets the same request, session scope and params,
with cases for an in-scope stop, another chat and an unknown param.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* gallery: a light window with no theme named renders light (#20794)

* test(gallery): a light window with no theme named should show the light default

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(gallery): a light window with no theme named shows Apple System Colors Light

The gallery defaulted the theme to the dark side of the app's default pair
whatever the window appearance, so colorScheme=light (or Appearance: Light)
rendered every stage in dark colors under a light media query. The default
now follows the appearance, as the app's does with no theme set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>

* Channels view: gallery plays for a message link with no link read (red)

A Cloud conversation has no link resolver (G4), so the Messages view's switch hand-off
#/conversation/<id>/message/<id> showed an error. Two plays: a thread reply and a timeline
message, opened through the store's openLink with an owner that has no resolver. cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: open a known conversation's message link without the owner's link read

When conversation-link-resolve fails (a Cloud conversation, an older daemon) and the page knows
the conversation, it opens it, pages the timeline until the message shows, opens its thread when
it is a reply, and marks it. The Messages view's switch hands off Cloud selections this way.
cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: format parseConversationLink (vp fmt)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: link fallback review follow-ups

Ids in a known conversation's link may hold '-' (the gallery's sample ids); the scheme check
follows the daemon (cmux[-<tag>], any case); the fallback runs only for a Cloud conversation or
an owner without a link read, so a refused local link (a deleted message) still shows its error.
cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* acpmux: host_lifetime idle test cleans up from the start and never signals a reused pid (cx-7z8yx review)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui: move the build identity into a leaf crate so an edit no longer recompiles cmux-remote

cmux-remote's build script hashed the dirty source tree into
CMUX_TUI_BUILD_IDENTITY, so every edit in the workspace reran it and
recompiled cmux-remote and every crate above it (about 8 s of a 12.6 s
one-line edit loop on rbx). The same build script now lives in
cmux-build-identity, which only cmux-tui reads; cmux-remote takes the
identity as an argument (SshBootstrapConfig::defaults,
pinned_linux_payload). The identity value, its inputs and format, and
every skew and SSH re-bootstrap comparison are unchanged.

* test: warm the dev server in the Claude reasoning default browser test (#20884)

#20592 landed after #20873 without warmViteServer, so check:browser-lanes is
red on base again.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: frame-strip report as an NSWindow method, not a namespace enum (no bead) (#20887)

The package conventions lint fails every PR on the new all-static public
enum WindowFrameStrip (cx-me3xd). The report reads its window, so it is an
extension on NSWindow; same body, same report.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Home channels: the run card is an inline disclosure (no frame, steps on an indented rail)

The live and done run cards under a Chief's reply lose their box: "Working
for" / "Worked for", the step count, cost and Open sit inline under the
message, and the opened steps hang on a thin rail under the status mark. The
narrow rules stay: the first line never wraps, and below 480 px of card width
the step count and cost move under it with no leading separator.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Home channels: the run rail and columns follow the author's picture

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Home channels: the run sheet keeps its error line; run card columns in logical properties

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Revert "cmux-motion: Chrome tab strip tween ..." (8a3ee62) until the app-ffi release App 403 is fixed (run 38682698310) (hq-1d, chief go 2026-10-15, no bead)

The release App could not publish cmux-app-ffi-854febb26527 (HTTP 403 on
POST /releases), so the app FFI pin check stays red on the tip. The tween
re-lands with the publish and repin after the App is fixed.

* test(ci): nightly jobs that run the license check must init ghostty-next (#20893)

* test(ci): nightly jobs that run the license check must init ghostty-next

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ci): nightly recovery initializes ghostty-next before the license check

Apple accepted, stapled and launch-tested each recovered nightly-next build,
then verify-app-bundle-licenses.sh failed on the missing
ghostty-next/build.zig.zon and the build was skipped as not accepted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep catalog in base order after feat merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
Co-authored-by: safe-push <safe-push@cmux.local>
teamleaderleo added a commit that referenced this pull request Oct 15, 2026
…ceiling, lower CPU weight; no process scanning) (#20755)

* test(acpmux): a chat's harness runs in its own cgroup scope on Linux (red)

cx-v9s8g, Linux half: systemd-run --user --scope with MemoryMax and
CPUWeight from the profile or daemon env; unscoped when systemd cannot
make a scope; no scope when both limits are 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(acpmux): each chat's harness in its own cgroup scope on Linux

cx-v9s8g, Linux half: session_scope.rs starts a session's harness under
systemd-run --user --scope (which execs it: same pid and process group)
with MemoryMax (ACPMUX_SESSION_MEMORY_MB, default half the machine's
memory) and CPUWeight (ACPMUX_SESSION_CPU_WEIGHT, default 50), from the
profile's env, then the daemon's; 0 turns one off. The kernel enforces
both, so nothing measures or scans processes. Without systemd-run, or
when it cannot make a scope (asked once per runner and limits), the
harness starts as before. macOS's process-group half stays with
ptyh-noscan after cx-t2sqm.3.

A runaway hitting MemoryMax ends that process, not the scope
(OOMPolicy=continue, dropped on a systemd that refuses it); a probe's
answer holds for 5 minutes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 348a805

- cargo fmt in Native/OptChat/optchat-chief

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38650955451

* cx-tgf4h red: cmux chief -p under ACPMUX_AGENT_LAUNCH=deny fails at once with a typed refusal

* cx-tgf4h: the Chief says at once that agents are disabled in this build session

With ACPMUX_AGENT_LAUNCH=deny (the nx-remote gui/input default since HQ
#2130) or CMUX_AGENTS_DISABLED=1, no Chief turn can start an agent.
`cmux chief -p` started a daemon and a brain and waited 240 s for a reply
that could not come.

- cmux-tui-core agent_launch: one test of the kill switch (acpmux
  launch_policy.rs), the reason agent_launch_disabled.
- `cmux chief` (pipe and chat) refuses before it starts or sends anything:
  operation.failed, details.reason agent_launch_disabled, exit 1.
- The daemon's supervisor starts no Chief brain; chief.engine.* and
  chief.stop answer the typed refusal at once.
- optchat-chief host does not start (logs why, exit 0).
- Home: the local Chief conversation's notice says agents are disabled;
  the engine panel maps the typed refusal (localized, all Home languages).
- Every Chief proof script refuses at once under deny and names
  `nx-remote --allow-agents`.

* cx-tgf4h review: forward chief control to a running brain first; daemon-side deny test; engine/stop CLI text; guard before side effects; fmt

* acpmux tests: every acpmux process a test starts gets a hermetic environment (cx-s8wux)

One helper, tests/support/daemon_env.rs, starts the acpmux binary for every
integration test: env_clear, a bare PATH (python3 plus named host tools,
then the system directories), a scratch HOME, and ACPMUX_AGENT_LAUNCH=deny
unless the test drives a stand-in agent (Agents::StandIn keeps the bare
PATH). The 26 test files that started the binary with the host environment
use it; tests/hermetic_daemon_guard.rs refuses a test file that names
CARGO_BIN_EXE_acpmux anywhere else.

Root cause of cx-w014j: a fleet host's PATH holds real codex/claude, which a
test daemon discovered and ran (probes, sign-in checks, auto-updates).

* cx-tgf4h: fmt and clippy

* acpmux tests: the hermetic HOME is <scratch>/home, never a chat's folder

* acpmux tests: review fixes for the hermetic environment (cx-s8wux)

python3 links the interpreter itself (sys.executable), not a version
manager's shim; stale tool links from an earlier run are replaced; the guard
also flags daemon_env::bin() outside a listed use; the live codex test probes
only codex; codex_bridge_config's discovery test runs with launches denied.

* acpmux tests: router_install_secret starts the router through daemon_env (cx-s8wux)

* fix(cmux-next): the Chief profile relay's retry is a two-attempt for loop, not while true with a wakeup-allow (cx-ebm.59.5)

* cmux-next sidebar perf round 3: Release-build hitch bench with real wheel input; O(1) hover-card hit

- scripts/cmux-next/sidebar-rows-perf-release.py: runs in a Release tagged app (no debug.* methods) inside
  nx-remote --needs input: CLI workspaces (50 with a terminal, 950 empty), HID-tap wheel events, Animation
  Hitches per gesture, A/B of two apps in one job (side by side, interleaved), traces and the recorder's ktrace
  files removed after each export, PTY count logged.
- SidebarListView+HoverCard: the hover-card hit reads the list's row dictionary instead of
  SidebarModel.isPlaceholder, an O(workspaces) section walk on every scroll frame (7-12% of main-thread samples
  while scrolling 1,000 rows in Release).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels: a primary or danger button keeps its fill under the pointer (a soft ring shows the hover); gallery play primary-hover

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels gallery: primary-hover asserts the hover ring and the unchanged fill; a budget with a reason for the Create frame

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-motion: Chrome tab strip tween (200 ms easeOutQuad, restart from value), tabStrip geometry tokens, damping <= 0 takes the fallback (hq-1d, no bead)

Additive: MotionTween {TabOpen, TabClose, TabMove}, TweenCurve::EaseOutQuad,
ease_out_quad, TweenParameters, TweenValue (f64 core: retarget restarts the
duration from the current value with no velocity; the same target does not
restart), Tween (f32, token-driven), MotionPolicy::tween (0 under speed off,
Reduce Motion, or tabOpen/tabClose with ui.animations.newTab off),
TabStripGeometry (design-tokens.json motion.tabStrip: max 200, open/close
18, min 56 active / 32 inactive / 64 pinned, reorder 16 x width / 200,
closing mode +40 below / +60 trailing / 0.3 s). cmux-app-ffi ABI 2 exports
cmux_motion_tween_{base,seconds,retarget,step}, cmux_motion_ease and
cmux_motion_tab_strip_geometry. SpringParameters::finite_or now treats a
damping fraction <= 0 like NaN (hq-4e nit). The parity corpus example
prints tween paths (60 Hz: 12 frames for 200 ms).

* acpmux: record a session's git branch and HEAD at start (hq#1920) (#20840)

* test(acpmux): record a session's git branch and head at start

A session should keep its folder's branch and HEAD commit as they were at
session/new, and a fork its own folder's state at fork time (hq#1920), so
agent spend can be joined to the pull request a session produced. These
fail today: neither the saved meta nor _acpmux/info carries gitBranch or
gitHead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(acpmux): record a session's git branch and head at start

session/new and session/fork now store the cwd's checked-out branch and
full HEAD commit in the session meta (gitBranch, gitHead), so a session
can later be joined to the pull request it produced (hq#1920). The values
are read once, as the session starts, from the repository's own files
(HEAD, loose refs, packed-refs, a linked worktree's gitdir and commondir),
never a git process, and off the async runtime. A detached HEAD records
only the head; a folder outside a repository records neither; a lookup
that fails never fails the session. A fork reads its own folder at fork
time instead of copying the parent's. _acpmux/info carries both fields.
Old session.json files load unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(acpmux): git state skips non-files, bad branch names and missing folders; follows symlinks

Review of #20840: a FIFO HEAD hung session/new, a symlinked folder was walked
lexically, a missing fork folder took its parent repository's state, and an
invalid ref name was stored as the branch. The test git also runs without the
machine's global or system config.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(acpmux): git state reads only small regular files, the physical folder and valid ref names

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* cx-tgf4h: keep CmuxNextApp line count (App size cap)

* acpmux: a pooled agent host no longer spins on Linux reading its own record (cx-7z8yx)

The record watch used notify's inotify backend, whose watch mask always
includes open, close-write and modify, and ran its callback on every event.
The callback reads the host record; on Linux that read queued an open and a
close in the watched directory, which ran the callback again. Every pooled
host spun at 100% CPU (about 200,000 read calls a second).

Linux now has its own inotify watch, like the macOS kqueue watch: a mask of
entry changes only (create, delete, moved from/to) and the directory's own
delete or move, one thread in poll with no timeout, an eventfd to stop it.
A read of the record never queues an event. The notify fallback (other
platforms) ignores access events. macOS is unchanged.

Behavior test (tests/host_lifetime.rs, Linux): a real daemon pools a host;
idle for 3 s it makes fewer than 100 read calls and 30 CPU ticks (before:
599,042 reads, 298 ticks), a rename rewrite of its record keeps it, and the
record removed ends it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui smoke: sidebar drag checks the drawn sidebar document order

Since sidebar-v2 S6 the TUI draws the daemon's sidebar document, where a new
workspace goes to the top (workspaces.newPlacement default), and a workspace
drop is one sidebar.move that changes the document only. The smoke still
assumed the list-workspaces order: it dragged the drawn top row (the new
workspace) below the original, which the product did, and then read
list-workspaces, which a sidebar.move never changes.

The smoke now reads the order from sidebar.get, checks that the rail draws
it, drags the top row below the second (a real reorder), and drags again when
needed so the original workspace ends below the new one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* PR dock chips; tooltips only where nothing else names the control (#20375)

* test: no tooltip on an element whose text shows

Red on base: 105 elements with visible text carry title/data-tooltip.
Command: bun test test/tooltip-rules.test.ts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: PR dock chips; tooltips only where nothing else names the control

The pull request dock's bars become content-sized chips (icon, #N, repo, title cut off at
360 px) with the dismiss button inside and no tooltip. Across the pages, a row, chip, link,
path or thought whose text shows carries no title tooltip any more; tooltips stay on
icon-only controls, shortcuts and text shown nowhere else (why a control is disabled or
failed, who reacted, policy detail), each marked tooltip-allow. Tooltips wait the native
hover delay (about a second) and show at once when moving straight on to the next one.

Green: bun test test/tooltip-rules.test.ts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: review of tooltip audit

Keep the PR dock's state colours, drop four more tooltips the lint now finds inside
components (a setting's summary, the branch and folder pickers, a video), let a click or
scroll end the quick next-tooltip window, and move two tests to the no-tooltip rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: Backdrop Art's line has no tooltip

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 047c247

- vp fmt webviews/src/pages/history/HistoryPage.tsx

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38502564051

* test(mock): the worked chat opens its pull request, so mock mode shows the PR dock

Captures of the agent pane in a real window (--showcase runs the in-page
mock) now show the pull request dock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the PR dock takes its own space; the transcript ends above it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 39f3ca9

- vp fmt webviews/test/ime-composition-keys.test.ts
- vp fmt webviews/test/new-tab-omnibar-pointer.test.ts

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38567629461

* chore(next): PR autofix at c7cb261

- regenerated Packages/macOS/CmuxNext/ci-target-graph.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38592980576

* fix: a channel image keeps its name tooltip (the name shows only when it fails)

Base added title={part.name} on channel image attachments (AttachmentViews.tsx).
A loaded image shows no name, so the tooltip is its only label; mark it with
tooltip-allow. tooltip-rules.test.ts: red before, green after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at f221864

- regenerated Packages/macOS/CmuxNext/ci-target-graph.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38633116142

* fix: keep the tool card scope hint and Steer shortcut tooltips

Both explain something shown nowhere else, so they stay under tooltip-allow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 022cc8e

- vp fmt webviews/src/pages/home-channels/cloudHome.ts

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38647981824

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>

* ci: nightly-next recovery initializes the notices submodule before continuing accepted submissions

The recovery job's checkout had no ghostty-next submodule, so the bundle
notice check that notarize-nightly-dmg.sh runs after Apple accepts failed,
and run 38669958906 skipped five accepted builds as 'Apple did not accept'.
Initialize the submodule like the signing job does, and make the skip notice
say that a post-acceptance check can also be the cause.

* ci: the cmux NIGHTLY-identity job also initializes the notices submodule

Run 38669958906's publish-nightly-identity-legacy job saw Apple accept,
then failed the bundle notice check for lack of ghostty-next and retired
the accepted submission of run 38609024986.

* agent pane: progress and stop for a running Claude subagent or workflow (cx-liswe) (#20718)

* test: a running subagent or workflow shows what it does and what it has spent

Red on base: both rows read only "Running" (bun test
test/subagent-progress.test.tsx).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* webviews: a running subagent or workflow shows what it does and what it has spent

The subagent fold keeps acpmux's subagent_progress fields (action, tokens,
tool uses, a workflow's agents done of started), and the transcript's
subagent row adds them to its status line through one shared helper
(subagentProgress.ts) the summary list and the Background tasks pane can use.
Three plural strings, translated for every pane locale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: a running subagent row has a stop button

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: stop a running Claude subagent or workflow from its row

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: restore the catalog's base order after the merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: stopping a subagent leaves its row closed, and a refused stop says so

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: a subagent's stop button waits for Claude and says when the stop failed

The button is off while Claude has the request and until the subagent
ends; a refused or unanswered stop (10 s) turns it into "Couldn't stop
<name>" and it can be tried again. The pane no longer drops the error.
Also moves the stop wiring above showSubagent's doc comment and corrects
two comments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style: format the subagent progress test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: stop rejects without English the pane would never show

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: restore the catalog's base order after the merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 2fe6cc4

- vp fmt webviews/src/pages/home-channels/HomeChannelsPage.tsx

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38637059713

* test: the agent pane relays a subagent stop for its chat (cx-liswe)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: the agent pane may stop a subagent of its chat (cx-liswe)

Adds _acpmux/subagent_stop to the pane's method allowlist: session-scoped,
params sessionId and subagentSessionId, no gesture (it stops work, like
_acpmux/kill). Without it the host refused every stop click.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: mirror the subagent stop into the shared pane policy (cx-liswe)

The Swift allowlist took _acpmux/subagent_stop; policy.json, its parity
source for Rust hosts, gets the same request, session scope and params,
with cases for an in-scope stop, another chat and an unknown param.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* gallery: a light window with no theme named renders light (#20794)

* test(gallery): a light window with no theme named should show the light default

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(gallery): a light window with no theme named shows Apple System Colors Light

The gallery defaulted the theme to the dark side of the app's default pair
whatever the window appearance, so colorScheme=light (or Appearance: Light)
rendered every stage in dark colors under a light media query. The default
now follows the appearance, as the app's does with no theme set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>

* Channels view: gallery plays for a message link with no link read (red)

A Cloud conversation has no link resolver (G4), so the Messages view's switch hand-off
#/conversation/<id>/message/<id> showed an error. Two plays: a thread reply and a timeline
message, opened through the store's openLink with an owner that has no resolver. cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: open a known conversation's message link without the owner's link read

When conversation-link-resolve fails (a Cloud conversation, an older daemon) and the page knows
the conversation, it opens it, pages the timeline until the message shows, opens its thread when
it is a reply, and marks it. The Messages view's switch hands off Cloud selections this way.
cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: format parseConversationLink (vp fmt)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: link fallback review follow-ups

Ids in a known conversation's link may hold '-' (the gallery's sample ids); the scheme check
follows the daemon (cmux[-<tag>], any case); the fallback runs only for a Cloud conversation or
an owner without a link read, so a refused local link (a deleted message) still shows its error.
cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* acpmux: host_lifetime idle test cleans up from the start and never signals a reused pid (cx-7z8yx review)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui: move the build identity into a leaf crate so an edit no longer recompiles cmux-remote

cmux-remote's build script hashed the dirty source tree into
CMUX_TUI_BUILD_IDENTITY, so every edit in the workspace reran it and
recompiled cmux-remote and every crate above it (about 8 s of a 12.6 s
one-line edit loop on rbx). The same build script now lives in
cmux-build-identity, which only cmux-tui reads; cmux-remote takes the
identity as an argument (SshBootstrapConfig::defaults,
pinned_linux_payload). The identity value, its inputs and format, and
every skew and SSH re-bootstrap comparison are unchanged.

* test: warm the dev server in the Claude reasoning default browser test (#20884)

#20592 landed after #20873 without warmViteServer, so check:browser-lanes is
red on base again.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: frame-strip report as an NSWindow method, not a namespace enum (no bead) (#20887)

The package conventions lint fails every PR on the new all-static public
enum WindowFrameStrip (cx-me3xd). The report reads its window, so it is an
extension on NSWindow; same body, same report.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Home channels: the run card is an inline disclosure (no frame, steps on an indented rail)

The live and done run cards under a Chief's reply lose their box: "Working
for" / "Worked for", the step count, cost and Open sit inline under the
message, and the opened steps hang on a thin rail under the status mark. The
narrow rules stay: the first line never wraps, and below 480 px of card width
the step count and cost move under it with no leading separator.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Home channels: the run rail and columns follow the author's picture

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Home channels: the run sheet keeps its error line; run card columns in logical properties

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Revert "cmux-motion: Chrome tab strip tween ..." (8a3ee62) until the app-ffi release App 403 is fixed (run 38682698310) (hq-1d, chief go 2026-10-15, no bead)

The release App could not publish cmux-app-ffi-854febb26527 (HTTP 403 on
POST /releases), so the app FFI pin check stays red on the tip. The tween
re-lands with the publish and repin after the App is fixed.

* test(ci): nightly jobs that run the license check must init ghostty-next (#20893)

* test(ci): nightly jobs that run the license check must init ghostty-next

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ci): nightly recovery initializes ghostty-next before the license check

Apple accepted, stapled and launch-tested each recovered nightly-next build,
then verify-app-bundle-licenses.sh failed on the missing
ghostty-next/build.zig.zon and the build was skipped as not accepted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
Co-authored-by: safe-push <safe-push@cmux.local>
teamleaderleo added a commit that referenced this pull request Oct 15, 2026
… one left edge (cx-w4q7j) (#20892)

* test: Chrome group look fixes from the sidebar groups verdict (red)

A collapsed header shows its member count, the members' line starts under the header bar
rounded, the header ink is white or near-black on the group color, and the header name starts
on the ungrouped rows' column (cg-design-judge 2026-10-14, cx-w4q7j).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: Chrome group look: collapsed count, line under the bar, on-color ink, ungrouped column

- A collapsed group header shows its member count after the name.
- The members' line starts just under the header bar with a rounded end, so it no longer cuts
  the bar's rounded corner.
- The header ink is white or near-black, whichever reads better on the group color, not the
  theme's text color (grey on the dark green header in Apple System Colors dark).
- The header name starts on the ungrouped rows' leading column (icon there, name on their text
  column), one left edge for headers and ungrouped rows.

cg-design-judge sidebar groups verdict 2026-10-14, cx-w4q7j.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cx-tgf4h red: cmux chief -p under ACPMUX_AGENT_LAUNCH=deny fails at once with a typed refusal

* cx-tgf4h: the Chief says at once that agents are disabled in this build session

With ACPMUX_AGENT_LAUNCH=deny (the nx-remote gui/input default since HQ
#2130) or CMUX_AGENTS_DISABLED=1, no Chief turn can start an agent.
`cmux chief -p` started a daemon and a brain and waited 240 s for a reply
that could not come.

- cmux-tui-core agent_launch: one test of the kill switch (acpmux
  launch_policy.rs), the reason agent_launch_disabled.
- `cmux chief` (pipe and chat) refuses before it starts or sends anything:
  operation.failed, details.reason agent_launch_disabled, exit 1.
- The daemon's supervisor starts no Chief brain; chief.engine.* and
  chief.stop answer the typed refusal at once.
- optchat-chief host does not start (logs why, exit 0).
- Home: the local Chief conversation's notice says agents are disabled;
  the engine panel maps the typed refusal (localized, all Home languages).
- Every Chief proof script refuses at once under deny and names
  `nx-remote --allow-agents`.

* cx-tgf4h review: forward chief control to a running brain first; daemon-side deny test; engine/stop CLI text; guard before side effects; fmt

* cx-tgf4h: fmt and clippy

* cx-tgf4h: keep CmuxNextApp line count (App size cap)

* acpmux: a pooled agent host no longer spins on Linux reading its own record (cx-7z8yx)

The record watch used notify's inotify backend, whose watch mask always
includes open, close-write and modify, and ran its callback on every event.
The callback reads the host record; on Linux that read queued an open and a
close in the watched directory, which ran the callback again. Every pooled
host spun at 100% CPU (about 200,000 read calls a second).

Linux now has its own inotify watch, like the macOS kqueue watch: a mask of
entry changes only (create, delete, moved from/to) and the directory's own
delete or move, one thread in poll with no timeout, an eventfd to stop it.
A read of the record never queues an event. The notify fallback (other
platforms) ignores access events. macOS is unchanged.

Behavior test (tests/host_lifetime.rs, Linux): a real daemon pools a host;
idle for 3 s it makes fewer than 100 read calls and 30 CPU ticks (before:
599,042 reads, 298 ticks), a rename rewrite of its record keeps it, and the
record removed ends it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* ci: nightly-next recovery initializes the notices submodule before continuing accepted submissions

The recovery job's checkout had no ghostty-next submodule, so the bundle
notice check that notarize-nightly-dmg.sh runs after Apple accepts failed,
and run 38669958906 skipped five accepted builds as 'Apple did not accept'.
Initialize the submodule like the signing job does, and make the skip notice
say that a post-acceptance check can also be the cause.

* ci: the cmux NIGHTLY-identity job also initializes the notices submodule

Run 38669958906's publish-nightly-identity-legacy job saw Apple accept,
then failed the bundle notice check for lack of ghostty-next and retired
the accepted submission of run 38609024986.

* Channels view: gallery plays for a message link with no link read (red)

A Cloud conversation has no link resolver (G4), so the Messages view's switch hand-off
#/conversation/<id>/message/<id> showed an error. Two plays: a thread reply and a timeline
message, opened through the store's openLink with an owner that has no resolver. cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: open a known conversation's message link without the owner's link read

When conversation-link-resolve fails (a Cloud conversation, an older daemon) and the page knows
the conversation, it opens it, pages the timeline until the message shows, opens its thread when
it is a reply, and marks it. The Messages view's switch hands off Cloud selections this way.
cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: format parseConversationLink (vp fmt)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Channels view: link fallback review follow-ups

Ids in a known conversation's link may hold '-' (the gallery's sample ids); the scheme check
follows the daemon (cmux[-<tag>], any case); the fallback runs only for a Cloud conversation or
an owner without a link read, so a refused local link (a deleted message) still shows its error.
cx-hnmbt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* acpmux: host_lifetime idle test cleans up from the start and never signals a reused pid (cx-7z8yx review)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui: move the build identity into a leaf crate so an edit no longer recompiles cmux-remote

cmux-remote's build script hashed the dirty source tree into
CMUX_TUI_BUILD_IDENTITY, so every edit in the workspace reran it and
recompiled cmux-remote and every crate above it (about 8 s of a 12.6 s
one-line edit loop on rbx). The same build script now lives in
cmux-build-identity, which only cmux-tui reads; cmux-remote takes the
identity as an argument (SshBootstrapConfig::defaults,
pinned_linux_payload). The identity value, its inputs and format, and
every skew and SSH re-bootstrap comparison are unchanged.

* test: warm the dev server in the Claude reasoning default browser test (#20884)

#20592 landed after #20873 without warmViteServer, so check:browser-lanes is
red on base again.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: frame-strip report as an NSWindow method, not a namespace enum (no bead) (#20887)

The package conventions lint fails every PR on the new all-static public
enum WindowFrameStrip (cx-me3xd). The report reads its window, so it is an
extension on NSWindow; same body, same report.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Home channels: the run card is an inline disclosure (no frame, steps on an indented rail)

The live and done run cards under a Chief's reply lose their box: "Working
for" / "Worked for", the step count, cost and Open sit inline under the
message, and the opened steps hang on a thin rail under the status mark. The
narrow rules stay: the first line never wraps, and below 480 px of card width
the step count and cost move under it with no leading separator.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Home channels: the run rail and columns follow the author's picture

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Home channels: the run sheet keeps its error line; run card columns in logical properties

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Revert "cmux-motion: Chrome tab strip tween ..." (8a3ee62) until the app-ffi release App 403 is fixed (run 38682698310) (hq-1d, chief go 2026-10-15, no bead)

The release App could not publish cmux-app-ffi-854febb26527 (HTTP 403 on
POST /releases), so the app FFI pin check stays red on the tip. The tween
re-lands with the publish and repin after the App is fixed.

* test(ci): nightly jobs that run the license check must init ghostty-next (#20893)

* test(ci): nightly jobs that run the license check must init ghostty-next

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ci): nightly recovery initializes ghostty-next before the license check

Apple accepted, stapled and launch-tested each recovered nightly-next build,
then verify-app-bundle-licenses.sh failed on the missing
ghostty-next/build.zig.zon and the build was skipped as not accepted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: group header count yields to trailing controls; review test fixes

Drop the collapsed count when a narrow sidebar has no room for it, measure the
ungrouped column the way the header's is, expect on-color inks in the palette
test, and mark ChipInks nonisolated like its neighbours.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
Co-authored-by: safe-push <safe-push@cmux.local>
teamleaderleo added a commit that referenced this pull request Oct 20, 2026
…every frame, jumps land, 250 ms card) (#22228)

* test: prompt bookmarks persist in the acpmux session (red)

Behavior tests for _acpmux/bookmark_get and _acpmux/bookmark_set before
they exist: restart, prompt-only seqs, fork, transfer, peer forwarding,
the pane relay's scope and params, and the minimap popover's store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: prompt bookmarks persist in the session

_acpmux/bookmark_get and _acpmux/bookmark_set keep the thread minimap's
marks in SessionMeta.bookmarks: seqs of the chat's user_message records,
checked against its log and capped at 1000. A change logs a mux
bookmarks record, as tags do, so attached panes hear it; the chat keeps
its place in the session list. A fork carries the marks of the prompts
it copies under its own seqs; a move carries them (the log keeps its
seqs) and the source keeps none. Peers' chats forward to their owner.

The pane relay allows both methods for a chat in the pane's scope with
only sessionId, seq and bookmarked. The minimap's store reads them from
acpmux, toggles optimistically with rollback, and imports this pane's
old localStorage marks for the chat once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: thread minimap matches the Codex rail; bookmarks show and jump (red)

Behavior tests for the rail's thresholds, hover magnification, card timing,
keyboard focus, scrub, the bookmark dot and row button, and the bookmark
steps, plus a WebKit motion test and the Swift action/page-event test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: thread minimap matches the Codex rail; bookmarks show and jump

The rail now follows the Codex desktop app's measured behavior: it shows
from 4 prompts with 48 px left of the column, 16 px in and centered, with
10 px x 36 px rows and 6 px ticks that scroll past 64 rows or 70% of the
window. Hover magnifies the rows (26/20/14/10 px) in 160 ms on Codex's
spring, the preview card opens after 250 ms (at once within 300 ms of a
close, or on keyboard focus) and closes 100 ms after leaving. A click
scrolls on cubic-bezier(0.35, 0.05, 0, 1) (~400 ms for ~650 px), landing
the bubble 35 px below the top, and highlights it for 1400 ms; an
unmounted target jumps. Dragging scrubs. Every row is a tab stop.

Bookmarks show as a 2 px dot past the tick (a click on its spot toggles
it), in the card, and in a new button on the prompt's own row. Previous
and Next Bookmarked Prompt are palette actions with no default key; the
turn steps keep Ctrl-Opt-Up/Down, now on Codex's 200 ms scroll curve.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: regenerate action exports for the bookmarked-prompt steps

Generated by scripts/measure/export-action-surfaces.sh on the fleet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: concurrent bookmark sets log the kept marks; legacy marks wait for acpmux (red)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* acpmux: log bookmark changes in order; keep legacy marks until acpmux holds them

set_bookmark changes, logs and saves the marks under the chat's move
lock, so concurrent sets log in the order they change the marks and a
move begins before or after a change. The prompt check scans the log on
a blocking thread. The pane forgets an old localStorage mark only once
bookmark_set succeeds for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: a clicked minimap row takes focus so Tab goes on from the rail

WebKit does not focus a clicked button, so in the app Tab after a click
left the rail for the transcript. Codex's rail (Chromium) focuses it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: focus a clicked minimap row so Tab goes on from the rail

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: read the minimap hover transition by seeking it, not by frame samples

CI's headless WebKit paced frames unevenly, so a sample 40 ms into the
transition could come long after it ended.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: regenerate action exports on the merged head

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 568dba62ae7a

- vp fmt webviews/test/ime-composition-keys.test.ts
- cargo fmt in Native/OptChat/optchat-chief

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38566033765

* chore: regenerate action exports after merging #20556's head

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the minimap's card follows the app hover intent (red)

Base's hover-intent and elevation tests now drive the rewritten rail
(.acpmux-minimap__card, the rows' pointerenter/pointerleave), and the
rail test pins the app hover intent (450 ms delay, warm 500 ms, gone as
the pointer leaves unless it enters the card) instead of CardTiming's
250/300/100 ms.

heavy --mem 3G bun test test/hover-intent-cards.test.tsx test/subtle-elevation.test.ts test/thread-minimap-rail.test.tsx
(fail) the card follows the app's hover intent: it waits the hover delay, follows the pointer at once while warm, and goes as the pointer leaves
  thread-minimap-rail.test.tsx:210 expect(card()).toBeNull() after 400 ms: the card opened at 250 ms
(fail) the minimap's turn preview waits the hover delay, then follows the pointer at once until the rail cools
  hover-intent-cards.test.tsx:117 expect(visible()).toBe(false) right after leaving: CardTiming keeps it 100 ms
(fail) one subtle elevation scale > .lg-sheet casts --elevation-2 (base red, ledger.css untouched)
The .acpmux-minimap__card casts --elevation-2 case passes (the merge already moved the card to it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: the minimap's card follows the app hover intent

The preview card now runs on ui/hoverIntent's useHoverIntent, as base's
minimap did: the first waits the hover delay, the card then follows the
pointer from row to row at once while the rail is warm, and it goes as
the pointer leaves the rail (moving into the card, or back, keeps it).
Keyboard focus still shows it at once; Escape dismisses it and focus-out
closes it on that frame. cardTiming.ts and the CARD_* constants are gone.

thread-bookmarks.test.tsx goes back to the PR's version: the merge took
base's half, which drives the old rail's pointermove on two prompts.

heavy --mem 3G bun test test/hover-intent-cards.test.tsx test/subtle-elevation.test.ts test/thread-minimap-rail.test.tsx
  46 pass, 1 fail (.lg-sheet casts --elevation-2: base red, ledger.css untouched)
heavy --mem 3G bun test test/thread-minimap-current.test.tsx test/thread-bookmarks.test.tsx test/thread-turn-keys.test.tsx test/hover-intent-parity.test.ts test/hover-row-sweep.test.ts test/thread-minimap-motion.test.ts
  7 pass, 0 fail (browser lanes skipped locally)
heavy --mem 4G bun run check: pass

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: regenerate action exports on the merged head

export-action-surfaces.sh on 6cf54078 (fleet step a2d9f3a7): 961 actions (base's
chat-identifier actions plus this PR's transcript actions).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: regenerate action exports on the merged head

export-action-surfaces.sh on eec94827 (fleet step fa811efc): 963 actions (base's
Music actions added).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* debug: a short seed of one-line turns for the thread minimap's density captures

debug.agent_pane seed_rows fixture "short": one-line prompts answered
"ok N", the same content as the Codex reference chat, so tick and highlight
density compare like for like.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: regenerate action exports after merging feat-cmux-next

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: warm Vite before the minimap motion and Claude reasoning browser tests

check-browser-test-lanes (#20733) requires warmViteServer after listen;
claude-reasoning-default (#20592) landed without it and fails the base check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 0f6b92945b0c

- regenerated plans/cmux-next/action-surfaces.json
- regenerated plans/cmux-next/actions.md

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38683433717

* fix: a prompt's bookmark shows with the hover actions experiment at one

The hover-actions experiment hides a prompt's action row unless it has Copy,
versions or Edit; a prompt that can carry a mark keeps the row for its
bookmark.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at a97cff790a52

- regenerated plans/cmux-next/action-surfaces.json
- regenerated plans/cmux-next/actions.md

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38694299706

* fix: a drag's click on the minimap stays swallowed until the next press

The flag cleared on a 0 ms timer, which a slow frame could run between the
pointerup and its click, so the drag's click jumped (CI web shard 2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: regenerate action exports after merging feat-cmux-next

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the thread minimap's feel in a long chat, in Chromium (red)

Jump landing for undrawn rows, the hover card's 250 ms, the band following the
scroll on its own frame, and older turns loading without moving the band.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: the thread minimap lands, previews, tracks and holds like the Codex rail

- A jump corrects its landing from the bubble while the rows it crossed measure.
- The card opens 250 ms after the pointer reaches the rail (Codex's delayOpen).
- The band of lit rows follows the scroll on the scroll's own frame, and counts
  the turns on screen as Codex's observer does (no turn below the view).
- Older turns loading above the reader leave the band where it was on screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: settle skips a scroller with no layout; the rail's card tests at 250 ms; gallery rest, scrub and long-chat states

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style: format

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: a jump settles only on a bubble with a box to measure

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: review findings on the minimap feel

- Older turns: the band reads the offset as the transcript will restore it, the
  keep-in-view scroll skips that commit, the hovered, focused, card and scrub
  rows follow their prompts, and a second page continues a running glide.
- The band's first turn is Codex's: a prompt within 35 + 16 px of the top ends
  the turn above it, so a prompt just jumped to is the first lit.
- A jump's landing correction ends on any input in the page; a pending turn step
  is tracked by its turn, not its offset.
- The scroll listener runs in capture; the gallery's synthetic scrub cannot throw.
- Tests: a scrolling rail taking 30 older turns, waits on frames instead of
  timers, a looser card window and one stray frame allowed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: the band reads the restored offset until the transcript's restoring scroll arrives

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at b030bd39f09d

- regenerated plans/cmux-next/actions.md
- regenerated cmux-tui/crates/cmux-app-host/generated/scopes.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39347840624

* fix: the band reads the transcript's placement; jumps end with their chat; the card closes on focus out and Escape; the rail fits a short pane

- VirtualTranscript calls the rail once its placement pass has set the offset,
  so the band reads the restored place directly instead of guessing from a
  frame with no scroll event.
- A running jump stops when the chat changes or the rail unmounts.
- The card closes when focus leaves rail and card, and on Escape inside it.
- The rail's height is capped by the transcript's own height.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: a press released off the minimap ends there; a chat switch stops a jump in its own commit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: the minimap card's Escape goes through ui/escapeDismiss, not a raw key handler (ui rules)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at a73c00f9cf02

- plans/cmux-next/action-surfaces.json
- plans/cmux-next/actions.md

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39360464009

* chore(next): PR autofix at 3c3d0e0b6bcf

- Packages/macOS/CmuxNext/ci-target-graph.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39364182964

* chore(next): PR autofix at 9d214de119ff

- plans/cmux-next/action-surfaces.json
- plans/cmux-next/actions.md

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39380161552

* chore(next): PR autofix at 6e9a03929633

- plans/cmux-next/action-surfaces.json
- plans/cmux-next/actions.md
- schemas/defaults/cmux-next.json
- web/data/cmux-shortcuts.generated.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39386397629

* agents: fence native updates to the captured machine (#21389)

* agents: expose machine platform capabilities

* agents: document machine capability metadata

* agents: model machine platform capabilities

* agents: format machine platform metadata

* agents: show machine platform metadata

* cloud: preserve machine capability contract

* test: cover cloud machine capabilities

* cloud: expose capability operation checks

* test: cover capability operation helpers

* cloud: gate actions on provider capabilities

* cloud: clarify capability refusal

* cloud: keep capability gates within app size budget

* agents: show machine capability summaries

* agents: show machine capability summaries

* test: cover agent capability summaries

* cloud-server: type capability checks

* cloud-server: centralize access gates

* cloud-server: centralize access gates

* cloud-server: carry machine capability metadata

* docs: specify machine capability metadata

* agents: exercise machine capability metadata

* testdata: add machine capability fixtures

* testdata: add machine capability fixtures

* testdata: add machine capability fixtures

* testdata: add machine capability fixtures

* testdata: add machine capability fixtures

* testdata: add machine capability fixtures

* testdata: add machine capability fixtures

* testdata: add machine capability fixtures

* agents: sanitize machine metadata at ingress

* agents: sanitize machine metadata at ingress

* test: cover agent metadata sanitization

* protocol: carry machine platform capabilities

* protocol: carry machine platform capabilities

* fix: restore provider runtime after upload truncation

* style: format machine capability protocol

* style: format provider runtime capability fields

* style: fix provider snapshot indentation

* protocol: retain provider metadata in machine catalog

* protocol: map provider metadata into machine catalog

* tests: update machine descriptors for provider metadata

* tests: update machine descriptors for provider metadata

* tests: update machine descriptors for provider metadata

* tests: update machine descriptors for provider metadata

* tests: update machine descriptors for provider metadata

* tests: update machine descriptors for provider metadata

* tests: update machine descriptors for provider metadata

* tests: update machine descriptors for provider metadata

* apps: reserve agent CLI provider family

* test: enforce agent CLI provider ownership

* agents: add verified read-only agent_cli owner

* agents: cover read-only agent_cli owner projection

* agents: register the read-only agent_cli owner

* agents: defer App provider adapter until feature ownership extraction

* agents: defer App provider adapter until feature ownership extraction

* agents: keep App provider wiring within its module size boundary

* agents: reproduce malformed machine capability crashes

* agents: validate capability wire types before rendering

* agents: specify cached harness inventory privacy and no-execution behavior

* agents: expose cached harness inventory without health side effects

Select only non-secret health fields and reject controls or machine selectors.
Advertise the view in initialize so older daemons cannot silently execute a
normal health request when a client expects a cache-only read.

Cmux-Daemon-Tests: run

* agents: retain prerelease and build metadata in cached inventory

Cmux-Daemon-Tests: run

* agents: negotiate cached inventory before sending health requests

Keep initialize and the inventory request on one transport. Unsupported
owners receive no health operation, and typed inventory decoding rejects
legacy or noncached responses. Preserve pipelining for existing calls and
enforce the documented frame limit before parsing complete replies.

* machines: import platform type in provider runtime

The hosted same-tree daemon build caught the missing import used by the machine catalog projection.

Cmux-Daemon-Tests: run

* test: specify trusted machine inventory routing

Exercise same-owner feature negotiation, cache-only responses, missing owners and refusal of machine selectors, controls and untrusted clients before adding the daemon command.

Cmux-Daemon-Tests: run

* machines: read cached agent inventory through the native owner

Add the agent-inventory-v1 daemon verb on trusted Unix connections. Negotiate harnessInventory on the exact owner socket before the cache-only health request, reject controls and machine selectors, bound per-daemon concurrency and serialize only non-secret DTO fields. Keep the conversations-only relay denied.

Cmux-Daemon-Tests: run

* test: unwrap successful provider snapshots before checking defaults

The hosted workspace test build caught direct accesses to a nonexistent ResponseEnvelope.result field in the legacy-metadata regression. Match its ProviderResponse::Success payload instead. Also use the already-imported AtomicUsize in the inventory worker bound.

Cmux-Daemon-Tests: run

* agents: share the inventory wire model with native machine requests

Move the cached observation DTO to the lower daemon protocol module, add the capability-gated AgentInventoryRequest and reuse that DTO in the direct acpmux reader. AgentPane now depends on Daemon, whose dependencies remain Compat and Wakeups; no cycle or app-target growth.

Cmux-Daemon-Tests: run

* chore(next): PR autofix at 48a59aac18e3

- regenerated Packages/macOS/CmuxNext/ci-target-graph.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39024598894

* test: repair machine metadata fixture construction

The hosted workspace test build caught duplicate platform/capabilities assignments in MachineDescriptor fixtures and assignments intended for MachineDescriptor on ManagedMachineDescriptor. Remove the duplicate and wrongly typed pairs while preserving each descriptor's actual capabilities.

Cmux-Daemon-Tests: run

* test: reproduce false absence claims from partial agent inventory

Cover partial caches, launcher uncertainty, presence-only auth evidence, watch removals and all three rendered layouts using FakeHost. Before the fix the focused suite executes 16 tests: 15 fail and the complete-inventory control passes.

Command: bun test first-party-apps/agents/test/inventory.test.ts

* agents: preserve unknown evidence in cached machine inventories

Only an explicit complete:true inventory can establish missing CLIs. Preserve nullable installation and presence-only auth evidence, keep partial removals unknown, and show uncertain rows/cells without install or update actions in every layout. Empty account observations no longer imply signed out. Add translated uncertainty copy and regenerate synthetic preview fixtures and the app bundle.

Focused regression: 15 failures and 1 control pass at 9863b7dc; all 16 pass after the fix. Full Agents suite: 27 passed, 297 assertions. Current Swift inventory suites also passed all 10 tests at 48a59aac.

The repository-wide localization audit still reports errors in unchanged macOS catalogs; the Agents English/Japanese source-key and placeholder checks pass.

* test: preserve inventory assertions during owner fixture cleanup

Do not set a read timeout on the immediately closed cleanup wake socket (macOS returns EINVAL), and preserve the original assertion if the worker fails during panic cleanup. Real owner calls are already bounded and close their link; the fixture reads their EOF.

Cmux-Daemon-Tests: run

* apps: move injected capability handlers into their feature owner

Move the request, error, handler protocol and family router from the executable App target to CmuxNextApps, preserving dispatch and refusal behavior. The App keeps its concrete handlers and channel wiring. Add a focused Apps test target, regenerate the target graph and import the feature at the remaining PageFactory use.

The extraction removes 61 App-target lines and gives the agent inventory owner an injectable feature-level handler seam. Local Swift syntax, namespace, concurrency, graph freshness, App size and diff checks pass; hosted compilation and the new behavioral suite are pending.

* docs: scope duplicate handler ownership to the in-process router

Clarify the existing local dictionary replacement behavior without implying that a live provider registration may be taken over.

* test: reproduce missing canonical machine identity in identify

* machines: expose canonical owner identity in trusted daemon handshakes

* sdk: preserve optional machine owner IDs in raw identify results

* test: reproduce inventory refusal without a selected-owner fence

* agents: fence native inventory reads to the selected machine owner

* test: require inventory family evidence from cached executable and package

* agents: project CLI family from the cached executable and package evidence

* test: require an admitted read grant for agent CLI inventory

* agents: serve scoped CLI inventory through admitted machine owner routes

* test: use synthetic host labels in machine inventory fixtures

* apps: give provider registration its own connection adapter

Preserve captured-transport capability admission and legacy-family fallback
without growing the already oversized DaemonConnection type. Update the
wire-level registration test and the request documentation.

* test(agents): reproduce wrong-platform install hints

Exercise structured Linux metadata and unknown remote owners through the
packed app. Keep legacy OS and recognized-platform controls.

* fix(agents): choose install hints from the selected owner platform

Prefer structured machine metadata, retain legacy OS compatibility, and
keep unknown or unsupported systems unknown instead of offering Mac install
commands. Record the current inventory owner/provider integration in the
shared coordination lane.

Regression: platform.test.ts goes from 3 behavioral failures and 2 controls
to 5 passes. Full Agents suite: 32 passed, 340 assertions.

* test(machines): define verified remote-catalog publication behavior

Exercise the existing machine.list/get API, exact owner fencing, bounded
metadata, ordered replacement, exclusive publication and connection expiry.
Keep the local-only default as a control.

* test(machines): use the resource module's error type

Repair the fixture import before evaluating the behavioral catalog regression.

* machines: project verified remote routes through the native catalog

Accept bounded, sequenced publications on the verified local app's existing
connection, fenced to this daemon's public ID. Keep the publication in that
connection record so disconnect removes it atomically. Expose metadata through
machine.list/get while retaining the native local identity and session owner.
Reject competing publishers, local impersonation, stale replacement, unknown
fields, commands, paths and nonlocal/agent-bound carriers.

The app publisher is not wired yet. No new route, remote execution authority,
owner reconnect, disk schema or credential delegation is introduced.

* test(machines): cover SDK catalog decoding and retain outer disconnect denial

Correct the expired native client's expected outer remote_denied refusal.
Add a behavioral SDK regression for native platform/capability metadata and
remote origins, preserving legacy records and strict malformed-field checks.

* sdk: carry machine origins and owner capability metadata across resource clients

Extend the authoritative MachineSnapshot contract and all seven ergonomic
SDKs to preserve optional platform/capability metadata and Cloud, SSH and
server origins. Keep legacy omission defaults and strict typed metadata.
Extract the Rust and Zig machine models instead of growing oversized files;
Zig retains a bounded capability table without allocation.

Regenerate the app declaration and canonical SDK descriptors. Update the
boundary guard to validate the exact remote metadata shape and add rejection
coverage for command fields, arbitrary origins and an open platform schema.
Document the native publication bridge; its app publisher remains unwired.

Behavioral Python SDK regression: four assertion failures at 1552187f, then
all three tests pass. Existing Python resource suite: 69 passed. Focused TS:
three passed. Resource boundary guard suite: 34 passed. Native catalog tests:
eight passed at 1552187f (f5d2957368a6c4fbd973f19d). Compiled SDK lanes pending.

* test(java): preserve the SDK test runner executable bit

* test(machines): require native owner metadata in the handshake

The app must publish the owner's observed platform and machine capabilities,
not infer them from a UI route, provider label or its own local system.

* machines: publish admitted owner metadata from the app registry

Observe existing native registry routes on the captured local connection. Fence snapshots to the owner and handshake epoch, preserve live lease sequences across temporary source loss, withdraw removed routes and discard stale acknowledgements. Bound catalog records and frames, and negotiate publication without a legacy fallback.

Expose native platform and machine capabilities in trusted identify using the same snapshot helpers; regenerate all seven raw SDKs. Add deterministic publisher lifecycle and wire-contract coverage.

* machines: verify the publication transport and avoid catalog panics

Exercise actual connection negotiation, legacy refusal before write and owner mismatch propagation. Repair test-fixture compilation and remove three fallible expect sites from catalog projection and selection. Document event-driven publication and native owner metadata.

* test(machines): expose policy readmission observation regression

Extract the current registry projection unchanged from app composition and exercise it against a paired-server fixture. A blocked route must keep observing the policy that can readmit it. No host or connection is started by the fixture.

* machines: keep blocked catalog routes observing readmission

Read observable registry admission and carrier state before excluding blocked routes. This preserves the subscriptions needed when policy or a reconnect readmits the existing route, without opening a connection. Cover both policy and carrier invalidation.

* test(machines): cover curated CLI and MCP catalog discovery

Exercise read-only machine discovery, explicit metadata lookup and continued exclusion of session effects and arbitrary machine routing. Extract existing catalog tests instead of growing the large MCP test file.

* test(machines): preserve CLI paths after catalog test extraction

* machines: expose read-only catalog discovery to CLI and MCP

Offer curated machine list/show and generate machine_list/machine_get from the existing operation catalog. Keep session effects and other-tool machine routing excluded. Share operation-aware route defaults between CLI and MCP so the catalog socket's session is not serialized as metadata, and resolve machine prefixes on the same connection with no ancestor fields.

Extract the machine grammar instead of growing command.rs. Document the metadata-only contract and translate root discovery help in both native CLI locales. Add actual transport and route regression coverage.

* machines: clarify catalog tool authority and repair fixture lints

* test(machines): use an explicit name selector for reserved words

* test: reproduce Swift catalog ancestor selector mismatch

* fix: encode Swift catalog selectors for their operation

* test: exercise catalog trust with real native owner connections

* machines: exercise app discovery and scoped inventory through real server owners

* test: use the catalog's read risk in the app journey fixture

* test: retain owned app diagnostics for native journey failures

* test: preserve new app runs across replacement host exit

* fix: fence queued app runs from the outgoing host generation

* style: terminate app dispatch match arm consistently

* docs: distinguish machine catalog metadata from session authority

* test: require negotiated metadata for strict provider v1 clients

* machines: negotiate optional provider metadata per control generation

* machines: reproduce strict native reader catalog incompatibility

* machines: require reader opt-in for extended native catalogs

* chore(next): PR autofix at 1938babb0ef6

- regenerated cmux-tui/crates/cmux-app-host/generated/cmux-app.d.ts

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39046739528

* tests: preserve native field validation in MCP catalog plans

* sdk: expose machine reader options and reproduce missing wire opt-in

* tests: format the SDK catalog wire regression for hosted Zig

* sdk: serialize explicit machine catalog reads without changing defaults

* tests: cover the extracted machine facade and typed invalid inputs

* tests: reproduce strict identify reader compatibility and opt-in boundaries

* tests: exercise identify through its native command handler

* machines: negotiate owner identity metadata without changing legacy identify

* tests: preserve identify callers and validate explicit metadata options

* cloud: localize unavailable operation refusals

* tests: capture bounded catalog withdrawal receipts on journey failures

* tests: await the acknowledged withdrawn catalog instead of a fixed sequence

* test(agents): reproduce stale and cross-owner inventory refreshes

* test(agents): require explicit catalog discovery and local fallback controls

* fix(agents): discover admitted owners and fence inventory refreshes

* test(agents): reproduce missing cache-only inventory change feed

* feat(agents): publish bounded cache-only inventory changes

* test(agents): require owner-fenced native inventory watch leases

* feat(agents): fence native cache watches to owner connection leases

* test: reproduce inventory watch replacement cleanup race

Correct the native watch fixture's writer construction and qualification warnings so the barrier-controlled socket replacement case can execute before changing cleanup behavior.

* fix: fence inventory watches across socket replacement

Detach old leases while changing the configured socket and close only that detached set outside the lock. Capture a monotonic internal socket epoch in every watch so a reused path cannot admit stale work. Preserve the native wire contract and the pre-fix barrier regression.

* fix: document native inventory streams and guard handshake replies

Declare the targeted watch events as control streams and document their payload/lifecycle. Keep moved agent-session emitters in their existing internal wire family. Replace the computed metadata reply subscript with a bounded lookup that fails malformed responses closed.

Spec inventory and all32 checker tests pass after the previously observed missing-stream failure. Swift syntax, scoped conventions/size and crash ratchet pass; no crash baseline changed. Native watch Rust inputs are unchanged from the 25-case fleet pass at01418d27; final job cleanup remains pending.

* test: preserve inventory lease events through tree resyncs

Exercise native event decoding, DaemonStore side-event delivery behind a snapshot barrier, and EventInbox overflow. Inventory changed/ended events belong to the separate subscription; keep an unrelated unknown-event control. Regression only, before event-retention repair; fleet execution pending.

* fix: retain native inventory lease events behind tree snapshots

Classify the two targeted inventory lease notices as independent of workspace snapshots. Keep unrelated unknown events behind the existing tree barrier. EventInbox already retains unknown events; the new overflow cases preserve that control behavior.

Regression be913d2e precedes this fix. Syntax, scoped conventions and size checks pass; exact fleet red/green execution remains pending the matching native artifact. No app-facing watch advertisement or new authority.

* test: preserve exact native inventory watch revisions

Exercise actual DaemonEvent wire decoding across UInt64 boundary values and malformed owner/lease/revision inputs. Require bounded lease-only side-event payloads and retain an unrelated numeric-event control. Regression only; no runtime RED claim before fleet execution.

* feat: type native inventory watch requests and exact revisions

Add separately capability-gated owner-fenced enable/disable requests and a directly decoded UInt64 receipt. Normalize targeted side events through validated owner/lease fields, preserving revisions as decimal text before generic JSON can round them and discarding diagnostics or malformed notices. Keep unrelated events unchanged.

Revision regression8ddb7918 precedes this change. Add request framing and bounded real-connection bridge coverage for legacy refusal and exact UInt64 replies. Syntax, scoped conventions/size and crash ratchet pass; packet0 localization entries. Fleet runtime red/green remains pending matching artifact publication. App-facing subscriptions are not yet wired or advertised.

* test: fence delayed inventory disable to its original lease

Enable a new lease on the same native control connection after socket replacement, then deliver cleanup naming the retired lease. Assert idempotent no-op for stale cleanup, continued invalidations on the new lease, and successful conditional retirement of its exact ID. Regression only, before owner-side conditional disable.

* fix: condition inventory cleanup on its captured lease

Advertise agent-inventory-watch-fence-v1 and accept a canonical expected_watch_id only on disable. Atomically compare/remove only that client's matching lease; a stale ID or absent lease is an idempotent no-op, never a selector for another client. Keep the original explicit connection-wide disable.

Managed Swift enable/disable requests require fencing support before starting and include the exact receipt ID on cleanup, with no unfenced fallback. Add cross-client, malformed-input and capability coverage. Regressionedd780 executes the missing conditional-disable assertion in the selected run and alone (jobdb37d30741c3eecdeda6d36e, failed100, no leaks). Spec inventory/32checker tests, scoped syntax/conventions/size and diff checks pass; runtime GREEN pending.

* test: use the daemon JSON object case in the watch fixture

Correct the connection fixture's assumed AppJSON accessor to pattern-match CmuxNextDaemon.JSONValue.object. Current lease-fenced wire assertions remain intact. The earlier904577 candidate stopped at this fixture compile error; it provided no runtime GREEN. Production sources and native inputs are unchanged.

* test: consume admitted agent inventory invalidations

Exercise the real Agents store in FakeHost: agent_cli.changed must re-discover/re-read admitted owners, ignore claimed event payload rows/selectors, and coalesce bursts while retaining a follow-up. The same focused Bun command executes13controls passed and3expected failures before the fix. Keep this regression before the implementation; advance the public branch with both after local GREEN.

* fix: re-read admitted agent inventory on cache invalidations

Subscribe to agent_cli.changed and ignore event payload rows, selectors, commands and job results. Re-discover/re-read current owners without live checks; coalesce an active batch and preserve a follow-up. Remove the unsupported watch payload listener and its proposed stream operation. Preserve stale-read, withdrawal and local-fallback fences across all three views.

Regression154e9306 precedes this fix. The same focused Bun command first executes13pass/3expected failures, then passes all16original cases plus2refusal/removal cases. Full Agents suite50tests/416assertions, canonical bundle/check and manifest pass; existing execute-scope warning remains. Localization packet0entries/attention; global static baseline failures unchanged. Publish both commits together after local GREEN. App-facing native/provider invalidation production remains open; Agents is unbundled.

* machines: bind inventory watch lifecycle to the ready control session

Capture one manager per admitted native control transport, retain pending
receipts for cancellation cleanup, and serialize conditional retirement
before readmission. Failed cleanup cannot silently admit another lease.

Add bounded bridge coverage for cancellation, owner refusal and reconnect.
Extract handshake admission to keep DaemonConnection within its size ratchet.

Runtime validation follows on the immutable hosted source; no end-to-end
provider watch or Cloud journey is claimed.

* test: reject cached inventory watch admission after control reconnect

An old manager must not return its cached receipt after the original
transport closes, even when the same canonical owner reconnects.
Keep the new transport's live lease as the cleanup isolation control.

* fix: refuse retired inventory watch transports before returning receipts

Check the captured transport before cached admission and after pending
admission. Recheck retirement when another caller starts a cleanup while
an earlier cleanup await resumes.

The preceding reconnect regression executes one expected cached-receipt
failure with four passing controls on fleet job7c5042eb1d069c387c351d77.
No reconnect or alternate-owner cleanup fallback is introduced.

* machines: exercise watch manager admission refusal before native contact

The new manager refuses an owner with only the older watch capability
and an owner without canonical identity. Real bridge inspection confirms
no watch operation is sent in either case.

Production watch lifecycle at parent7c58329a passes all seven selected
Swift suites, fleet job1d9ad29f7b992e9612c26052, including the preceding
closed-transport regression.

* test: revalidate app stream authority before each queued delivery

Use the scripted host and an explicit queue barrier to hold the current
process alive across grant, enabled, host-state and manifest changes.
A saved subscription must not keep receiving events after those changes.
The readable running host remains the positive delivery control.

* apps: fence inventory invalidations to current readable demand

Recheck install, manifest, grant and host state under the supervisor lock
before queuing stream events. Keep fixed inventory invalidation out of the
generic daemon event feed.

Add opt-in inventory demand snapshots, targeted monotonic demand events,
and a strict verified non-agent Unix provider ingress taking only the
current demand sequence. Revoke, unsubscribe, host exit and provider
replacement fence stale callbacks without supplying rows or selectors.

The preceding real-host queue-barrier regression executes five expected
authority failures with a passing running-host control, selected and alone,
job b8b8381c06c4dd0c40a75d3a; no leaks. Current runtime validation follows.

* fix: validate inventory invalidations at the apps request entry

Move the strict frame check from cancel-request to try_handle, where the
command discriminator and optional response shape are available.

The prior candidate stopped during native compilation; no runtime result
is attributed to it. Inventory and cancel-request behavioral gates will
run on this corrected immutable source.

* test: preserve the page relay role while proving its inventory refusal

The generic verify helper sets role main. Set only the verified-app test
fact in this fixture so the request actually originates from page_relay.
Production inventory permission and demand behavior is unchanged.

* apps: satisfy demand test lint and document inventory provider opt-in

Add the two required test closure semicolons without suppressing warnings.
Document the additive demand snapshot and fixed provider carrier in the
owning module; legacy registration remains unchanged.

Prior f85cfc62 runtime executes42 passing inventory cases with one invalid
page-relay fixture failure, corrected in parent8859e57c. The queued8859
verification was cancelled before placement after these lint issues were
known; the final exact source is the verification target.

* machines: preserve source identity through side-event delivery

Tag native event envelopes with the connection instance and retain their
original control serial through the store, snapshots and inbox collapse.
A new envelope subscription rejects unsequenced or unstamped delivery;
existing event-only subscriptions remain compatible.

Delegate typed wire execution and ready transport sequence projection to
their resource owners without growing the connection type.

Exact Swift files pass eight suites/28methods on immutable validation
source2a74fd62 using its official matching published native tree eb966;
job a4c6a87c47577e66e78cd436 is done/0/reset. This is component proof only.
Current carrier Rust at parenta11bfff6 passes43inventory and140shared app
cases. Current native/provider composition awaits its matching artifact
and the production demand-to-watch bridge.

* test: retain exact provider inventory demand sequences

* apps: connect permissioned inventory demand to admitted machine watches

* apps: validate inventory subscriptions through the native app journey

* apps: fence provider callbacks and verify reconnect and lease retirement

* test: reject stale registry admission after actor reconnect

* apps: require the registry observed control transport for inventory admission

* daemon: expose observable side event control admission

* test: name disconnected control reason in stamp lifecycle fixture

* test: recover explicit no-lease inventory admission refusals

* machines: prove absent inventory leases before explicit admission retry

* sdk: model native owner inventory events across generated wire layers

* test(agents): reproduce duplicate and stale selected-owner job admission

* fix(agents): bind job actions to one admitted pending request

* test(core): pin agent update admission to the captured machine

* feat(core): fence agent update calls to their captured machine

---------

Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Use paired server identities for links (#21644)

* test(server): expose missing pairing commands

* test(server): reject missing idempotency values before mutation

* Implement human-approved server enrollment in Rust

* test(server): keep pairing links in the selected API environment

* Keep pairing verification links on the selected API and fix isolated fixtures

* Keep server enrollment coordination in its lane ledger

* test(link): bind paired servers to their enrolled WireGuard identity

* Use the enrolled server identity for an explicitly selected link

* Dereference the protected key when checking a running link identity

* Share link help and exercise enrolled traffic through real cmux processes

* Report the exact encrypted-journey deadline and owned process logs

* test(link): exit a refused encrypted dial while stdin remains open

* Finish link dial after peer EOF with stdin still open

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* docs(web): point Huge Blue inspection comment at the vms README (#21952)

docs/design/huge-blue-app-server-inspection.md only existed in the
unmerged #21335; the design lives in web/services/vms/README.md.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* test(agent cursor): red: the name tag is a gray Liquid Glass capsule (pill gallery P1)

User (Oct 20): 'more designs for tinted pill ... gray and liquid glassy, think
about how Apple would design this'. Replaces the light-glass tag expectations:
neutral gray translucent tint, continuous corners, light specular rim and top
highlight, subtle shadow, dark text, gray dot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next agent cursor: the name tag is a gray Liquid Glass capsule (pill gallery P1)

Neutral gray translucent tint (#D6D6D8 at 78%), continuous-corner capsule
22 pt tall, light specular rim, top highlight gradient, soft 1.5 pt shadow,
dark label text and a gray dot; the typing caption uses the same material.
No agent color anywhere in the tag. The overlay panel sits above Chromium's
child windows, so it cannot blur the page; the tint stands in for the blur.
No new animation: the tag still shows and hides through the existing Motion
fades, so Reduce Motion and animation speed apply unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(cmux-next tabs): a tab switch at rest walks no row of tabs (red: every switch runs the occlusion and full separator passes)

TabSwitchWorkBudgetTests counts the strip's passes over every cell (TabStripRowPasses: occlusion,
separators, frames) across 2N switches at 2, 10 and 30 tabs and expects none: the switch's cost must
not grow with the tab count. Deterministic, where a timing gate on shared CI hosts is not (the same
bench measured 0.4 ms on one fleet host and 29 ms on another).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next tabs: a tab switch updates the two tabs it changes, not the row (tab-switch-rewrite)

syncSelection walked every cell twice per switch: the full separator pass and the occlusion pass
(TabOcclusion samples each cell's motion path). At rest no tab overlaps another and no mask is on, so
occlusion runs only while a strip move is in flight (movesInFlight, cleared by the move's completion),
and the separator rule is applied to the lines next to the old and new selected tab through a slot
index built once per layout (TabSlotIndex), which reveal also reads. A multi-selection change keeps
the full pass. TabStripSwitchSeparatorTests proves the lines equal a full pass after any switch order.

Benches (fleet, aws-m4pro-5, same host A/B against a9439ce7ac3): TabSwitchPathBench apply p50
60 tabs 0.42 -> 0.31 ms, 30 tabs 0.21 -> 0.15 ms; strip separators 38 us -> 2.5 us at 60 tabs,
constant. TabSwitchPathBench now prints the per-stage split (BenchSpans switch.*), and
TabStripSelectionBench each strip step. scripts/cmux-next/tab-switch-matrix.py also reports each
Chromium and WebKit page's own first frame after a switch (content_frame_ms).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Server: Add Server card: the Name/Team grid takes the card's width

The fleet render at 39b0b0b23b5 still drew the Name field at its ideal width:
the Grid sized to its cells, so maxWidth on a cell had nothing to fill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Server: Add Server card: Name and Team as captioned full-width rows

The Grid kept the Name field at its ideal width (the maxWidth frame sat
outside the drawn card) and wrapped the Chief checkbox. Captions over the
controls need no label column, so long labels in any language fit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: actions.md lists the transcript actions where the generator puts them (AgentTranscriptActionCatalog order)

* test(next): pinned groups read as pinned; a group header's right-click is one flat menu (red)

Lawrence 2026-10-20: Move to Top left a workspace under groups that were
pinned (the Pinned band lists above the machine sections), but nothing
showed they were pinned and Pin/Unpin could only pin. And the header's
right-click opened the editor, whose More Group Actions row held the menu.

Red: SidebarDocPinnedGroupTests (a Pinned-band group maps pinned),
WorkspaceGroupFlatMenuTests (common rows top-level, no catch-all folder,
every row in the palette), GroupPaletteDefaultTests (right-click shows the
menu, editor rows without More). Design: plans/cmux-next/sidebar-ordering-DESIGN.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): pinned groups read as pinned and unpin; a group header's right-click is one flat menu

Root cause (plans/cmux-next/sidebar-ordering-DESIGN.md): the groups Move to
Top left a workspace under were pinned. Pin Group moves a group into the
Pinned band, above every machine section, but SidebarDocSections mapped
every document group with pinned: false, so no pin glyph showed and
Pin/Unpin always pinned. A Pinned-band group now maps pinned (the header's
pin, Unpin Group in the menu and editor), and Pin/Unpin toggles from what
the sidebar shows.

The header's right-click opens the group's menu at once (no editor, no More
Group Actions row): Color, Set/Remove Icon, Pin/Unpin, Collapse All/Expand
All/Collapse Others, Move to Top, Ungroup, Close Group, Delete Group and
Close Other Groups are top-level; Move and Sort keep their lists. The editor
offers Pin and Move to Top itself. New Workspace in Group, Pin/Unpin Group,
Mark Read/Unread, Clear Notifications and Edit Group Config join the palette.
Pin and Collapse rows read as the change they make. Strings in all 21 locales
(19 need review).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): the flat group menu keeps 12 top-level rows

ActionSurfaceParityTests.everyMenuKeepsAShortTopLevel (Lawrence 2026-10-01:
at most 12 top-level rows) failed at 21. The rows Lawrence named stay
top-level (New Workspace in Group, Rename, Color, Pin/Unpin, Collapse/Expand,
Move to Top, Ungroup, Close Group) with Delete Group and Close Other Groups;
Options ▸ takes icon, read state, collapse-all, config and Duplicate, and
Move ▸ takes the group sorts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(next): Copy Group ID joins Options, so the group menu keeps 12 top-level rows

Its one-row Copy folder showed inline as a 13th row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): regenerate action exports for the flat group menu

Regenerated on the fleet (key a219-exports-820ba663b9c,
scripts/measure/export-action-surfaces.sh). check-group-menus.py passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next): TitleCoalescerTests takes mutating results before #expect (the pattern that broke CEFPumpScheduleTests)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
Co-authored-by: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com>

This branch was previously deployed

2 inactive deployments
artifacts — 115d4da7 Deployed Oct 15, 2026 by teamleaderleo via publish tree-addressed cmux-tui #15608
cmux-tui-merge-tree — 115d4da7 Deployed Oct 15, 2026 by teamleaderleo via publish the merge tree #1127
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant