Repository navigation
CmuxNext: one-line sidebar rows by default; notification text in the hover card - #20483
Conversation
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
|
Not verified: macOS jobs did not run: compile and app tests were skipped on CI passes on Written by |
|
Bead |
…ver card Leo 2026-10-14: "why is it even 2 lines in the first place?" Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ext moves to the hover card sidebar.workspaceRow.notification is now off by default (Leo 2026-10-14). #19862 turned it on for classic parity; the setting stays as an opt-in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
a31be47 to
d22bf94
Compare
This comment has been minimized.
This comment has been minimized.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…icons/one-line-rows # Conflicts: # Packages/macOS/CmuxNext/Sources/CmuxNextSidebar/Logic/WorkspaceHoverCardContent.swift
Bugbot needs on-demand usage enabledBugbot 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. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
|
cmux-next tour of 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 |
…icons/one-line-rows
… next) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot needs on-demand usage enabledBugbot 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. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ay where the text shows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…20492) * test(cmux-next): static status glyphs are apart by shape, crisp, still and theme-colored Red: there is no static status set and no ui.status-glyphs contact sheet yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * CmuxNext: static status glyphs for the Static status mode Leo 2026-10-14: six custom static glyphs for op-next-motion's Static status mode (Reduce Motion): a hollow ring (idle), a half-filled ring (working), an empty speech bubble (waiting on you), a shield (needs permission or a sign-in), a check disc (done) and a ! diamond (error). Told apart by shape alone at 10 pt, tinted by their theme role, never animated. One source, schemas/status-glyphs/static-status.json: a 20-unit master (10 pt at 2x) and 10 px pixel art for 1x. gen-status-glyphs.py writes the Swift StaticStatusGlyph (CGPaths) and the web statusGlyphs.generated.ts (SVG paths); --check runs in bun run check. StatusMark.vector draws a glyph, StatusIconSet.still (Static) maps every state to them, and StaticStatusGlyph(state)?.plan is the hook for the Static setting. The gallery sheet ui.status-glyphs shows each live, in a row, and its 1x and 2x pixels, plain and on a wallpaper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cmux-next): status glyphs at 2x probe their straight edges A share-of-soft-pixels cap cannot tell a curve from blur (a ring half a pixel off reads the same), and the idle ring's 63% tripped it. At 2x the test now probes pixels on each glyph's straight edges, which are whole only when the drawing sits on the pixel grid; half a pixel off, every glyph leaves some half covered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * CmuxNext: the Static set keeps a known-progress ring Review: StatusIconSet.still drew the working glyph for an agent that reports progress, dropping how far along it is. That ring never moves, so the Static set keeps it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: Codex-like status set draws grey work, blue done, red error Red: StatusIconSet has no codex case, there is no spinner or unread tint, and the web sheet has no codex glyphs (shieldLine, info, dot). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: Codex-like status icon set A Debug set for the status indicators that follows Codex's sidebar: Lawrence's shared spinner re-tinted to the foreground at 66% (a still ring under Reduce Motion or Static), Codex's blue (#3982F5) done dot, muted line bubble and shield for waiting, and a red line i for error. The schema gains a codex set that the generator emits for the web copy, and the gallery sheet gains codex variants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: Codex-like still loader stays a stopped arc Red: with motion off, the Codex-like set turned every arc into the still work ring, so a busy page or command loader read as agent work. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: Codex-like still ring only for agent work Only working without progress becomes the still ring when nothing animates; a busy loader keeps its stopped arc. The glyph generator now checks that every set picks a glyph for every state from its own set or the static set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: Codex blue as a theme token; document the idle/info 2x floor Codex's blue moves to --cmux-status-unread in webviews/src/theme, so the generated glyph table holds no blue literal (check-system-blue). At 2x the still work ring and the error ring differ only by the i, 16 whole pixels, and they differ in color too; the test says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: the Codex-like status icons are the default set Red: the default is still symbols. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: the typeahead GUI gate in the repo (cx-wb5.78): typeahead-gate.sh runs split, newterm, split with a slow host, new-tab and cold new-tab proofs on the GUI host and fails on a lost key; typeahead-compare.py counts keys sent by terminal id; creation-hold-proof.py is superseded (named in the gate header) * feat: Codex-like status icons are the default Leo 2026-10-14, a team decision: the grey spinner (a still ring under Reduce Motion), Codex's blue done dot, muted line glyphs and a red line i for errors, for everyone. Symbols, Static and the other sets stay as Debug alternates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: Codex blue as a theme constant, not a page stylesheet color A literal color in a page stylesheet trips the theme-colors ratchet, so the blue moves to STATUS_UNREAD in webviews/src/theme/statusColors.ts and the generator writes it as the fallback of --cmux-status-unread. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at a8608cf - vp fmt webviews/src/agent-session/acpmux/modelPickerLayout.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38521104749 * test: the status glyph and count badge center on the row's middle, one line or two Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: the status glyph centers on the row's middle beside the badge; a 6 pt done dot The glyph sat on the title's capitals, so on a two-line row it rode above the count badge (Leo 2026-10-14: "why is it superscript?"). It now centers on the line the badge and the other trailing marks share. The Codex-like done dot grows from 4 pt to 6 pt ("too tiny"). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: sidebar rows lead with a logo or kind icon, badge its corner, and draw no count Leo and Lawrence 2026-10-14: no count numbers on NEXT rows; one corner badge on the leading icon (T3 Code style), the most urgent state first, and the dim age on the right. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: NEXT sidebar rows badge their logo's corner instead of drawing a count Leo and Lawrence 2026-10-14 ("maybe we can do unreads like this", T3 Code's thread list): - a row leads with its harness logo or its kind icon; a user's icon still wins; - one badge on that icon's bottom-right corner, on a disc of the row's backdrop so it cuts a thin ring: the most urgent of failed, needs approval, question, working (the grey spinner, a still ring under Reduce Motion), done or unread (the blue dot); - no count number; the dim age of the last change sits on the right, and the count reads in the hover card, the tooltip and the accessibility label. Debug Settings sidebar.rowBadge switches to Leo's stack (up to three overlapping glyphs, one per pending item) or back to the count and trailing glyph. SidebarModel.rowBadgeStyle pins a style per sidebar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: a 10 pt corner badge on the pixel grid; stack items as whole chips Coordinator 2026-10-14 on the #20492 captures: the corner badge's i, shield and bubble were unreadable at 1x, and at 40% overlap the stack's half-hidden dot read as a D. - Every badge glyph is the static glyphs' 10 pt box in a whole-point cell, so their 1x pixel art lands on the grid; a 1.5 pt cutout ring hugs it (6 pt for the unread dot). - Each stack item is a full chip (a tonal disc ringed by the backdrop); a chip tucks under the one before only as far as its padding, so every glyph stays whole. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: pass the badge look when a row draws no badge Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: the row's time never ends in an ellipsis A time today ("1:26 AM") measured a few points short of its text in the compact row's trailing slot and drew "1:26…". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * acpmux: behavior test, a new chat starts in full access; the person's switch back is saved; every chat gets the cmux MCP server (red) (cx-wx93i) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * acpmux: new chats start in the harness's full-access mode; the person can switch back and the pick is saved; every agent chat gets the cmux MCP server (cx-wx93i) Lawrence 2026-10-14: "give every agent chat the cmux MCP tools by default, but by default all chats should be in skip permission mode, that user can switch back". - hub/start_mode.rs: once the harness is up, acpmux sets the start mode with session/set_mode (soft, like the start model): the person's saved pick (config.json defaults.<profile>.mode), else acpmux config.json defaultPermissionMode, else the cmux setting agents.defaultPermissionMode (fullAccess | standard | ask), else full access: Claude Code bypassPermissions, the Codex bridge agent-full-access (approval never + danger-full-access), or a harness's yolo/full-access mode. Not for Web (remote-origin) chats, presets, folder profiles, adopted chats, or a profile that pins its mode in argv. - A mode pick from the person's app pane (session/set_mode or the mode config option) is saved; Plan is a toggle and is not saved; a pick equal to what the setting gives removes the saved mode. Only the person may clear a saved mode (_acpmux/defaults clear), as for a stored policy. - claude_stdio: offers bypassPermissions ("Full access") and launches Claude Code with --allow-dangerously-skip-permissions when it accepts it (not root, or IS_SANDBOX=1); remote chains strip the flag. - agent_tools: the cmux MCP server (cmux mcp serve) is on for every agent chat unless the settings file says mcp.enabled false; the settings file is now the app's cmux-next.json (user_settings.rs), not classic cmux.json. - cmux mcp serve: on when CMUX_AGENT_SESSION is set and mcp.enabled is not set; any other client still needs mcp.enabled true. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(cmux-next): Page Info on the shared native popup host; its rows on the shared popup row (cx-x9o52, REUSE.md slice 3) PageInfoPanel is a NativePopupPanel: placement through PopupPlacement, the host's grow/shrink motion and dismiss rules (click outside with the omnibar button kept open, Escape, key loss, app inactive, window move/resize/close). The hand placement, PopupFade and the mouse-down reopen guard go. The card no longer draws its own shadow band (PopupHostView draws the one popup shadow). PageInfoRowView is a PopupRowView. NativePopupPanel joins the child-window allow list (the bookmark bubble tripped it in DEBUG). PageInfoMotionTests (a unit test of PopupFade on PageInfoPanel) is deleted under the no-unit-test rule; the live check covers the bubble. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(cmux-next): a new bookmark's name follows the page title that arrives after the star (bookmark bubble) The star saves the bookmark at once, often before the page has a title, so the bubble showed an empty name. While the bubble shows, the page title (observed) fills the name until the user edits it; close saves it as before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: bookmark bubble live check covers the late page title, Page Info on the shared host (captures) and a real click on the star (--real-click) (cx-x9o52) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * review: Page Info rows keep their inset before the accessory (cx-x9o52) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Home: the home workspace with hq-ec's page in a left chat dock; Home links open on its right side (cx-ebm.59.5, cx-ebm.59.6) S5 (cx-ebm.59.5): Home is the daemon home workspace. On every connect the app ensures ONE `home-channels` page tab in it (keyed new-conversation-tab on the workspace) and moves it into a left dock column with the agent chat role through TabMoves.toNewDockColumn (the Cmd-I dock, cx-ihyx2; width TabMoves.leftDockWidth), never a second dock. A home from before migrates on its first connect: its Chief conversation tabs close (the page shows the Chief conversation) and the page moves into the dock; when nothing else stays on the right, the move respawns a terminal there in the same daemon commit (the daemon undocks a screen with no scrolling column). home.show reveals the home workspace in the active window (or brings forward the window that has it). The classic top page stays behind HomeDock.classic (false here; the setting lands separately). New code is in CmuxNextHomeEngine/HomeDock.swift; CmuxNextApp net lines unchanged. S6 (cx-ebm.59.6): the home-channels page tab's onOpenExternal (a person's click on an http(s) link) calls workspace.open_url {machine, session: current, url} on the app's own daemon connection (WorkspaceOpenURLClient). No routing in Swift: the daemon picks the pane and the tab. * cmux-next: pin CEF cef-154.0.28-cmux.25 (cmux.24 + WebAuthn hybrid fix with a PIN key attached; fork API 23) (cx-d0d.97) * cmux-next: remote browser host linked against CEF cmux.25 (step beeef9df6b98) (cx-d0d.97) * passkeys-roaming-e2e: --typing smoke with trusted key events (cx-d0d.97) * passkeys-matrix-e2e: a timed-out addVirtualAuthenticator reply counts as sent (the socket deadline is now 6 s) (cx-d0d.97) * chrome-promos-live: ignore Chromium's status bubble (<= 30 pt at the page window's bottom-left), logged; any other new window still fails (cx-d0d.97) * passkeys matrix + promo check review fixes: unconfirmed authenticator rows that pass without one are NOTE; promo count uses only new windows (cx-d0d.97) * Home dock: review fixes; classic Home reads DesignSettings.classicHome (cx-ebm.59.5) - HomeService: one @ObservationIgnored declaration per property (the macro takes one binding; the fleet compile caught it). - HomeDock: no second dock move while one is in flight or the tree does not show its dock yet (launch runs ensure twice); the guard also needs tab-column-respawn-v1. - home.show refuses with homeNeedsFocus for a run that may not change the view, as before. - HomeDock.classic reads DesignSettings.classicHome (off). The setting that sets it (labs.classicHome) touches the frozen settings paths and lands separately. * free tier: a Keychain error never mints a new App Attest key; no key-id read where App Attest cannot work (cx-5bxp7 P3-A, P3-B) AttestKeyStore load/save/delete throw on a store error (load: nil only for errSecItemNotFound). The provider answers failed when the key id cannot be read or saved, so it never generates or attests a new key then (a new key spends the device's monthly grant), and it saves a new key id before attesting it. The register line skips the Keychain read when App Attest is not supported (no prompt in an ad-hoc DEV build). * acpmux start mode: review fixes (cx-wx93i) - Settings file read as JSONC like the app (comments, trailing commas); a file acpmux cannot read or parse, or an unknown value, gives the harness's own mode, never full access. Classic cmux.json only when cmux-next.json does not exist. The setting is resolved before the config write lock. - A remote chain never starts Claude in bypassPermissions (a transfer keeps the mode): the spawn mode and args fall back to default. - A handoff target starts in full access only from a full-access source and never with narrower. - A profile with its own skip flag keeps its mode on respawn; config reload picks up defaultPermissionMode; acpmux reads mcp.enabled only nested, as the CLI does. - Tests: the fake Claude refuses bypassPermissions without the launch flag; the settings-file test is its own binary (it sets CMUX_NEXT_CONFIG_FILE) and covers JSONC, no key, an unparseable file and an unknown value. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-next: Ctrl and Option holds show their shortcut hints too; hint chips are Liquid Glass in the theme's tint (cx-w9f3s) Lawrence 2026-10-14: "we need cmd hold tab to work for hold ctrl as well, maybe option if there are relevant shortcuts, we also need it to be liquid glassy". - A hold arms on any set of Cmd, Ctrl and Option (with or without Shift), not only bare Cmd or Ctrl. The overlay shows each target's binding from the resolved table (keybindings.json and cmux.json overrides included) whose modifiers contain the held set; a hold with no matching binding shows no chip. Same 300 ms delay, key-down suppression and placement. - ShortcutHintChipView: a full Liquid Glass capsule (GlassPanelView -> NSGlassEffectView) tinted with the theme's glass tint, text in the theme's primary text color. macOS 14/15: the same panel's blur, tint and hairline. Reduce Transparency: the theme's opaque popup surface, a device-pixel hairline and the soft shadow. - debug.shortcut_hints takes opt, shift and +-joined sets (ctrl+opt). CmuxNextApp line count unchanged (113450). * cmux-next: hold hints review fixes: only Cmd/Ctrl chords get a chip, a click suppresses, stronger chip tint (cx-w9f3s) Review of d77c9ec (P2s): a chip only for bindings KeyRouter runs (KeyRouter.isChord: a pure-Option binding types a character); a mouse-down while a modifier is held hides and suppresses the hints like a key-down (Option-click / Option-drag in a terminal); the chip's glass tint is the theme tint at 1.5x alpha so 10 pt text keeps its contrast over bright content. P3s: no double Borders.color, no continuous curve against a circular shadow path, debug release only alone. plans/cmux-next/keybindings.md describes the Cmd/Ctrl/Option holds and the glass chip. CmuxNextApp line count unchanged (113450). * review: Page Info keeps its side on a new page; any NativePopupPanel passes the child-window policy; the bookmark name follows only the bookmarked URL's title; live check presses the star once (cx-x9o52) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * free tier e2e runner: never kill; the app quits through its control socket (action.run quitEndSessions, peer-checked), the daemon by acpmux daemon shutdown (cx-ds09i T1/T2) * cmux-next groups: a right-click on a group header or tab group chip opens the group editor Lawrence 2026-10-14: "im talking about the rename + color things ... looks like the image in chrome, we had it at some point". The editor bubble (name field, color dots, group rows; cx-rcby, 3ca7be3) is still there, but #19019 (31db8ce) pointed the header's more button and its right-click at the plain menu. #20442 put the more button back on the editor; this puts the right-click back too, and does the same for a tab group chip, whose right-click showed the app's menu (the editor opened only on a press and hold). The full menu stays one row away (More Group Actions… in the sidebar editor) and on Option-right-click of a tab group chip. scripts/cmux-next/group-editor-live.py proves it with real right-clicks (cua-driver): the editor opens with its name field, color dots and rows, a typed name with Return renames the group, Escape closes it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Channels view in the app: the page may play video from the daemon's loopback listener (PageCSP media-src) The Channels page plays a video attachment from the daemon's own GET /attachments/<token> (ws-attachments-v1). The app's page scheme sent no media-src, so the web view blocked it. PageCSP gains media sources; the home-channels descriptor allows http://127.0.0.1:* (the daemon listener). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * group-editor-live: find the group by its member row, the tab chip by the strip, the rename by the reopened editor Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * group-editor-live: real window-server right-click first, the in-app event path as fallback; strip rects are arrays Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-next tab groups: the group editor's More row shows the group's full menu under the chip (review P2) A right-click on a tab group chip now opens the editor, whose rows are New Tab, Ungroup, Close, Move to New Window and Save. The rest of the group's menu (icon, moves to a split, column or workspace, collapse, configured rows) stays one row away: More (the strip's existing paneToolbar.more string, 21 locales) pops the same menu under the chip, as the sidebar editor's More Group Actions row does. Option-right-click still shows it directly. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-tui: behavior test, a large legacy registry gets no full VACUUM while its owner serves (cx-4bd8m) Red commit: drives the owner binary on a registry in the shape a build without journal maintenance leaves (auto_vacuum off, no marker). Adds a debug-only CMUX_TUI_TEST_JOURNAL_MAINTENANCE_INTERVAL_MS override. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: the row badge stack shows five by default, Debug Settings picks 3/5/7, and a preview set shows 1-7 items Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(next): the row badge stack shows five, 3/5/7 in Debug Settings, and a preview set with 1-7 items Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-tui: no full VACUUM of a large legacy registry while its owner serves (cx-4bd8m) Austin's cmux NEXT registry grew to 10.7 GB in 8.5 h under a build without journal maintenance (auto_vacuum off, 4.47M journal rows). The maintenance worker's first step converts such a registry with PRAGMA auto_vacuum= INCREMENTAL; VACUUM; while it holds the registry: on that file every client of the owner would read "not connected" for minutes after the next update. Convert only while live data is at most 64 MiB. Above it the conversion is deferred (one diagnostic per owner); sealing still runs and deletes archived rows, and SQLite reuses their pages, so the file stops growing once sealing keeps up. It does not shrink: sealed segments stay in the registry, so giving the free pages back needs a compaction that does not hold the registry (follow-up bead). Debug-only overrides for the interval and the bound keep the behavior test small. Red/green: legacy_registry_maintenance. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-next: a local merge commit reuses the fetched same-tree acpmux (#20534) * test: a local merge commit reuses the fetched same-tree acpmux (red) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: a local merge commit reuses the fetched same-tree acpmux reload.sh on a developer Mac stopped with "no cached acpmux" for any commit no workflow published (a local integration of feat-cmux-next and PRs), even though the tree fetch had just put that tree's checksum-verified acpmux in place. build-acpmux.sh now seeds its commit cache from it when it has every requested architecture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 7be0d24 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38542356066 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * webviews: popups draw one hairline, not two (#20461) * webviews: test that every popup draws one hairline edge Red: --acpmux-popup-elevation stacks a composer-edge hairline on the one already in --ui-popup-shadow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: popups draw one hairline, not two --acpmux-popup-elevation stacked a 0.5px composer-edge hairline on the hairline already in --ui-popup-shadow. It now is the shared popup shadow, so every menu and popover has one edge. Increase Contrast colors that one edge in the popup's ink, which the composer-edge hairline did before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 1cc6616 - vp fmt webviews/src/agent-session/acpmux/modelPickerLayout.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38520503432 * webviews: test that popups draw no border over the hairline and clear it under borders none Red: the pages' .ui-popup and the chat find bar draw a border over the --ui-popup-shadow hairline, and appearance.borders none leaves the hairline drawn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: one rim on page popups and the find bar; borders none clears it The pages' .ui-popup drew a 1px border over the --ui-popup-shadow hairline; it now draws no border. The chat find bar keeps its border (its focus cue) and casts --ui-popup-drop, the shadow without the hairline. appearance.borders none sets --ui-popup-edge transparent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: format modelPickerLayout.ts (base fmt red) Keeps cmux-autofix's vp fmt of the base file, which the base merge replaced with the unformatted copy; react-apps-check fails on it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: the Channels palette draws one rim; test follows the summary card Red after the base merge: the new Channels palette (.lg-palette, ledger.css) drew a 1px border over the --ui-popup-shadow hairline, and the summary popover became .acpmux-summary-card. The palette now draws no border. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 54dd59d - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38543983469 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * fix(next): the preview's working row shows its spinner; the cap test registers the sidebar tunables Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: name the owner of ControlWorkspaceInfoCache's publish hop (clears the concurrency red) (#20485) * fix: clear two feat-cmux-next check reds tree-publish-v2's stub uploader takes --conflict-exit-code, which the tree publisher now passes (af8b68e). PaneAnimationsSetting carries the namespace-type justification its ratcheted sibling settings have. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep PaneAnimationsSetting internal to CmuxNextSettings Nothing outside the module uses it, so it needs no namespace-type allow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at c2bdb98 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38530638157 * fix: name the owner of ControlWorkspaceInfoCache's publish hop check-concurrency flags the unowned Task d0a62be added; it is a one-shot main-actor hop that holds the cache weakly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 7cf07e3 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38544346314 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * CmuxNext: one-line sidebar rows by default; notification text in the hover card (#20483) * test: a notified compact row stays one line; its text reads in the hover card Leo 2026-10-14: "why is it even 2 lines in the first place?" Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: compact sidebar rows are one line by default; the notification text moves to the hover card sidebar.workspaceRow.notification is now off by default (Leo 2026-10-14). #19862 turned it on for classic parity; the setting stays as an opt-in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: regenerate the settings schema hash for the notification default Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: regenerate the settings schema hash on the merged base Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: take base's settings exports after the base merge (regenerated next) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: regenerate the settings exports on the merged base Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: refresh the defaults snapshot on the merged base Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: the hover card template lists the notification fact; comments say where the text shows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * fix: badge time from row content, stack-only overflow, themed tooltip Review fixes: the compact row's dim time comes from the layout's now and skips rows whose second line shows the last activity or minimal mode; the tooltip names what the stack left out only for the stack; preview rows have no close button. 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>
… a running app (hq#1870) (#20471) * cmux-next: the typeahead GUI gate in the repo (cx-wb5.78): typeahead-gate.sh runs split, newterm, split with a slow host, new-tab and cold new-tab proofs on the GUI host and fails on a lost key; typeahead-compare.py counts keys sent by terminal id; creation-hold-proof.py is superseded (named in the gate header) * test(control): a deleted control socket file comes back while the app runs (red) Two readiness soaks on one capture mini lost /tmp/cmux-debug-<tag>.sock at about 11:00Z (an external /tmp sweep); the app kept listening on the unlinked inode and no client could connect until a relaunch. These tests delete the file under a running server and expect a ping within 5 s, with the same mode, and expect a path another server took meanwhile to stay that server's. hq#1870, no bead Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(control): put the control socket back when its file is deleted under a running app ControlSocketPathWatch watches the socket's directory with kqueue (no timer) and, once a burst of changes settles, asks the server for a new listener when nothing is at the path. The new socket is bound like the first (private staging dir, its mode, then RENAME_EXCL), so a path another server or a file took meanwhile is never replaced, and stop still unlinks only the server's own inode. Open connections keep working. The socket-file helpers move to ControlSocketServer+File.swift (move-only) to keep the server under the file budget. hq#1870, no bead Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: a stop during a socket restore closes the new socket The restore cancelled a listener it never resumed when the server stopped mid-bind. The other-server test now swaps the path in one rename. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: restore the control socket on its accept queue The path check runs inline on the accept queue instead of a timer task, so a restore never races an accept pausing on descriptor exhaustion. The new listener is resumed before it is published, and resumed then cancelled when the server stopped mid-restore. The watch stops after three failed restores in a row instead of re-triggering itself. Adds the wake source to the idle-wakeups plan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: set SO_NOSIGPIPE on the stale-socket probe The probe moved to its own file, where the crash-safety check looks for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: take the restore path with a plain listener A second ControlSocketServer renamed into place restored its own path and dropped the listener the test connected to. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: failing socket restores back off and still come back Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: back off failing socket restores instead of giving up Three failed restores in a row stopped the watch for good, so a delete during descriptor exhaustion lost the socket until a relaunch. The watch now ignores events and retries with a capped backoff until a restore works. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 8dd3953 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38530936996 * acpmux: behavior test, a new chat starts in full access; the person's switch back is saved; every chat gets the cmux MCP server (red) (cx-wx93i) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * acpmux: new chats start in the harness's full-access mode; the person can switch back and the pick is saved; every agent chat gets the cmux MCP server (cx-wx93i) Lawrence 2026-10-14: "give every agent chat the cmux MCP tools by default, but by default all chats should be in skip permission mode, that user can switch back". - hub/start_mode.rs: once the harness is up, acpmux sets the start mode with session/set_mode (soft, like the start model): the person's saved pick (config.json defaults.<profile>.mode), else acpmux config.json defaultPermissionMode, else the cmux setting agents.defaultPermissionMode (fullAccess | standard | ask), else full access: Claude Code bypassPermissions, the Codex bridge agent-full-access (approval never + danger-full-access), or a harness's yolo/full-access mode. Not for Web (remote-origin) chats, presets, folder profiles, adopted chats, or a profile that pins its mode in argv. - A mode pick from the person's app pane (session/set_mode or the mode config option) is saved; Plan is a toggle and is not saved; a pick equal to what the setting gives removes the saved mode. Only the person may clear a saved mode (_acpmux/defaults clear), as for a stored policy. - claude_stdio: offers bypassPermissions ("Full access") and launches Claude Code with --allow-dangerously-skip-permissions when it accepts it (not root, or IS_SANDBOX=1); remote chains strip the flag. - agent_tools: the cmux MCP server (cmux mcp serve) is on for every agent chat unless the settings file says mcp.enabled false; the settings file is now the app's cmux-next.json (user_settings.rs), not classic cmux.json. - cmux mcp serve: on when CMUX_AGENT_SESSION is set and mcp.enabled is not set; any other client still needs mcp.enabled true. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(cmux-next): Page Info on the shared native popup host; its rows on the shared popup row (cx-x9o52, REUSE.md slice 3) PageInfoPanel is a NativePopupPanel: placement through PopupPlacement, the host's grow/shrink motion and dismiss rules (click outside with the omnibar button kept open, Escape, key loss, app inactive, window move/resize/close). The hand placement, PopupFade and the mouse-down reopen guard go. The card no longer draws its own shadow band (PopupHostView draws the one popup shadow). PageInfoRowView is a PopupRowView. NativePopupPanel joins the child-window allow list (the bookmark bubble tripped it in DEBUG). PageInfoMotionTests (a unit test of PopupFade on PageInfoPanel) is deleted under the no-unit-test rule; the live check covers the bubble. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(cmux-next): a new bookmark's name follows the page title that arrives after the star (bookmark bubble) The star saves the bookmark at once, often before the page has a title, so the bubble showed an empty name. While the bubble shows, the page title (observed) fills the name until the user edits it; close saves it as before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: bookmark bubble live check covers the late page title, Page Info on the shared host (captures) and a real click on the star (--real-click) (cx-x9o52) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * review: Page Info rows keep their inset before the accessory (cx-x9o52) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Home: the home workspace with hq-ec's page in a left chat dock; Home links open on its right side (cx-ebm.59.5, cx-ebm.59.6) S5 (cx-ebm.59.5): Home is the daemon home workspace. On every connect the app ensures ONE `home-channels` page tab in it (keyed new-conversation-tab on the workspace) and moves it into a left dock column with the agent chat role through TabMoves.toNewDockColumn (the Cmd-I dock, cx-ihyx2; width TabMoves.leftDockWidth), never a second dock. A home from before migrates on its first connect: its Chief conversation tabs close (the page shows the Chief conversation) and the page moves into the dock; when nothing else stays on the right, the move respawns a terminal there in the same daemon commit (the daemon undocks a screen with no scrolling column). home.show reveals the home workspace in the active window (or brings forward the window that has it). The classic top page stays behind HomeDock.classic (false here; the setting lands separately). New code is in CmuxNextHomeEngine/HomeDock.swift; CmuxNextApp net lines unchanged. S6 (cx-ebm.59.6): the home-channels page tab's onOpenExternal (a person's click on an http(s) link) calls workspace.open_url {machine, session: current, url} on the app's own daemon connection (WorkspaceOpenURLClient). No routing in Swift: the daemon picks the pane and the tab. * cmux-next: pin CEF cef-154.0.28-cmux.25 (cmux.24 + WebAuthn hybrid fix with a PIN key attached; fork API 23) (cx-d0d.97) * cmux-next: remote browser host linked against CEF cmux.25 (step beeef9df6b98) (cx-d0d.97) * passkeys-roaming-e2e: --typing smoke with trusted key events (cx-d0d.97) * passkeys-matrix-e2e: a timed-out addVirtualAuthenticator reply counts as sent (the socket deadline is now 6 s) (cx-d0d.97) * chrome-promos-live: ignore Chromium's status bubble (<= 30 pt at the page window's bottom-left), logged; any other new window still fails (cx-d0d.97) * passkeys matrix + promo check review fixes: unconfirmed authenticator rows that pass without one are NOTE; promo count uses only new windows (cx-d0d.97) * Home dock: review fixes; classic Home reads DesignSettings.classicHome (cx-ebm.59.5) - HomeService: one @ObservationIgnored declaration per property (the macro takes one binding; the fleet compile caught it). - HomeDock: no second dock move while one is in flight or the tree does not show its dock yet (launch runs ensure twice); the guard also needs tab-column-respawn-v1. - home.show refuses with homeNeedsFocus for a run that may not change the view, as before. - HomeDock.classic reads DesignSettings.classicHome (off). The setting that sets it (labs.classicHome) touches the frozen settings paths and lands separately. * free tier: a Keychain error never mints a new App Attest key; no key-id read where App Attest cannot work (cx-5bxp7 P3-A, P3-B) AttestKeyStore load/save/delete throw on a store error (load: nil only for errSecItemNotFound). The provider answers failed when the key id cannot be read or saved, so it never generates or attests a new key then (a new key spends the device's monthly grant), and it saves a new key id before attesting it. The register line skips the Keychain read when App Attest is not supported (no prompt in an ad-hoc DEV build). * acpmux start mode: review fixes (cx-wx93i) - Settings file read as JSONC like the app (comments, trailing commas); a file acpmux cannot read or parse, or an unknown value, gives the harness's own mode, never full access. Classic cmux.json only when cmux-next.json does not exist. The setting is resolved before the config write lock. - A remote chain never starts Claude in bypassPermissions (a transfer keeps the mode): the spawn mode and args fall back to default. - A handoff target starts in full access only from a full-access source and never with narrower. - A profile with its own skip flag keeps its mode on respawn; config reload picks up defaultPermissionMode; acpmux reads mcp.enabled only nested, as the CLI does. - Tests: the fake Claude refuses bypassPermissions without the launch flag; the settings-file test is its own binary (it sets CMUX_NEXT_CONFIG_FILE) and covers JSONC, no key, an unparseable file and an unknown value. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-next: Ctrl and Option holds show their shortcut hints too; hint chips are Liquid Glass in the theme's tint (cx-w9f3s) Lawrence 2026-10-14: "we need cmd hold tab to work for hold ctrl as well, maybe option if there are relevant shortcuts, we also need it to be liquid glassy". - A hold arms on any set of Cmd, Ctrl and Option (with or without Shift), not only bare Cmd or Ctrl. The overlay shows each target's binding from the resolved table (keybindings.json and cmux.json overrides included) whose modifiers contain the held set; a hold with no matching binding shows no chip. Same 300 ms delay, key-down suppression and placement. - ShortcutHintChipView: a full Liquid Glass capsule (GlassPanelView -> NSGlassEffectView) tinted with the theme's glass tint, text in the theme's primary text color. macOS 14/15: the same panel's blur, tint and hairline. Reduce Transparency: the theme's opaque popup surface, a device-pixel hairline and the soft shadow. - debug.shortcut_hints takes opt, shift and +-joined sets (ctrl+opt). CmuxNextApp line count unchanged (113450). * cmux-next: hold hints review fixes: only Cmd/Ctrl chords get a chip, a click suppresses, stronger chip tint (cx-w9f3s) Review of d77c9ec (P2s): a chip only for bindings KeyRouter runs (KeyRouter.isChord: a pure-Option binding types a character); a mouse-down while a modifier is held hides and suppresses the hints like a key-down (Option-click / Option-drag in a terminal); the chip's glass tint is the theme tint at 1.5x alpha so 10 pt text keeps its contrast over bright content. P3s: no double Borders.color, no continuous curve against a circular shadow path, debug release only alone. plans/cmux-next/keybindings.md describes the Cmd/Ctrl/Option holds and the glass chip. CmuxNextApp line count unchanged (113450). * review: Page Info keeps its side on a new page; any NativePopupPanel passes the child-window policy; the bookmark name follows only the bookmarked URL's title; live check presses the star once (cx-x9o52) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * free tier e2e runner: never kill; the app quits through its control socket (action.run quitEndSessions, peer-checked), the daemon by acpmux daemon shutdown (cx-ds09i T1/T2) * cmux-next groups: a right-click on a group header or tab group chip opens the group editor Lawrence 2026-10-14: "im talking about the rename + color things ... looks like the image in chrome, we had it at some point". The editor bubble (name field, color dots, group rows; cx-rcby, 3ca7be3) is still there, but #19019 (31db8ce) pointed the header's more button and its right-click at the plain menu. #20442 put the more button back on the editor; this puts the right-click back too, and does the same for a tab group chip, whose right-click showed the app's menu (the editor opened only on a press and hold). The full menu stays one row away (More Group Actions… in the sidebar editor) and on Option-right-click of a tab group chip. scripts/cmux-next/group-editor-live.py proves it with real right-clicks (cua-driver): the editor opens with its name field, color dots and rows, a typed name with Return renames the group, Escape closes it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Channels view in the app: the page may play video from the daemon's loopback listener (PageCSP media-src) The Channels page plays a video attachment from the daemon's own GET /attachments/<token> (ws-attachments-v1). The app's page scheme sent no media-src, so the web view blocked it. PageCSP gains media sources; the home-channels descriptor allows http://127.0.0.1:* (the daemon listener). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * group-editor-live: find the group by its member row, the tab chip by the strip, the rename by the reopened editor Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * group-editor-live: real window-server right-click first, the in-app event path as fallback; strip rects are arrays Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-next tab groups: the group editor's More row shows the group's full menu under the chip (review P2) A right-click on a tab group chip now opens the editor, whose rows are New Tab, Ungroup, Close, Move to New Window and Save. The rest of the group's menu (icon, moves to a split, column or workspace, collapse, configured rows) stays one row away: More (the strip's existing paneToolbar.more string, 21 locales) pops the same menu under the chip, as the sidebar editor's More Group Actions row does. Option-right-click still shows it directly. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-tui: behavior test, a large legacy registry gets no full VACUUM while its owner serves (cx-4bd8m) Red commit: drives the owner binary on a registry in the shape a build without journal maintenance leaves (auto_vacuum off, no marker). Adds a debug-only CMUX_TUI_TEST_JOURNAL_MAINTENANCE_INTERVAL_MS override. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-tui: no full VACUUM of a large legacy registry while its owner serves (cx-4bd8m) Austin's cmux NEXT registry grew to 10.7 GB in 8.5 h under a build without journal maintenance (auto_vacuum off, 4.47M journal rows). The maintenance worker's first step converts such a registry with PRAGMA auto_vacuum= INCREMENTAL; VACUUM; while it holds the registry: on that file every client of the owner would read "not connected" for minutes after the next update. Convert only while live data is at most 64 MiB. Above it the conversion is deferred (one diagnostic per owner); sealing still runs and deletes archived rows, and SQLite reuses their pages, so the file stops growing once sealing keeps up. It does not shrink: sealed segments stay in the registry, so giving the free pages back needs a compaction that does not hold the registry (follow-up bead). Debug-only overrides for the interval and the bound keep the behavior test small. Red/green: legacy_registry_maintenance. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-next: a local merge commit reuses the fetched same-tree acpmux (#20534) * test: a local merge commit reuses the fetched same-tree acpmux (red) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: a local merge commit reuses the fetched same-tree acpmux reload.sh on a developer Mac stopped with "no cached acpmux" for any commit no workflow published (a local integration of feat-cmux-next and PRs), even though the tree fetch had just put that tree's checksum-verified acpmux in place. build-acpmux.sh now seeds its commit cache from it when it has every requested architecture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 7be0d24 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38542356066 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * webviews: popups draw one hairline, not two (#20461) * webviews: test that every popup draws one hairline edge Red: --acpmux-popup-elevation stacks a composer-edge hairline on the one already in --ui-popup-shadow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: popups draw one hairline, not two --acpmux-popup-elevation stacked a 0.5px composer-edge hairline on the hairline already in --ui-popup-shadow. It now is the shared popup shadow, so every menu and popover has one edge. Increase Contrast colors that one edge in the popup's ink, which the composer-edge hairline did before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 1cc6616 - vp fmt webviews/src/agent-session/acpmux/modelPickerLayout.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38520503432 * webviews: test that popups draw no border over the hairline and clear it under borders none Red: the pages' .ui-popup and the chat find bar draw a border over the --ui-popup-shadow hairline, and appearance.borders none leaves the hairline drawn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: one rim on page popups and the find bar; borders none clears it The pages' .ui-popup drew a 1px border over the --ui-popup-shadow hairline; it now draws no border. The chat find bar keeps its border (its focus cue) and casts --ui-popup-drop, the shadow without the hairline. appearance.borders none sets --ui-popup-edge transparent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: format modelPickerLayout.ts (base fmt red) Keeps cmux-autofix's vp fmt of the base file, which the base merge replaced with the unformatted copy; react-apps-check fails on it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: the Channels palette draws one rim; test follows the summary card Red after the base merge: the new Channels palette (.lg-palette, ledger.css) drew a 1px border over the --ui-popup-shadow hairline, and the summary popover became .acpmux-summary-card. The palette now draws no border. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 54dd59d - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38543983469 --------- 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: name the owner of ControlWorkspaceInfoCache's publish hop (clears the concurrency red) (#20485) * fix: clear two feat-cmux-next check reds tree-publish-v2's stub uploader takes --conflict-exit-code, which the tree publisher now passes (af8b68e). PaneAnimationsSetting carries the namespace-type justification its ratcheted sibling settings have. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep PaneAnimationsSetting internal to CmuxNextSettings Nothing outside the module uses it, so it needs no namespace-type allow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at c2bdb98 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38530638157 * fix: name the owner of ControlWorkspaceInfoCache's publish hop check-concurrency flags the unowned Task d0a62be added; it is a one-shot main-actor hop that holds the cache weakly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 7cf07e3 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38544346314 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * CmuxNext: one-line sidebar rows by default; notification text in the hover card (#20483) * test: a notified compact row stays one line; its text reads in the hover card Leo 2026-10-14: "why is it even 2 lines in the first place?" Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: compact sidebar rows are one line by default; the notification text moves to the hover card sidebar.workspaceRow.notification is now off by default (Leo 2026-10-14). #19862 turned it on for classic parity; the setting stays as an opt-in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: regenerate the settings schema hash for the notification default Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: regenerate the settings schema hash on the merged base Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: take base's settings exports after the base merge (regenerated next) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: regenerate the settings exports on the merged base Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: refresh the defaults snapshot on the merged base Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: the hover card template lists the notification fact; comments say where the text shows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * test(cmux-next): a failed LineClient connect closes its descriptor once The init closed the socket and then threw with every stored property set, so deinit closed the same number again. Under parallel suites that number already belonged to another test's listener: listen failed with EBADF in passwordModeRequiresLogin, and a restored socket stopped answering in aDeletedSocketFileComesBackWithItsMode (macOS 27 run at 140aa1f). The restore tests' connect-until-up loops made it frequent. Set the stored descriptor only after the connect works. 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>
…s picked (cx-uq9xi) (#20506) * free tier: the local relay owns the device lease; the cmux NEXT app is its App Attest provider; freeModel.enabled (cx-ds09i) Relay (cmux-coderouter free_tier.rs): attest_provider on the admin socket, peer-checked (same user; a Team-signed build: the cmux app that contains the relay, cmux-link app_caller), production origin only in release apps (staging and the dev Worker only in DEV builds), one provider and one request at a time. The relay builds the client data hash from the server challenge, keeps the free token in memory only (never in status or logs), drops it when the provider ends, when the setting turns off or an account signs in (also for a lease in flight), and starts a lease only at the first signed-out data-plane request (20 s, in its own task). App Attest refuses a second attestation of a key in a later launch, so a new month takes a fresh key. The account bearer wins. App: Packages/Shared/CmuxAppAttest (DeviceAttesting, AppleDeviceAttest, KeychainAttestKeyStore); FreeTierAttestProvider (CmuxNextAgentPane) answers only purpose cmux.free-tier.v1 over a 32-byte hash on an AcpmuxCheckedLine connection, one keychain item per bundle id; AppServices+FreeTier follows freeModel.enabled (default on, MDM lock, agents refused: privacy) and the sign-in (a restoring session counts as signed in). Behavior tests: acpmux/tests/router_free_tier.rs (real relay, admin socket, stand-in app and API; 7 tests, Linux rbx and hosted macOS 38362901804). Red check of the in-flight race at a3b1066ba9c7 recorded on cx-ds09i. scripts/cmux-next/free-tier-attest-e2e.sh: T1/T2 on a real Mac. Security review hq-84: APPROVE at 8b1c3f861a1e. * settings: tip catalog with the four freeModel strings (21 locales); exports reset to the tip before regeneration (cx-ds09i) * test(cmux-tui): Chief registry behavior tests at the socket and the CLI (red) (cx-ebm.60.1, cx-ebm.60.2) The person makes, edits and deletes Chiefs with cmux chief new|list|edit|delete; a Chief on its own bound connection changes its own picture and only the fields its self_edit lists; another agent is refused. Red: the daemon has no chief.list/get/create/update/delete/avatar.* operations yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(cmux-tui): the daemon's Chief registry and cmux chief new|list|edit|delete (cx-ebm.60.1, cx-ebm.60.2, cx-ebm.60.3) Multiple Chiefs per Chief home (Lawrence 2026-10-14: "create new chiefs too with specific roles, names, ... profile pics (chiefs need to be able to do it themselves too), personality, system prompt, tools"). Daemon: chief_registry.rs owns chiefs.sqlite3 in the session state directory. A record has id (= the conversation participant id; agent_mux is the default Chief, written on first open; new ones agent_<26 base32>), display_name, role, personality, instructions (system prompt additions), avatar (PNG/JPEG/GIF/WebP <= 1 MiB, bytes by SHA-256, collected when no record names them), tools (closed catalog), harnesses, engine, self_edit, is_default, rev. Deleted ids stay as tombstones. Idempotency ledger per caller. cmux.protocol/2: chief.list, chief.get, chief.avatar.get (read); chief.create, chief.update, chief.delete, chief.avatar.set (mutation). Callers: the owner (Unix, no link peer, user_local) and the Chiefs (a connection bound with conversation-bind to a live record's id). A Chief reads every record and changes only its own self_edit fields (default: avatar); tools, harnesses, engine, self_edit, create and delete are the owner's; anything else is chief_self_edit_denied before any write. Pages, WebSocket, relayed and remote connections and non-Chief agents get origin.forbidden. Not MCP tools. CLI: cmux chief list | new --name N [profile] | edit CHIEF [profile] [--clear-avatar] | delete CHIEF, CHIEF an id or a unique name, --json. schemas/chief-profile/tool.json: the Chief's self-edit tool (profile) for the brain (wired in cx-ebm.60.5 after the per-build Chief home lands). Catalog: 271 operations (84 read, 168 mutation). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: AcpmuxEnvironment.resolve for the free-tier provider (the tip removed AgentTabPaneSource.paneEnvironment) (cx-ds09i) * settings: regenerated settings schema and MDM exports for freeModel.enabled on the tip (cx-ds09i; export-settings cmux-ci step 02f78faaf8d0) * webviews: settings strings for freeModel on the tip (cx-ds09i) * fix(cmux-tui): Chief registry ops are chief.profile.*; review fixes (cx-ebm.60.1, cx-ebm.60.2) Review (correctness subagent, 2026-10-14): - The cloud catalog already has chief.list/create/update with another shape; the app platform merges both catalogs by name with the local op winning, so equal names hid the cloud ops. The daemon ops are now chief.profile.list/get/create/update/delete/avatar.set/avatar.get; the generated app-host types keep the cloud chief.* ops. - idempotency.conflict names the committed operation; the ledger keeps the newest 256 keys per caller. - harness, model and effort are single-line. - CLI: "needs a newer daemon" only when the first chief.profile.list call is refused as unknown; a value error prints the daemon's reason. The picture is checked for 1 MiB before any write; when the picture write fails after the create or edit committed, the CLI says what committed. edit with no change, and --clear-avatar outside edit, are usage errors. - The spec states the trust limit (a Chief turn with a shell is the same OS user; the self_edit rule guards the model); hardening is cx-ebm.60.9. - Generated: app-host d.ts/ops/scopes and the AppPlatform scopes copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * style(cmux-tui): rustfmt the Chief registry files (cx-ebm.60.1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * style(cmux-tui): mod order for rustfmt (cx-ebm.60.1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: the typeahead GUI gate in the repo (cx-wb5.78): typeahead-gate.sh runs split, newterm, split with a slow host, new-tab and cold new-tab proofs on the GUI host and fails on a lost key; typeahead-compare.py counts keys sent by terminal id; creation-hold-proof.py is superseded (named in the gate header) * app: the free-tier composition moves to CmuxNextAgentPane (FreeTierAttestStarter); CmuxNextApp keeps a two-line hook (cx-ds09i; CmuxNextApp size cap) * app: one-line free-tier hook; FreeTierAttestStarter owns its task for the app's life (cx-ds09i; CmuxNextApp size cap) * +1 cx-ds09i launch hook, +1 tip drift from another lane; chief go * free tier: keep the tip's ghostty pointer (cb1ef7091cde, #20024; a stale submodule checkout had moved it back); signed-in fallback true when the services are gone (cx-ds09i move-check) * free tier: ci-target-graph.json and the workspace file are the tip's plus only the CmuxAppAttest lines (cx-ds09i final check: a stale regeneration had dropped CmuxNextBrowserHistory, CmuxNextCLIInstall, CmuxNextNowPlaying and two CmuxNextCompat test edges) * settings: regenerated settings schema and MDM exports on the merged tip ae657738afc2 (cx-ds09i; export-settings cmux-ci step 788077f4a088) * webviews: settings strings for freeModel on the merged tip (cx-ds09i) * chore(cmux-app-host): regenerate the app catalog after the tip merge (cx-ebm.60.1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * acpmux: a Windows client whose daemon lost the start lock waits for the winner's socket; fixes the Windows start code of landings 2/3 (2f063f889c, 1fdb2a766b) (cx-ncc.54) two_starts_at_once_share_one_daemon failed on the hosted Windows runner (cmux-tui full run 38352677155): the losing start's client connected once before the winning daemon had bound its socket ("read the owner: The system cannot find the file specified"). It passed on run 38352027335 (same start code), so it is a race in the Windows start that landings 2 and 3 put on feat-cmux-next, not in the agent job change. Now, when the started daemon exits without a readiness line, the client waits (within the same start budget) while another daemon holds daemon.lock and the socket file is missing. The probe takes daemon.lock for an instant when it is free (Windows cannot test a LockFileEx lock without taking it); the risk is written in a code comment. The change is in daemon/windows.rs; daemon.rs does not grow. * acpmux: owner-only files and folders on Windows (cx-ncc.54) What is mode 0600 / 0700 and a uid + mode check on Unix is an access list on Windows (src/owner_only.rs): - Written files are created (CreateFileW, CREATE_NEW, no reparse-point follow) with a protected DACL that grants only our token user, our user as owner: write_atomic (config.json and every file written through it: handoffs, the registry cache, harness profiles), the LocalApp and peer token files, private agent files, preset system prompts. daemon.log is opened that way and narrowed if a wider one exists. - Folders made here inherit the same list (0700 on create): harness_admin, handoffs, the registry cache, `cmux harness add` and `harness registry`, the doctor's temporary folder. ensure_private_dir (the run folder of the token files) makes or checks one and refuses a wider folder, as on Unix. narrow_to_owner replaces a wider list on config.json. - Profile reads (user and folder profiles): a file another user can change is refused, as `mode & 022` or a foreign uid is on Unix. "Another user" is the owner, or a SID granted write, delete or ACL rights, other than our user, SYSTEM, Administrators or OWNER RIGHTS (they can change any file, as root can); deny and inherit-only entries grant nothing; no DACL grants everyone. The diagnostic names the SID and gives the icacls fix. Not in this landing: the Computer Use endpoint check (cua_v2, with its socket), router routes (the router landing) and the remote-floor file identity checks. Behavior tests (tests/windows_daemon.rs; green on the Windows Server 2022 VM): after `acpmux daemon start`, config.json, daemon.log, run\, run\localapp.token and run\peer.token are owner-only (the SDK's owner-only check); `acpmux harness add` writes an owner-only profile that `harness list` loads, and after `icacls /grant Everyone:(M)` the list refuses it with "another user (S-1-1-0) can change this file" and the fix. * acpmux: Windows owner-only review fixes (cx-ncc.54) - daemon.log is opened shared for read, write and delete, as std opens are: a running daemon holds it as stdout and stderr, and a second start (two clients at once) must still open it; with read-only sharing it failed with a sharing violation. - daemon.log must be the file itself, not a link: the access list checks and the narrowing go by path. - write_atomic removes a temporary file a crashed process left under a reused pid, then creates it again (CREATE_NEW refused it). Behavior test: two acpmux daemon start at once on a new home both succeed and name the same daemon (green on the Windows VM). * acpmux: the Windows narrow_to_owner body moves to owner_only.rs, so daemon.rs does not grow (cx-ncc.54) daemon.rs is over the Rust god-file budget on feat-cmux-next (not in the baseline), and the CI god-file check fails any growth. The Windows narrow_to_owner (an existing file or folder that is not owner-only gets the owner-only access list; a failure is logged) is now owner_only::narrow, imported under the old name. No behavior change. Base reds: exact base 7b86415e2d82 (cmux-tui full run 38529816383). The gate excludes a red job only when that run has the same job red with the same error lines; expected from the earlier base 12b1e29b82d9 (run 38357900702): "valgrind-leak-check (startup)" and "valgrind-leak-check" ("cmux_tui-f9329b8abfb86495 does not contain exact test config::tests::ghostty_config_files::load_uses_file_ghostty_defaults_without_invoking_external_resolver"; valgrind 0 errors). This stack changes only cmux-tui/crates/acpmux. The first commit of this landing is the Windows start-race fix for landings 2/3 (2f063f889c, 1fdb2a766b). Linux tests (chief decision 2026-10-14, cx-f4thx): this landing changes no Linux-compiled line (every hunk is cfg(windows) code or under it). Its gate accepts a red "test (linux)" only when the only failing test is router_lifetime::a_kept_agent_keeps_its_router_and_the_next_daemon_adopts_it, a feat-cmux-next flake ("agent response channel dropped" on the first prompt; Freestyle loops: 4/120 on aebcd4572fc6, 2/40 on this stack; its cause, a double host adoption, is fixed by cx-f4thx 7b86415e2d82). darwin, windows, the Linux build and clippy must be green. * acpmux: red test, a direct agent's moved-out helper outlives the model probe and a kill (cx-n3uid) Behavior test at the daemon socket (real acpmux binary, agent hosts off): the fake harness starts a setsid helper on session/new. _acpmux/models {refresh:true} waits for the model probes; every probe helper must be gone within 10 s. A direct session agent stopped with _acpmux/kill must take its helper too. ChildAgent::kill/terminate end the harness with killpg only, so both helpers run on with ppid 1. * acpmux: ending a direct agent ends what its harness moved out of its group (cx-n3uid) The model probe (probe_one, at each daemon start, config reload and models --refresh) and a direct session agent (agent hosts off) end the harness through ChildAgent::kill/terminate: killpg only. A helper the harness moved into its own session (setsid, Node detached:true) missed the group signal and ran on with ppid 1. As the pool does for a SIGKILLed host (cx-7gs9r, terminate_unadoptable): take the processes under the harness by pid and start time (agent_host::tree::descendants) while the harness is not reaped, signal the group, then SIGKILL the ones still running (kill_still_running). Both ps reads run off the runtime. Unix only. harness_helpers.rs: red before (both helpers ran on after 10 s), green after (rbx). * acpmux: review follow-ups, name what the direct-agent tree end does not reach; the helper test cleans up on failure (cx-n3uid) * acpmux: red test: with nothing picked a new chat starts on the harness's own config (cx-uq9xi) Codex's config.toml model and model_reasoning_effort, and Claude Code's settings.json model (an alias resolved to the catalog id), come before the catalog default; the person's pick still wins; a config that does not parse, or names a model the catalog does not list, falls back to the catalog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * acpmux: a new chat with nothing picked starts on the harness's own config (cx-uq9xi) Leo 2026-10-14: the composer's starting model and effort are the last pick, else what the harness's own config chooses, else the catalog default. start_defaults now reads Codex's config.toml (`model`, `model_reasoning_effort`, the active `[profiles.<profile>]` first) and Claude Code's settings.json `model` (harness_config.rs), between the saved pick and the catalog. A config model counts only when the catalog lists it (an alias names its id) and a config effort only when it fits the running model; a file that is missing or does not parse chooses nothing. `_acpmux/harnesses` reports the new source as "harness". The start_defaults test homes give each fake its own config folder, so the machine's ~/.codex and ~/.claude never leak in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * CmuxNext: chrome fonts are nil-checked; a nil font no longer aborts keycap drawing (no bead) nxtip3-v1 (772b6ea30a5b) aborted at 01:42 in PaletteKeycapsView.capWidth: NSString.size(withAttributes:) -> CoreText TAttributes::ApplyFont -> -[NSDictionary initWithDictionary:copyItems:] "attempt to insert nil object from objects[0]". Cause: Typography.shortcut (NSFont.monospacedSystemFont(ofSize:weight:), imported non-optional) returned nil on the main thread. Swift kept the null NSFont in [.font: ...]; CoreText found no font, copied the attributes to apply its default, and the copy hit the nil. Reproduced on cmux-lawrence-2 (macOS 27.0.1): a nil font in the attributes gives the same exception and frames; a nil foreground color does not abort. Ruled out: the size (NaN, 0, negative and infinite sizes all return a font there), the weight, and a call off the main thread (the faulting thread is the main queue). The laptop log has no fontd or CoreText error and no jetsam before the crash; why AppKit returned nil is not visible. Fix: CheckedFont tests each AppKit font for null (an optional bit cast in a non-inlined function, which -O does not fold) and falls back to the system font, then CoreText Helvetica, then the last font that resolved, with a fault log. Every Typography font (body, caption, header, search, shortcut, title, subtitle) goes through it, so the palette keycaps, the which-key overlay and every other Typography caller measure and draw with a non-null font; ShortcutHintChipView and the braille spinner font too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(cmux-next): one shared native popup row; page menus and the Extensions menu draw their rows with it (cx-jpi3o, REUSE.md slice 2) PopupRowView (CmuxNextDesign/Overlay) owns what every native popup row shares: the leading column, title and subtitle, a trailing view, the gray highlight in the popup row radius, press fill, gray focus ring, hover tracking, release-inside click, Return/Enter/Space and the accessibility press. WebPopupRows stacks PopupRowViews (the page keeps the one highlight); ExtensionMenuRowView is a PopupRowView with the system menu font and colors. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * free tier part 2: settings sources and exports equal the tip (landed in part 1, 7bd91dc3129c) (cx-ds09i) * chore(next): PR autofix at 73b393e337d9 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38542400367 * chore(app-platform): sync the AppPlatform scopes copy after the tip merge (cx-ebm.60.1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Channels view: connect with no credential when the page's origin already authorized the person A plain-browser Channels page whose daemon endpoint is its own origin first asks `GET /auth/session`. When that server answers {"authorized": true} (a relay that identified the person, for example by a private-network identity, and attaches the daemon credential upstream itself), the page connects with the new `{ origin: true }` auth: no preamble, no form, and the browser never holds the credential. Any other answer (a 404 from a daemon that serves the page itself included) shows the credential form as before; a refused origin connection falls back to the form too. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Channels view: origin auth comments (review) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * acpmux: behavior test, a new chat starts in full access; the person's switch back is saved; every chat gets the cmux MCP server (red) (cx-wx93i) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * acpmux: new chats start in the harness's full-access mode; the person can switch back and the pick is saved; every agent chat gets the cmux MCP server (cx-wx93i) Lawrence 2026-10-14: "give every agent chat the cmux MCP tools by default, but by default all chats should be in skip permission mode, that user can switch back". - hub/start_mode.rs: once the harness is up, acpmux sets the start mode with session/set_mode (soft, like the start model): the person's saved pick (config.json defaults.<profile>.mode), else acpmux config.json defaultPermissionMode, else the cmux setting agents.defaultPermissionMode (fullAccess | standard | ask), else full access: Claude Code bypassPermissions, the Codex bridge agent-full-access (approval never + danger-full-access), or a harness's yolo/full-access mode. Not for Web (remote-origin) chats, presets, folder profiles, adopted chats, or a profile that pins its mode in argv. - A mode pick from the person's app pane (session/set_mode or the mode config option) is saved; Plan is a toggle and is not saved; a pick equal to what the setting gives removes the saved mode. Only the person may clear a saved mode (_acpmux/defaults clear), as for a stored policy. - claude_stdio: offers bypassPermissions ("Full access") and launches Claude Code with --allow-dangerously-skip-permissions when it accepts it (not root, or IS_SANDBOX=1); remote chains strip the flag. - agent_tools: the cmux MCP server (cmux mcp serve) is on for every agent chat unless the settings file says mcp.enabled false; the settings file is now the app's cmux-next.json (user_settings.rs), not classic cmux.json. - cmux mcp serve: on when CMUX_AGENT_SESSION is set and mcp.enabled is not set; any other client still needs mcp.enabled true. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * review: popup row title end yields in a narrow row; 12 pt before a shortcut; a disabled row's check mark is quiet (cx-jpi3o) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * ci-guards: keep the step name "Run agent-chat tests" (cx-eczvt) Main sync merge 3bf71fdcda1e brought back main's "Run agent-chat unit tests"; feat-cmux-next renamed it after the unit-test deletion (cx-034r, cx-ys07v). test_ci_guard_workflow_structure and test_ci_change_areas now name the same step. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(cmux-next): Page Info on the shared native popup host; its rows on the shared popup row (cx-x9o52, REUSE.md slice 3) PageInfoPanel is a NativePopupPanel: placement through PopupPlacement, the host's grow/shrink motion and dismiss rules (click outside with the omnibar button kept open, Escape, key loss, app inactive, window move/resize/close). The hand placement, PopupFade and the mouse-down reopen guard go. The card no longer draws its own shadow band (PopupHostView draws the one popup shadow). PageInfoRowView is a PopupRowView. NativePopupPanel joins the child-window allow list (the bookmark bubble tripped it in DEBUG). PageInfoMotionTests (a unit test of PopupFade on PageInfoPanel) is deleted under the no-unit-test rule; the live check covers the bubble. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(cmux-next): a new bookmark's name follows the page title that arrives after the star (bookmark bubble) The star saves the bookmark at once, often before the page has a title, so the bubble showed an empty name. While the bubble shows, the page title (observed) fills the name until the user edits it; close saves it as before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: bookmark bubble live check covers the late page title, Page Info on the shared host (captures) and a real click on the star (--real-click) (cx-x9o52) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * review: Page Info rows keep their inset before the accessory (cx-x9o52) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-tui: the documented customize template installs as is in a real daemon (cx-swovq.1) Behavior test through the daemon socket: the docs/cmux-next/sidebar-customization folder, copied unchanged into the customize folder, is listed installed, enabled and sandboxed with its workspace:read grant. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * CmuxNext: Codex-like status icons by default, plus static status glyphs (#20410) * test(cmux-next): static status glyphs are apart by shape, crisp, still and theme-colored Red: there is no static status set and no ui.status-glyphs contact sheet yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * CmuxNext: static status glyphs for the Static status mode Leo 2026-10-14: six custom static glyphs for op-next-motion's Static status mode (Reduce Motion): a hollow ring (idle), a half-filled ring (working), an empty speech bubble (waiting on you), a shield (needs permission or a sign-in), a check disc (done) and a ! diamond (error). Told apart by shape alone at 10 pt, tinted by their theme role, never animated. One source, schemas/status-glyphs/static-status.json: a 20-unit master (10 pt at 2x) and 10 px pixel art for 1x. gen-status-glyphs.py writes the Swift StaticStatusGlyph (CGPaths) and the web statusGlyphs.generated.ts (SVG paths); --check runs in bun run check. StatusMark.vector draws a glyph, StatusIconSet.still (Static) maps every state to them, and StaticStatusGlyph(state)?.plan is the hook for the Static setting. The gallery sheet ui.status-glyphs shows each live, in a row, and its 1x and 2x pixels, plain and on a wallpaper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cmux-next): status glyphs at 2x probe their straight edges A share-of-soft-pixels cap cannot tell a curve from blur (a ring half a pixel off reads the same), and the idle ring's 63% tripped it. At 2x the test now probes pixels on each glyph's straight edges, which are whole only when the drawing sits on the pixel grid; half a pixel off, every glyph leaves some half covered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * CmuxNext: the Static set keeps a known-progress ring Review: StatusIconSet.still drew the working glyph for an agent that reports progress, dropping how far along it is. That ring never moves, so the Static set keeps it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: Codex-like status set draws grey work, blue done, red error Red: StatusIconSet has no codex case, there is no spinner or unread tint, and the web sheet has no codex glyphs (shieldLine, info, dot). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: Codex-like status icon set A Debug set for the status indicators that follows Codex's sidebar: Lawrence's shared spinner re-tinted to the foreground at 66% (a still ring under Reduce Motion or Static), Codex's blue (#3982F5) done dot, muted line bubble and shield for waiting, and a red line i for error. The schema gains a codex set that the generator emits for the web copy, and the gallery sheet gains codex variants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: Codex-like still loader stays a stopped arc Red: with motion off, the Codex-like set turned every arc into the still work ring, so a busy page or command loader read as agent work. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: Codex-like still ring only for agent work Only working without progress becomes the still ring when nothing animates; a busy loader keeps its stopped arc. The glyph generator now checks that every set picks a glyph for every state from its own set or the static set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: Codex blue as a theme token; document the idle/info 2x floor Codex's blue moves to --cmux-status-unread in webviews/src/theme, so the generated glyph table holds no blue literal (check-system-blue). At 2x the still work ring and the error ring differ only by the i, 16 whole pixels, and they differ in color too; the test says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: the Codex-like status icons are the default set Red: the default is still symbols. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: Codex-like status icons are the default Leo 2026-10-14, a team decision: the grey spinner (a still ring under Reduce Motion), Codex's blue done dot, muted line glyphs and a red line i for errors, for everyone. Symbols, Static and the other sets stay as Debug alternates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: Codex blue as a theme constant, not a page stylesheet color A literal color in a page stylesheet trips the theme-colors ratchet, so the blue moves to STATUS_UNREAD in webviews/src/theme/statusColors.ts and the generator writes it as the fallback of --cmux-status-unread. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at a8608cf74601 - vp fmt webviews/src/agent-session/acpmux/modelPickerLayout.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38521104749 * test: the status glyph and count badge center on the row's middle, one line or two Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: the status glyph centers on the row's middle beside the badge; a 6 pt done dot The glyph sat on the title's capitals, so on a two-line row it rode above the count badge (Leo 2026-10-14: "why is it superscript?"). It now centers on the line the badge and the other trailing marks share. The Codex-like done dot grows from 4 pt to 6 pt ("too tiny"). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: a status glyph at 3x or 5x draws the master, not the 1x pixel art blown up Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: only a 10 px status glyph takes the 1x pixel art; 3x and 5x draw the master Review on #20410: the odd-multiple-of-10 rule sent 30 and 50 px glyphs to the 10-unit art, blown up in coarse blocks. 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> * cmux-next: pin CEF cef-154.0.28-cmux.25 (cmux.24 + WebAuthn hybrid fix with a PIN key attached; fork API 23) (cx-d0d.97) * cmux-next: remote browser host linked against CEF cmux.25 (step beeef9df6b98) (cx-d0d.97) * passkeys-roaming-e2e: --typing smoke with trusted key events (cx-d0d.97) * passkeys-matrix-e2e: a timed-out addVirtualAuthenticator reply counts as sent (the socket deadline is now 6 s) (cx-d0d.97) * chrome-promos-live: ignore Chromium's status bubble (<= 30 pt at the page window's bottom-left), logged; any other new window still fails (cx-d0d.97) * passkeys matrix + promo check review fixes: unconfirmed authenticator rows that pass without one are NOTE; promo count uses only new windows (cx-d0d.97) * CmuxNext: no gray ring trails a split (#20500) * CmuxNext: test that a split's focus ring lands on the new pane in the apply that mounts it (red) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * CmuxNext: no gray ring trails a split The split's daemon tree mounted the new pane and the layout view drew the focus ring around the old pane at its new frame; the focus coordinator landed focus on the new pane in the same apply, but the view mirrored it only on its observation's next turn. Sync the view again after the topology so the frame rings the pane focus landed on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * CmuxNext: one line for the split focus sync (app size cap) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * CmuxNext: split focus ring test compiles (closure, not a key path, in #expect) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 44de1da3d855 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38532789052 * chore(next): PR autofix at c7d76dbf2fb3 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38541548095 --------- 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: tabs take the max width while the strip has room Lawrence 2026-10-14 asked for max width: "default widths for new tabs still have the same max width and try to grow as much as possible (ofc it can shrink tho)". This reverses the fit-to-title rule from 7f9fc1ed53f2 (merged into feat-cmux-next with 3ef63bc02144; that titlebar commit carries Lawrence's name as author but was hq-1d's worker change). Every unpinned tab takes the max width (Metrics.tabMaxWidth) while the strip has room; a crowded strip shrinks all tabs evenly down to the minimum, then scrolls. The tabs.minFitTabWidth tunable, TabLayoutItem.naturalWidth, TabStripMetrics.naturalTabWidth and the title measurement in TabStripView+Layout go with it. That measurement ran NSString.size for every tab title on every layoutItems call, on the main thread, also during strip springs; it no longer runs. Separators, the traffic-light center line, the + position and the reserved close-button room are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-next: debug.pane_chrome reports each tab's slot tab_slots ([x, width] per tab in strip content points) and tab_available, so a proof can read the tab width rule without pixels. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * free tier: a Keychain error never mints a new App Attest key; no key-id read where App Attest cannot work (cx-5bxp7 P3-A, P3-B) AttestKeyStore load/save/delete throw on a store error (load: nil only for errSecItemNotFound). The provider answers failed when the key id cannot be read or saved, so it never generates or attests a new key then (a new key spends the device's monthly grant), and it saves a new key id before attesting it. The register line skips the Keychain read when App Attest is not supported (no prompt in an ad-hoc DEV build). * acpmux start mode: review fixes (cx-wx93i) - Settings file read as JSONC like the app (comments, trailing commas); a file acpmux cannot read or parse, or an unknown value, gives the harness's own mode, never full access. Classic cmux.json only when cmux-next.json does not exist. The setting is resolved before the config write lock. - A remote chain never starts Claude in bypassPermissions (a transfer keeps the mode): the spawn mode and args fall back to default. - A handoff target starts in full access only from a full-access source and never with narrower. - A profile with its own skip flag keeps its mode on respawn; config reload picks up defaultPermissionMode; acpmux reads mcp.enabled only nested, as the CLI does. - Tests: the fake Claude refuses bypassPermissions without the launch flag; the settings-file test is its own binary (it sets CMUX_NEXT_CONFIG_FILE) and covers JSONC, no key, an unparseable file and an unknown value. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-next: Ctrl and Option holds show their shortcut hints too; hint chips are Liquid Glass in the theme's tint (cx-w9f3s) Lawrence 2026-10-14: "we need cmd hold tab to work for hold ctrl as well, maybe option if there are relevant shortcuts, we also need it to be liquid glassy". - A hold arms on any set of Cmd, Ctrl and Option (with or without Shift), not only bare Cmd or Ctrl. The overlay shows each target's binding from the resolved table (keybindings.json and cmux.json overrides included) whose modifiers contain the held set; a hold with no matching binding shows no chip. Same 300 ms delay, key-down suppression and placement. - ShortcutHintChipView: a full Liquid Glass capsule (GlassPanelView -> NSGlassEffectView) tinted with the theme's glass tint, text in the theme's primary text color. macOS 14/15: the same panel's blur, tint and hairline. Reduce Transparency: the theme's opaque popup surface, a device-pixel hairline and the soft shadow. - debug.shortcut_hints takes opt, shift and +-joined sets (ctrl+opt). CmuxNextApp line count unchanged (113450). * cmux-next: hold hints review fixes: only Cmd/Ctrl chords get a chip, a click suppresses, stronger chip tint (cx-w9f3s) Review of d77c9ec3f078 (P2s): a chip only for bindings KeyRouter runs (KeyRouter.isChord: a pure-Option binding types a character); a mouse-down while a modifier is held hides and suppresses the hints like a key-down (Option-click / Option-drag in a terminal); the chip's glass tint is the theme tint at 1.5x alpha so 10 pt text keeps its contrast over bright content. P3s: no double Borders.color, no continuous curve against a circular shadow path, debug release only alone. plans/cmux-next/keybindings.md describes the Cmd/Ctrl/Option holds and the glass chip. CmuxNextApp line count unchanged (113450). * review: Page Info keeps its side on a new page; any NativePopupPanel passes the child-window policy; the bookmark name follows only the bookmarked URL's title; live check presses the star once (cx-x9o52) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * free tier e2e runner: never kill; the app quits through its control socket (action.run quitEndSessions, peer-checked), the daemon by acpmux daemon shutdown (cx-ds09i T1/T2) * New Tab: never block launch on a slow WebKit setup (#20484) * test: launch prewarm does not wait for a stalled WebKit NewTabSparePool.makeSpare becomes a seam (no behavior change) so the test puts a WebKit that blocks for 2 s in its place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: the New Tab spare never makes a web view before WebKit's setup answers Launch no longer makes the spare. prewarm() asks the sandbox broker off the main thread (WebKitSetupGate, 2 s timeout); the first spare follows the first window's first frame once it answered, and a spare that held the main thread over 1 s marks WebKit slow for the run. A wedged sandboxd (cua-2: WKWebView load blocked >10 s in sandbox_extension_issue) gets no spare. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * backend: behavior test for a Chief brain on Durable Objects answering in Cloud conversations (red) The owner turns the chief's brain on over HTTP; a person writes in the chief's DM and in a channel over POST /v1/ops; the chief answers as itself (typing, then the message, on a member's socket), in the channel message's thread, with a run reference whose run and steps only members read. Fails before the brain exists (the routes answer 404). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * backend: ChiefBrainDO, a Chief that answers in Cloud conversations on pi-durable with OptChat memory One ChiefBrainDO per chief (agent id): the optchat-lab impl/pi-durable turn loop ported into the API Worker (agents SDK PiHarness, pi-durable, the OptChat Rust core as wasm over DO SQLite, background compactor, one fresh pi session per turn). - MuxDO forwards each committed mux.wake (DM, mention, reply, wake_policy all) to the brain where CHIEF_BRAIN_ENABLED is 1 (staging, development; not production). - The brain pulls the conversation's new messages as the chief, runs one turn per conversation batch, and posts one message.send as the chief (in a channel, in the thread of the message it answers) with a work part naming the run; NO_REPLY posts nothing; then mux.ack. A refused post (loop guard agent_rate) retries under a new key; no answer retries under the same key. - Realtime: ConversationDO.agentFrame sends the chief's typing (same gate as a socket) and conversation-run-changed frames (TRAJECTORIES Run/Step JSON) to member sockets, never stored. - HTTP: GET/PUT /v1/chiefs/<agent>/brain (owner: enabled, model, role, personality, instructions), GET /v1/chiefs/<agent>/runs[?conversation=] and /runs/<run> (members only). - Model: fake by default; Workers AI through the Worker's AI binding where CHIEF_BRAIN_MODEL names one (staging: @cf/moonshotai/kimi-k2.7-code). The team subrouter is tailnet-only. - DO migration v15 (ChiefBrainDO). firstName moves to its own file (ConversationDO size limit). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * backend: Chief brain review fixes: one memory per conversation, live-chief and placement checks, bounded turns Review (subagent, REVISE) findings fixed: - P0: one OptChat memory for every conversation could carry one conversation's messages into another's reply and run steps. Now one brain per conversation (ChiefBrainDO name <agent>/<conversation>); the chief's settings object (name <agent>) holds the owner's settings. - P1: every turn and post asks the settings object: enabled, the chief record live (archived chiefs stop at once) and not placed on a paired server, under a daily turn limit (CHIEF_BRAIN_DAILY_TURNS, default 300). MuxDO forwards only for brain cloud. - P1: the reply answers the newest woken message (rows carry woke), not the newest message. - P1: the compactor wait is bounded (60 s, then the view as it is); a turn stops after 10 min. - P2: post retries reschedule through the job outcome; no concurrent post of one run; a drain that ends re-checks the queue; the test fires the brain's alarm. - P3: run reads need ?conversation= and check membership before touching a brain; NO_REPLY match ignores punctuation and case; replies are clipped under the text limit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * backend: Chief brain: a transient owner-record error retries later instead of skipping; a failing drain backs off Re-review (APPROVE) follow-ups 1 and 2: owner.unreachable leaves the woken messages queued and schedules a drain job; a drain that threw schedules its next try instead of looping at once. Reply helpers move to brain-text.ts (size limit). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(cmux-next): macOS 27 base reds: CLI parity person-only refusal, cancelled load keeps the new address (#20373) * test(cmux-next): a New Tab load that cancels a loading page shows no preview of it (red) Replays WebKit's callbacks in the order the macOS 27 lane hit (NewTabHoverPreviewTests:50 on #20342): the New Tab address arrives, then the cancellation of the load it replaced puts the old address back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cmux-next): a cancelled load keeps the address of the load that replaced it When a new load replaced one still provisional, WebKit reports the new address first (the URL KVO, or the old load's late start reading webView.url) and then the old load's cancellation. The state machine reverted the address to the last committed one on that cancellation, so until the new load started the tab showed the old address: the hover card pictured the old page over the New Tab page (NewTabHoverPreviewTests:50 on the macOS 27 lane) and the omnibar flickered back. A cancellation whose failing URL is not the address shown now keeps it, and never restores the old error page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cmux-next): CLI parity expects the person-only refusal browserProfile.delete is person-only since 596dd7a (cx-dwg.10): it keeps its CLI verb but the control socket refuses every run. CLISurfaceParityTests:48 still expected the run to reach its handler. A person-only action now has to answer exactly the person-only refusal (code unavailable, the personOnly reason) and run nothing; every other CLI action still has to run its own handler. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cmux-next): keep a replaced load's address by its start, not its failing URL Review: comparing the cancellation's failing URL with the address shown broke CEF, where the start carries the URL cmux asked for and the failing URL is Chromium's (after a redirect): a redirected load aborted as a download kept its address over the committed page. The address now stays only when it moved off the cancelled load's own start/redirect address, which in CEF it never does before commit. WebKit can also report an old load's start after a newer one was asked for, reading the newer address; the tab now tells the state machine which navigation it asked for last (`requested`), and the start of an older one is ignored, so its cancellation changes nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cmux-next): drop the requested-navigation rule; test the real order Re-review: the late-start order cannot revert in practice (the New Tab URL KVO arrives first while nothing is provisional and moves the committed address too), so the test half for it could not fail, and ignoring an older start could leave a real load untracked when the newer request is refused by policy. Removed both. The test now commits a page first, so the cancellation would put that page back on the base. A cancellation WebKit already moved back to the committed page is not a replacement, so a cancelled retry still shows its error page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at c26ac6910070 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38530564433 * chore(next): PR autofix at b55536721932 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38542307133 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * optchat-chief: Chiefs react to other Chiefs, with a per-thread loop guard In local side conversations another Chief's message now wakes a Chief (never its own; agents that are not Chiefs still never do). The loop guard is computed from the conversation itself, so every host and a page agree with no shared state: - cmux_chief::rules::chief_streak: consecutive Chief messages at a thread's tail (or the top level) since a person wrote; at CHIEF_TURN_BUDGET (6) no Chief answers a Chief there until a person posts. - cmux_chief::rules::owner_agent_streak mirrors the owner's own guard (MAX_AGENT_TURNS agent messages per conversation): a Chief stays quiet instead of running a turn whose reply the owner would refuse. That guard is lower than 6 today, so it is the one that stops Chiefs first. - Both are taken at the newest message read, and a pause is logged ("Chiefs paused ..."). Side messages now carry the author's name in their label on the local source, the Channels prompt says acks need nothing (NO_REPLY), and only a person answers a pending approval. WakeRule replaces wakes_as' loose arguments; wakes() is unchanged. Test: channels_daemon.rs two_chiefs_wake_each_other_stop_at_the_budget_and _a_person_resumes_them, two real brains on a real headless daemon. Red with Chief wakes off (the Chiefs never reach the budget), green with them (twice). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * optchat-chief: loop guard review fixes - the thread streak follows the thread a reply goes into (a top-level message's own thread), and retracted Chief messages still count; - the owner-streak mirror stops only at a known person's text, skips authors who left, and resets at an answered question, as the owner does; - side labels say whether the author is a person, a Chief or an agent. Not done (P2 test gap): a behavior case where the 6-message thread budget, not the owner's 4-per-conversation guard, stops the Chiefs. Any person's text that resets the owner's streak also wakes the Chiefs in its own thread, so with the owner guard as it is the thread budget cannot bind first. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-next groups: a right-click on a group header or tab group chip opens the group editor Lawrence 2026-10-14: "im talking about the rename + color things ... looks like the image in chrome, we had it at some point". The editor bubble (name field, color dots, group rows; cx-rcby, 3ca7be38252e) is still there, but #19019 (31db8ce85faf) pointed the header's more button and its right-click at the plain menu. #20442 put the more button back on the editor; this puts the right-click back too, and does the same for a tab group chip, whose right-click showed the app's menu (the editor opened only on a press and hold). The full menu stays one row away (More Group Actions… in the sidebar editor) and on Option-right-click of a tab group chip. scripts/cmux-next/group-editor-live.py proves it with real right-clicks (cua-driver): the editor opens with its name field, color dots and rows, a typed name with Return renames the group, Escape closes it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-tui: socket test for a workspace move during New Workspace losing tab identity (cx-95uaf) The app's New Workspace: create-workspace, then the first terminal with a reserved terminal id, while the app places the listed workspace with move-workspace. A debug-only gate (CMUX_TUI_TEST_CREATE_BIND_GATE, scoped to one workspace key) holds that create between its surface insert and its pane binding until the move is done. Fails on this commit: the create reports 'tab slot N has no durable identity' and later closes fail the same way. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-tui: a created tab keeps its durable identity until it is placed (cx-95uaf) State::rebuild_resource_indexes rebuilt tab identity only from the tabs the workspace/screen walk reached. A created terminal is inserted (its identity registered) before it is bound to a pane, and a move-workspace (or any other index rebuild) in that window dropped the identity. The binding then placed a slot with no durable identity, the create failed ('tab slot N has no durable identity'), and every later full projection (close-workspace too) failed the same way. On the app this was the second New Workspace: the sidebar places the listed workspace while its first terminal is still being created. - Placing a live surface in a pane registers the identity it was created with when the topology has none (State::register_placed_tab_identity) at every terminal placement site: hosted bind, local create, effect tabs and panes. - The rebuild carries the identity of a live surface that is in no pane. - A placed slot whose record is missing takes its live surface's identity (State::tab_identity_of) in the full rebuild and in the scoped reindex, so one lost record cannot wedge later mutations. A slot with neither still fails loudly. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Channels view in the app: the page may play video from the daemon's loopback listener (PageCSP media-src) The Channels page plays a video attachment from the daemon's own GET /attachments/<token> (ws-attachments-v1). The app's page scheme sent no media-src, so the web view blocked it. PageCSP gains media sources; the home-channels descriptor allows http://127.0.0.1:* (the daemon listener). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * group-editor-live: find the group by its member row, the tab chip by the strip, the rename by the reopened editor Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * group-editor-live: real window-server right-click first, the in-app event path as fallback; strip rects are arrays Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-next tab groups: the group editor's More row shows the group's full menu under the chip (review P2) A right-click on a tab group chip now opens the editor, whose rows are New Tab, Ungroup, Close, Move to New Window and Save. The rest of the group's menu (icon, moves to a split, column or workspace, collapse, configured rows) stays one row away: More (the strip's existing paneToolbar.more string, 21 locales) pops the same menu under the chip, as the sidebar editor's More Group Actions row does. Option-right-click still shows it directly. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux NEXT: Ghostty unfocused-split-opacity and unfocused-split-fill dim unfocused panes (cx-6so.35) (#20533) * test(cmux-next): Ghostty unfocused-split keys dim unfocused panes (red, cx-6so.35) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: honor Ghostty unfocused-split-opacity and unfocused-split-fill (cx-6so.35) The Ghostty config's unfocused-split-opacity dims every unfocused pane by 1 - value while a screen has more than one pane, over the unfocused-split-fill color (the theme background when unset), as Ghostty.app draws over an unfocused split. cmux loads unfocused-split-opacity = 1 before the user's files, so nothing dims unless the config sets it. Both keys leave GhosttyKeySupport.unsupported, so Settings' diagnostics stop listing them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 918e7d3d8115 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38541840504 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * cmux-tui: behavior test, a large legacy registry gets no full VACUUM while its owner serves (cx-4bd8m) Red commit: drives the owner binary on a registry in the shape a build without journal maintenance leaves (auto_vacuum off, no marker). Adds a debug-only CMUX_TUI_TEST_JOURNAL_MAINTENANCE_INTERVAL_MS override. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * ci(nightly): signed app downloadable before the notarization wait (#20551) * test(nightly): require a signed app artifact before the notarization wait Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(nightly): upload the signed app before the notarization wait Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 3dd82a78f6aa - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38546488020 * ci(nightly): signed-app upload never blocks notarization; only the import pause skips it 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> * docs: gh-merge-green is optional on feat-cmux-next (#20567) A plain gh pr merge on the exact head is fine under the branch's rules; the batch queue's own --land still uses the helper. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-tui: no full VACUUM of a large legacy registry while its owner serves (cx-4bd8m) Austin's cmux NEXT registry grew to 10.7 GB in 8.5 h under a build without journal maintenance (auto_vacuum off, 4.47M journal rows). The maintenance worker's first step converts such a registry with PRAGMA auto_vacuum= INCREMENTAL; VACUUM; while it holds the registry: on that file every client of the owner would read "not connected" for minutes after the next update. Convert only while live data is at most 64 MiB. Above it the conversion is deferred (one diagnostic per owner); sealing still runs and deletes archived rows, and SQLite reuses their pages, so the file stops growing once sealing keeps up. It does not shrink: sealed segments stay in the registry, so giving the free pages back needs a compaction that does not hold the registry (follow-up bead). Debug-only overrides for the interval and the bound keep the behavior test small. Red/green: legacy_registry_maintenance. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-next: a local merge commit reuses the fetched same-tree acpmux (#20534) * test: a local merge commit reuses the fetched same-tree acpmux (red) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: a local merge commit reuses the fetched same-tree acpmux reload.sh on a developer Mac stopped with "no cached acpmux" for any commit no workflow published (a local integration of feat-cmux-next and PRs), even though the tree fetch had just put that tree's checksum-verified acpmux in place. build-acpmux.sh now seeds its commit cache from it when it has every requested architecture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 7be0d24457e0 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38542356066 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * webviews: popups draw one hairline, not two (#20461) * webviews: test that every popup draws one hairline edge Red: --acpmux-popup-elevation stacks a composer-edge hairline on the one already in --ui-popup-shadow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: popups draw one hairline, not two --acpmux-popup-elevation stacked a 0.5px composer-edge hairline on the hairline already in --ui-popup-shadow. It now is the shared popup shadow, so every menu and popover has one edge. Increase Contrast colors that one edge in the popup's ink, which the composer-edge hairline did before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 1cc661608ff2 - vp fmt webviews/src/agent-session/acpmux/modelPickerLayout.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38520503432 * webviews: test that popups draw no border over the hairline and clear it under borders none Red: the pages' .ui-popup and the chat find bar draw a border over the --ui-popup-shadow hairline, and appearance.borders none leaves the hairline drawn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: one rim on page popups and the find bar; borders none clears it The pages' .ui-popup drew a 1px border over the --ui-popup-shadow hairline; it now draws no border. The chat find bar keeps its border (its focus cue) and casts --ui-popup-drop, the shadow without the hairline. appearance.borders none sets --ui-popup-edge transparent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: format modelPickerLayout.ts (base fmt red) Keeps cmux-autofix's vp fmt of the base file, which the base merge replaced with the unformatted copy; react-apps-check fails on it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: the Channels palette draws one rim; test follows the summary card Red after the base merge: the new Channels palette (.lg-palette, ledger.css) drew a 1px border over the --ui-popup-shadow hairline, and the summary popover became .acpmux-summary-card. The palette now draws no border. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 54dd59d45f3d - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38543983469 --------- 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: name the owner of ControlWorkspaceInfoCache's publish hop (clears the concurrency red) (#20485) * fix: clear two feat-cmux-next check reds tree-publish-v2's stub uploader takes --conflict-exit-code, which the tree publisher now passes (af8b68ebc8f9e). PaneAnimationsSetting carries the namespace-type justification its ratcheted sibling settings have. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep PaneAnimationsSetting internal to CmuxNextSettings Nothing outside the module uses it, so it needs no namespace-type allow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at c2bdb982abed - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38530638157 * fix: name the owner of ControlWorkspaceInfoCache's publish hop check-concurrency flags the unowned Task d0a62bef9b28c added; it is a one-shot main-actor hop that holds the cache weakly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 7cf07e3731b0 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38544346314 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * CmuxNext: one-line sidebar rows by default; notification text in the hover card (#20483) * test: a notified compact row stays one line; its text reads in the hover card Leo 2026-10-14: "why is it even 2 lines in the first place?" Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: compact sidebar rows are one line by default; the notification text moves to the hover card sidebar.workspaceRow.notification is now off by default (Leo 2026-10-14). #19862 turned it on for classic parity; the setting stays as an opt-in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: regenerate the settings schema hash for the notification default Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: regenerate the settings schema hash on the merged base Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: take base's settings exports after the base merge (regenerated next) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: regenerate the settings exports on the merged base Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: refresh the defaults snapshot on the merged base Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: the hover card template lists the notification fact; comments say where the text shows 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: 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>
…nce, failed with why, lost) (#20362) * test(acpmux): every session summary carries its lane (cx-v9k2f) Working since its turn started, input, idle since its last turn ended, failed with the error text (a restart's outcome_unknown included), lost; peer sessions too. Red: summaries have no lane yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(acpmux): every session summary carries its lane (cx-v9k2f) lane = {state, since, error}, decided once next to indicator from the summary's own fields, for local and peer sessions (re-decided when a peer goes unreachable): working since its turn started, input, idle since its last turn ended, failed with that turn's error text, lost. A restart's outcome_unknown now records lastTurn, so that session reads failed with why, not idle. The restart test moves to its own file (hub_integration.rs was at its size cap). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): review follow-ups for the session lane (cx-v9k2f) Waiting reads inputSince (when the session began waiting), so a draft save, a later record or a second ask no longer moves it. A session lost mid-turn is lost since it went, without the previous turn's error. outcome_unknown records lastTurn before its record goes out, as every turn end does, and lastTurn keeps detail. An idle session with turns but no lastTurn falls back to updatedAt. The schema documents lane and inputSince; the restart test module sits in order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 5099ccd - cargo clippy --fix cmux-tui/crates/acpmux/src/hub/turns.rs - cargo clippy --fix cmux-tui/crates/acpmux/src/hub/views.rs Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38495693983 * acpmux: lastTurn keys live beside the lane (turns.rs does not grow) (cx-v9k2f) Adding `detail` wrapped the key list and grew hub/turns.rs to 1064 lines, which the god-file check refuses (1062 on base, over the 1000 limit). The list becomes views::LAST_TURN_KEYS, so turns.rs stays at 1062. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: the typeahead GUI gate in the repo (cx-wb5.78): typeahead-gate.sh runs split, newterm, split with a slow host, new-tab and cold new-tab proofs on the GUI host and fails on a lost key; typeahead-compare.py counts keys sent by terminal id; creation-hold-proof.py is superseded (named in the gate header) * acpmux: behavior test, a new chat starts in full access; the person's switch back is saved; every chat gets the cmux MCP server (red) (cx-wx93i) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * acpmux: new chats start in the harness's full-access mode; the person can switch back and the pick is saved; every agent chat gets the cmux MCP server (cx-wx93i) Lawrence 2026-10-14: "give every agent chat the cmux MCP tools by default, but by default all chats should be in skip permission mode, that user can switch back". - hub/start_mode.rs: once the harness is up, acpmux sets the start mode with session/set_mode (soft, like the start model): the person's saved pick (config.json defaults.<profile>.mode), else acpmux config.json defaultPermissionMode, else the cmux setting agents.defaultPermissionMode (fullAccess | standard | ask), else full access: Claude Code bypassPermissions, the Codex bridge agent-full-access (approval never + danger-full-access), or a harness's yolo/full-access mode. Not for Web (remote-origin) chats, presets, folder profiles, adopted chats, or a profile that pins its mode in argv. - A mode pick from the person's app pane (session/set_mode or the mode config option) is saved; Plan is a toggle and is not saved; a pick equal to what the setting gives removes the saved mode. Only the person may clear a saved mode (_acpmux/defaults clear), as for a stored policy. - claude_stdio: offers bypassPermissions ("Full access") and launches Claude Code with --allow-dangerously-skip-permissions when it accepts it (not root, or IS_SANDBOX=1); remote chains strip the flag. - agent_tools: the cmux MCP server (cmux mcp serve) is on for every agent chat unless the settings file says mcp.enabled false; the settings file is now the app's cmux-next.json (user_settings.rs), not classic cmux.json. - cmux mcp serve: on when CMUX_AGENT_SESSION is set and mcp.enabled is not set; any other client still needs mcp.enabled true. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(cmux-next): Page Info on the shared native popup host; its rows on the shared popup row (cx-x9o52, REUSE.md slice 3) PageInfoPanel is a NativePopupPanel: placement through PopupPlacement, the host's grow/shrink motion and dismiss rules (click outside with the omnibar button kept open, Escape, key loss, app inactive, window move/resize/close). The hand placement, PopupFade and the mouse-down reopen guard go. The card no longer draws its own shadow band (PopupHostView draws the one popup shadow). PageInfoRowView is a PopupRowView. NativePopupPanel joins the child-window allow list (the bookmark bubble tripped it in DEBUG). PageInfoMotionTests (a unit test of PopupFade on PageInfoPanel) is deleted under the no-unit-test rule; the live check covers the bubble. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(cmux-next): a new bookmark's name follows the page title that arrives after the star (bookmark bubble) The star saves the bookmark at once, often before the page has a title, so the bubble showed an empty name. While the bubble shows, the page title (observed) fills the name until the user edits it; close saves it as before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: bookmark bubble live check covers the late page title, Page Info on the shared host (captures) and a real click on the star (--real-click) (cx-x9o52) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * review: Page Info rows keep their inset before the accessory (cx-x9o52) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-next: pin CEF cef-154.0.28-cmux.25 (cmux.24 + WebAuthn hybrid fix with a PIN key attached; fork API 23) (cx-d0d.97) * cmux-next: remote browser host linked against CEF cmux.25 (step beeef9df6b98) (cx-d0d.97) * passkeys-roaming-e2e: --typing smoke with trusted key events (cx-d0d.97) * passkeys-matrix-e2e: a timed-out addVirtualAuthenticator reply counts as sent (the socket deadline is now 6 s) (cx-d0d.97) * chrome-promos-live: ignore Chromium's status bubble (<= 30 pt at the page window's bottom-left), logged; any other new window still fails (cx-d0d.97) * passkeys matrix + promo check review fixes: unconfirmed authenticator rows that pass without one are NOTE; promo count uses only new windows (cx-d0d.97) * free tier: a Keychain error never mints a new App Attest key; no key-id read where App Attest cannot work (cx-5bxp7 P3-A, P3-B) AttestKeyStore load/save/delete throw on a store error (load: nil only for errSecItemNotFound). The provider answers failed when the key id cannot be read or saved, so it never generates or attests a new key then (a new key spends the device's monthly grant), and it saves a new key id before attesting it. The register line skips the Keychain read when App Attest is not supported (no prompt in an ad-hoc DEV build). * acpmux start mode: review fixes (cx-wx93i) - Settings file read as JSONC like the app (comments, trailing commas); a file acpmux cannot read or parse, or an unknown value, gives the harness's own mode, never full access. Classic cmux.json only when cmux-next.json does not exist. The setting is resolved before the config write lock. - A remote chain never starts Claude in bypassPermissions (a transfer keeps the mode): the spawn mode and args fall back to default. - A handoff target starts in full access only from a full-access source and never with narrower. - A profile with its own skip flag keeps its mode on respawn; config reload picks up defaultPermissionMode; acpmux reads mcp.enabled only nested, as the CLI does. - Tests: the fake Claude refuses bypassPermissions without the launch flag; the settings-file test is its own binary (it sets CMUX_NEXT_CONFIG_FILE) and covers JSONC, no key, an unparseable file and an unknown value. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-next: Ctrl and Option holds show their shortcut hints too; hint chips are Liquid Glass in the theme's tint (cx-w9f3s) Lawrence 2026-10-14: "we need cmd hold tab to work for hold ctrl as well, maybe option if there are relevant shortcuts, we also need it to be liquid glassy". - A hold arms on any set of Cmd, Ctrl and Option (with or without Shift), not only bare Cmd or Ctrl. The overlay shows each target's binding from the resolved table (keybindings.json and cmux.json overrides included) whose modifiers contain the held set; a hold with no matching binding shows no chip. Same 300 ms delay, key-down suppression and placement. - ShortcutHintChipView: a full Liquid Glass capsule (GlassPanelView -> NSGlassEffectView) tinted with the theme's glass tint, text in the theme's primary text color. macOS 14/15: the same panel's blur, tint and hairline. Reduce Transparency: the theme's opaque popup surface, a device-pixel hairline and the soft shadow. - debug.shortcut_hints takes opt, shift and +-joined sets (ctrl+opt). CmuxNextApp line count unchanged (113450). * cmux-next: hold hints review fixes: only Cmd/Ctrl chords get a chip, a click suppresses, stronger chip tint (cx-w9f3s) Review of d77c9ec (P2s): a chip only for bindings KeyRouter runs (KeyRouter.isChord: a pure-Option binding types a character); a mouse-down while a modifier is held hides and suppresses the hints like a key-down (Option-click / Option-drag in a terminal); the chip's glass tint is the theme tint at 1.5x alpha so 10 pt text keeps its contrast over bright content. P3s: no double Borders.color, no continuous curve against a circular shadow path, debug release only alone. plans/cmux-next/keybindings.md describes the Cmd/Ctrl/Option holds and the glass chip. CmuxNextApp line count unchanged (113450). * review: Page Info keeps its side on a new page; any NativePopupPanel passes the child-window policy; the bookmark name follows only the bookmarked URL's title; live check presses the star once (cx-x9o52) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * free tier e2e runner: never kill; the app quits through its control socket (action.run quitEndSessions, peer-checked), the daemon by acpmux daemon shutdown (cx-ds09i T1/T2) * cmux-next groups: a right-click on a group header or tab group chip opens the group editor Lawrence 2026-10-14: "im talking about the rename + color things ... looks like the image in chrome, we had it at some point". The editor bubble (name field, color dots, group rows; cx-rcby, 3ca7be3) is still there, but #19019 (31db8ce) pointed the header's more button and its right-click at the plain menu. #20442 put the more button back on the editor; this puts the right-click back too, and does the same for a tab group chip, whose right-click showed the app's menu (the editor opened only on a press and hold). The full menu stays one row away (More Group Actions… in the sidebar editor) and on Option-right-click of a tab group chip. scripts/cmux-next/group-editor-live.py proves it with real right-clicks (cua-driver): the editor opens with its name field, color dots and rows, a typed name with Return renames the group, Escape closes it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Channels view in the app: the page may play video from the daemon's loopback listener (PageCSP media-src) The Channels page plays a video attachment from the daemon's own GET /attachments/<token> (ws-attachments-v1). The app's page scheme sent no media-src, so the web view blocked it. PageCSP gains media sources; the home-channels descriptor allows http://127.0.0.1:* (the daemon listener). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * group-editor-live: find the group by its member row, the tab chip by the strip, the rename by the reopened editor Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * group-editor-live: real window-server right-click first, the in-app event path as fallback; strip rects are arrays Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-next tab groups: the group editor's More row shows the group's full menu under the chip (review P2) A right-click on a tab group chip now opens the editor, whose rows are New Tab, Ungroup, Close, Move to New Window and Save. The rest of the group's menu (icon, moves to a split, column or workspace, collapse, configured rows) stays one row away: More (the strip's existing paneToolbar.more string, 21 locales) pops the same menu under the chip, as the sidebar editor's More Group Actions row does. Option-right-click still shows it directly. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-tui: behavior test, a large legacy registry gets no full VACUUM while its owner serves (cx-4bd8m) Red commit: drives the owner binary on a registry in the shape a build without journal maintenance leaves (auto_vacuum off, no marker). Adds a debug-only CMUX_TUI_TEST_JOURNAL_MAINTENANCE_INTERVAL_MS override. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: gh-merge-green is optional on feat-cmux-next (#20567) A plain gh pr merge on the exact head is fine under the branch's rules; the batch queue's own --land still uses the helper. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-tui: no full VACUUM of a large legacy registry while its owner serves (cx-4bd8m) Austin's cmux NEXT registry grew to 10.7 GB in 8.5 h under a build without journal maintenance (auto_vacuum off, 4.47M journal rows). The maintenance worker's first step converts such a registry with PRAGMA auto_vacuum= INCREMENTAL; VACUUM; while it holds the registry: on that file every client of the owner would read "not connected" for minutes after the next update. Convert only while live data is at most 64 MiB. Above it the conversion is deferred (one diagnostic per owner); sealing still runs and deletes archived rows, and SQLite reuses their pages, so the file stops growing once sealing keeps up. It does not shrink: sealed segments stay in the registry, so giving the free pages back needs a compaction that does not hold the registry (follow-up bead). Debug-only overrides for the interval and the bound keep the behavior test small. Red/green: legacy_registry_maintenance. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-next: a local merge commit reuses the fetched same-tree acpmux (#20534) * test: a local merge commit reuses the fetched same-tree acpmux (red) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: a local merge commit reuses the fetched same-tree acpmux reload.sh on a developer Mac stopped with "no cached acpmux" for any commit no workflow published (a local integration of feat-cmux-next and PRs), even though the tree fetch had just put that tree's checksum-verified acpmux in place. build-acpmux.sh now seeds its commit cache from it when it has every requested architecture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 7be0d24 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38542356066 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * webviews: popups draw one hairline, not two (#20461) * webviews: test that every popup draws one hairline edge Red: --acpmux-popup-elevation stacks a composer-edge hairline on the one already in --ui-popup-shadow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: popups draw one hairline, not two --acpmux-popup-elevation stacked a 0.5px composer-edge hairline on the hairline already in --ui-popup-shadow. It now is the shared popup shadow, so every menu and popover has one edge. Increase Contrast colors that one edge in the popup's ink, which the composer-edge hairline did before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 1cc6616 - vp fmt webviews/src/agent-session/acpmux/modelPickerLayout.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38520503432 * webviews: test that popups draw no border over the hairline and clear it under borders none Red: the pages' .ui-popup and the chat find bar draw a border over the --ui-popup-shadow hairline, and appearance.borders none leaves the hairline drawn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: one rim on page popups and the find bar; borders none clears it The pages' .ui-popup drew a 1px border over the --ui-popup-shadow hairline; it now draws no border. The chat find bar keeps its border (its focus cue) and casts --ui-popup-drop, the shadow without the hairline. appearance.borders none sets --ui-popup-edge transparent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: format modelPickerLayout.ts (base fmt red) Keeps cmux-autofix's vp fmt of the base file, which the base merge replaced with the unformatted copy; react-apps-check fails on it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: the Channels palette draws one rim; test follows the summary card Red after the base merge: the new Channels palette (.lg-palette, ledger.css) drew a 1px border over the --ui-popup-shadow hairline, and the summary popover became .acpmux-summary-card. The palette now draws no border. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 54dd59d - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38543983469 --------- 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: name the owner of ControlWorkspaceInfoCache's publish hop (clears the concurrency red) (#20485) * fix: clear two feat-cmux-next check reds tree-publish-v2's stub uploader takes --conflict-exit-code, which the tree publisher now passes (af8b68e). PaneAnimationsSetting carries the namespace-type justification its ratcheted sibling settings have. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep PaneAnimationsSetting internal to CmuxNextSettings Nothing outside the module uses it, so it needs no namespace-type allow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at c2bdb98 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38530638157 * fix: name the owner of ControlWorkspaceInfoCache's publish hop check-concurrency flags the unowned Task d0a62be added; it is a one-shot main-actor hop that holds the cache weakly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 7cf07e3 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38544346314 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * CmuxNext: one-line sidebar rows by default; notification text in the hover card (#20483) * test: a notified compact row stays one line; its text reads in the hover card Leo 2026-10-14: "why is it even 2 lines in the first place?" Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: compact sidebar rows are one line by default; the notification text moves to the hover card sidebar.workspaceRow.notification is now off by default (Leo 2026-10-14). #19862 turned it on for classic parity; the setting stays as an opt-in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: regenerate the settings schema hash for the notification default Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: regenerate the settings schema hash on the merged base Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: take base's settings exports after the base merge (regenerated next) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: regenerate the settings exports on the merged base Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: refresh the defaults snapshot on the merged base Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: the hover card template lists the notification fact; comments say where the text shows 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>
Compact sidebar rows are one line by default again.
sidebar.workspaceRow.notificationis now off, so an unread notification's text no longer adds a second line under the row. The status glyph carries the state, and the text now reads in the row's hover card.Call owner: Leo, 2026-10-14. On the #20410 capture, Leo asked "why is it even 2 lines in the first place?". The coordinator confirmed that question is the call, and this PR cites it as the call-owner approval for the default change.
Background: #19862 (hq#1870 P1 11) turned the setting on for parity with classic's
sidebar.showNotificationMessage. @austinywang, FYI: the classic-parity behavior is still one toggle away. Settings > Appearance > Workspace Rows > Latest Notification (sidebar.workspaceRow.notification: true) draws the text under the row again. The classic settings migration still carries an explicitshowNotificationMessagevalue across. Moving a notified workspace to the top (notifications.attention.moveToTop) is unchanged.Changes
WorkspaceRowElements.standarddrops.notification.The workspace hover card shows the unread notification's text as a fact (bell icon, up to two lines).
Defaults snapshot, settings schema and MDM exports now show
false.scripts/cmux-next/sidebar-notification-e2e.pynow expects a one-line row by default.Custom hover cards: the template (
docs/cmux-next/sidebar-customization/main.js) lists the newnotificationfact (bell icon) so a card made from it keeps the text; the fact kinds doc andhovercard-live.pyfollow.The defaults snapshot regenerated on the merged base also picks up
setting.freeModel.enabled, which base had in its schema but not yet in its snapshot.Tests
SidebarOneLineRowsTests:In the app
DEV builds of base and this head, same scene (three workspaces get a notification). Top: base draws the text as a second line; this PR keeps every row one line. Bottom: hovering
apiputs the text in the hover card, with a bell icon.Turning on Settings > Appearance > Workspace Rows > Latest Notification in the same run brings the second line back on every notified row.
Pre-existing, on base too: a notification's text can reach the sidebar a few seconds after its unread badge, so a row (or its card) shortly after the notification may not have the text yet.
Changelog
🤖 Generated with Claude Code