Skip to content

cmux-next: Reduce Motion stops every loop; Status indicators setting (Animated / Static) (cx-sya4n) - #20511

Closed
teamleaderleo wants to merge 19 commits into
feat-cmux-nextfrom
feat/reduce-motion-status
Closed

teamleaderleo wants to merge 19 commits into
feat-cmux-nextfrom
feat/reduce-motion-status

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Reduce Motion now stops every repeating animation in cmux NEXT, native and web. A new setting, Status indicators: Animated / Static, swaps pulsing and blinking status lights for steady shapes (cx-sya4n). Austin gets headaches from the pulsing lights.

What changed

  • Setting: appearance.statusIndicator.animation takes animated (the default, unchanged look) or static. It is under Settings > Appearance > Loading Indicator > Status Indicators. Reduce Motion forces Static.

  • Static, native: each state has a steady shape, so states differ without color:

    • working: solid dot
    • busy: arc
    • paused: outlined circle
    • waiting: !
    • permission: hand
    • question: question bubble
    • sign-in: key
    • error: circled ✕
    • done: ✓

    Short labels are tooltip and VoiceOver text only; sidebar rows get no new visible text. The attention ring uses the steady style, which matches Leo's call to make steady the attention default.

  • Static, web (agent pane): these stop: the mic, model-picker and harness spinners, the floating breathe, the Thinking sweep (its text stays muted), the reply caret, the jump-to-latest dots and the writing marker. Color-only marks get their own shapes (triangle, ✓, ✕, dot, ring) and accessible labels in every mode.

  • Reduce Motion also stills Home's caret and typing dots, the update and import spinners, and the diff base-picker spinner.

  • Blink cap in every mode: nothing flashes faster than about 2 Hz (MotionLoop.minimumPeriod). The flash is 1.0 s and the pulse 0.5 s.

  • Motion kit for late content (asked for by cx-next-handoff):

    • Web: webviews/src/ui/motion.ts + motion.css provide spring tokens (linear()), reveal (a fade plus a 3 px rise), crossfade, and stagger (25 ms steps, at most 4 items).
    • Native: Motion.stagger(index:) and Motion.reveal(_:).
    • Under Reduce Motion both are a plain fade with no stagger. The "Late content" section of plans/cmux-next/motion.md documents them.
  • Classic is unchanged. Its audit and a needs-a-call note for Austin are in Status lights: Reduce Motion and a Static mode, NEXT default and classic plan #20403.

Testing

Red, then green: the failing tests were committed first.

  • Web:
    • webviews/test/static-status-loops.test.ts asserts that under Static or Reduce Motion, document.getAnimations() finds no running infinite animation.
    • motion-kit.test.ts covers the kit.
    • Red: run 38524527894 (1fb2d2d), with 4 static-status-loops failures and 3 motion-kit failures. Green: run 38528233410 (395e046).
  • Swift:
    • ReduceMotionLoopTests covers no running CAAnimation loops under Reduce Motion. StaticStatusIndicatorTests covers distinct shapes per state. BlinkRateCapTests covers the 2 Hz cap.
    • Red: fleet step 0432026f, a compile failure (the new API is missing). Green: step 0c4def9a.
  • On the final head c98fa57:
    • Fleet Swift suites: 293 passed, 0 failed.
    • Release compile: fleet step 22fd4288, BUILD SUCCEEDED.
    • Settings exports were regenerated on the merged head. gen-strings --check is clean. Catalogs and exports only add lines; none were deleted.
    • Locally: check-motion, crash_ratchet, system-blue, type-scale and text-focus pass. God files: none of these files grew.
  • Captures: real cmux NEXT DEV builds on a fleet capture slot, base on the left and head on the right. Host names are blurred. I watched them.
    • Chat after a turn in Animated, Static and Reduce Motion. In Static and Reduce Motion, nothing on the head side loops except the system text caret in the composer (see Left over).
    • The sidebar and attention ring in Animated vs Static.

Captures

In each clip, base is on the left and head on the right. In order: chat in Static, chat under Reduce Motion, the attention ring in Static, then a still of the Static shapes.

plan gif 20511/chat-static.gif (from chat-static.mp4)
plan video 20511/chat-static.mp4
plan gif 20511/chat-reduce.gif (from chat-reduce.mp4)
plan video 20511/chat-reduce.mp4
plan gif 20511/clip-attention-static.gif (from clip-attention-static.mp4)
plan video 20511/clip-attention-static.mp4
plan image 20511/still-static.png
uploaded 20511/chat-static.gif
uploaded 20511/chat-static.mp4
uploaded 20511/chat-reduce.gif
uploaded 20511/chat-reduce.mp4
uploaded 20511/clip-attention-static.gif
uploaded 20511/clip-attention-static.mp4
uploaded 20511/still-static.png

chat static

Full quality: chat-static.mp4

chat reduce

Full quality: chat-reduce.mp4

clip attention static

Full quality: clip-attention-static.mp4

still static

Left over

  • The composer's text-input caret still blinks, at about 1 Hz. That is the system caret.
  • The floating, summary and jump dots don't appear in the live captures. The browser test covers them.
  • Base reds this PR does not touch: react-apps-check check:layers on summary.css (from d77476f), main-thread concurrency in ControlWorkspaceInfoCache.swift, and PaneAnimationsSetting in the package conventions lint.

Changelog

Added: a Status indicators setting (Animated / Static); Reduce Motion now stops every looping animation, and nothing blinks faster than 2 Hz.

Light and dark, base vs head (real cmux NEXT DEV builds of the merge-base and this head, on a capture mini; host name blurred). Each sheet has three columns: base (animated), Status indicators = Static, and Reduce Motion. The rows are the sidebar status rows at 2x, the tab strip and the attention-ring corner. The clips put the sidebar and tabs side by side: on base the spinners turn, and on head with Static or Reduce Motion they are a still dot. In the attention clip a notification arrives: base flashes the ring and head shows a steady one. Pixel diffs of the head recordings show nothing moving except the composer's system caret. The "Release notes" row's subtitle differs only because of when the tabs were seeded. The set has no running agent turn; the chat clips above cover the web Thinking sweep and the reply caret.

Showcase

sheet light

sheet dark

clip light static

clip dark static

clip light reduce

clip dark reduce

clip light attention

clip dark attention

Open the live gallery

🤖 Generated with Claude Code

teamleaderleo and others added 18 commits October 13, 2026 20:20
…blink cap

Red on the base: the Home typing bubble and transcript typing row loop
forever under Reduce Motion; appearance.statusIndicator.animation is not
in the settings schema; the pane attention blink and the flash verb
flash faster than twice a second (0.3 s blinks; faster at the lowest
Debug Settings loop periods).

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

The vendored MessagesLab transcript and conversation list looped their
caret blink, typing dots and typing breath whatever Reduce Motion said;
cmux-next mirrors Motion.reduceMotionOverride into their new pins
(blocker patches recorded in Patches/ and the README). The update
sheet's symbol effect follows Motion.animatesLoops, and the onboarding
import row uses the shared native spinner, still under Reduce Motion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
appearance.statusIndicator.animation (animated by default, or static;
Settings > Appearance > Loading Indicator > Status Indicators). Static,
and always under Reduce Motion, stills every indicator and draws each
state as its own shape whatever the Debug icon set: working a solid dot,
waiting the kind marks (hand, ? bubble, key, !), error an x in a ring,
done a check, paused an empty ring, busy the style's still glyph (an
arc for the dot style). Tab badges draw the same marks. Every
indicator's tooltip and VoiceOver label is the state's short name, and
a static sidebar workspace row shows it beside its mark.

Nothing flashes faster than twice a second: the flash loop is at least
1 s (two blinks half a period apart, the attention ring's blinks half a
period each) and the pulse at least 0.5 s, Debug Settings overrides
included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Regenerated on the fleet (export-settings-artifacts.sh) and gen-strings.

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Coordinator 2026-10-13: in the static look the attention ring does not
blink or pulse; it reuses the existing steady style (the default stays
with cg-lane-adopt).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lawrence is redoing the sidebar, so sidebar work stays additive (glyphs
only). The static look keeps the distinct still glyph in the same slot
and size; the short state name stays the indicator's tooltip and
VoiceOver label everywhere. WorkspaceRowView is back to its base layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uce-motion-status

# Conflicts:
#	Packages/macOS/CmuxNext/Sources/CmuxNextOnboarding/Steps/ImportProfileRow.swift
#	Packages/macOS/CmuxNext/Sources/CmuxNextTabs/TabCell+StatusIndicator.swift
The textual merge dropped base's browser.omnibar.glass "transparent"
choice from the exports; regenerated on the fleet and gen-strings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… Reduce Motion; late-content kit

Red first (cx-sya4n phase 2): the pane theme has no statusIndicators, the
stylesheets have no static rules and the color-only marks no shapes, and
there is no shared reveal/stagger on either side.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
react-apps-check stopped at the typecheck on the red commit, so the browser
test never ran; load the kit by path with its own type and format the file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- AgentPaneTheme sends statusIndicators (static when the setting is static
  or Reduce Motion is on); AgentPaneView re-themes when it changes.
  applyAgentTheme sets data-status-indicators on the root.
- Under it no pane loop runs: mic wait, model picker and harness spinners,
  floating breathe, Thinking sweep (text stays muted), reply caret, Jump to
  Latest dots, Home writing. The diff base picker's spinner stops under
  Reduce Motion (animation none).
- Color-only marks get a shape each in static: the floating chat's
  attention triangle; subagent dots with a check or cross; lineage badges
  as dot, check, cross or ring. Subagent dot tooltips name the state.
- Late content: webviews/src/ui/motion.ts + motion.css (reveal, crossfade,
  stagger, spring easings as CSS linear()) and Motion.reveal /
  Motion.stagger natively; Reduce Motion fades only, no stagger.
  plans/cmux-next/motion.md "Late content".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The lineage badge's running dot and stopped ring differed only through
theme variables the bare page did not define.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Static shapes now cover base's new lineage states: done (check) and waiting (!).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The base merge interleaved the new keys into the top of both catalogs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo teamleaderleo added the hold cmux-next batch queue: keep this PR out of the batch (docs/ci/cmux-next-batch.md) label Oct 14, 2026
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 14, 2026 08:40 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Important

Review skipped

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

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

⚙️ Run configuration
  • Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 16383eb7-633d-4e33-b791-d2459ea7efa5

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

Use the checkbox below for a quick retry:

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

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

❤️ Share

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

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

red: web / Web status; do not merge until green; see lane-rules
red: web / react-apps-check; do not merge until green; see lane-rules

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Unclear: web / react-apps-check failed: no known signature; failed step: Check webviews (Oxlint, Oxfmt, type check).

CI failed on c6d04a7804 (run 38537675544 attempt 1): 1 unknown.
macOS jobs did not run: compile and app tests were skipped: Fast static checks skipped.

Job Verdict Why
web / react-apps-check unknown no known signature; failed step: Check webviews (Oxlint, Oxfmt, type check)

Not re-run automatically: web / react-apps-check is not a machine failure.

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

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

CI fast guards failed on c6d04a7804 (https://github.com/manaflow-ai/cmux/actions/runs/38537675544). It does not block the merge; a red guard merged into main breaks it for every open PR.
The log named no failed step; see the run.

Agents: python3 scripts/ci/guard_attribution.py fix applies the mechanical fixes locally. This comment is updated in place on each push.

- regenerated Packages/macOS/CmuxNext/ci-target-graph.json
- regenerated schemas/defaults/cmux-next.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38536731324
@blacksmith-sh

blacksmith-sh Bot commented Oct 14, 2026

Copy link
Copy Markdown

Found 1 test failure on Blacksmith runners:

Failure

Test View Logs
convention violations found (ERROR lines above)./
convention violations found (ERROR lines above).
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@github-actions

Copy link
Copy Markdown
Contributor

cmux-next tour of c6d04a7804fadafe777ba1d8e95fa2a2e536c6fe (run)

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

The tour produced no media.

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

@teamleaderleo

teamleaderleo commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator Author

Split plan

Compared with origin/feat-cmux-next at c15ae68 (merge base 4d45234, 3,258 commits behind). Nothing in this PR has landed as-is. Two things on feat-cmux-next overlap it:

  • CmuxNext: Codex-like status icons by default, plus static status glyphs #20410 added StaticStatusGlyph and a StatusIconSet.still set: one still shape per state, which the Debug status-icons switch can pick. Its commit names StaticStatusGlyph(state)?.plan as "the hook for the Static setting". This PR's StatusIndicatorPlan.stillPlan (dot, arc, hand, key and so on) is a second, competing still set. Decided (Leo): Static draws CmuxNext: Codex-like status icons by default, plus static status glyphs #20410's StaticStatusGlyph set (StatusIconSet.still) by default. This PR's stillPlan shapes stay available behind a Debug tunable, following the existing status.* tunable pattern.
  • The motion rewrite (4b5cf67) changed the springs: appear went from 0.18/0.9 to 0.135/1, and move and settle changed too. The web kit's linear() easings and its 167 ms reveal were sampled from the old springs, so they need re-sampling before that slice lands.

What's in the diff

  • Generated or churn, about 1,915 lines in 10 files:

    • the two Localizable.xcstrings (1,716)
    • settings/generated/strings.json (84)
    • settings-schema.json (71)
    • the mdm docs (28)
    • localization-allowed-omissions.json (12)
    • schemas/defaults and ci-target-graph.json. The defaults diff also carries a stale chromeStyle line.

    All of this is regenerated inside the slice that adds the setting.

  • Real change, about 1,240 lines in 56 files, of which about 630 are tests.

Slices, in landing order. Each one is cut from feat-cmux-next and stands on its own.

  1. 2 Hz blink cap: cmux-next: nothing flashes faster than twice a second (cx-sya4n) #21633.
    • Files: MotionLoop.minimumPeriod and shortestFlashCycle, the flash default of 1.0 s, the flash verb's keyframes, BlinkRateCapTests (in CmuxNextDesignTests), and the motion.md and design-tokens rows.
    • 7 files.
  2. Late-content motion kit, native and web:
    • Files: Motion+Reveal.swift (reveal and stagger; drop the cmux NEXT: live resize content follows the window edge every frame #20467 TODO, since animatesMovement already covers live resize), webviews/src/ui/motion.ts and motion.css with re-sampled easings, the ui.css import, the README section, the kit half of motion-kit.test.ts, a Design test for reveal and stagger, and motion.md "Late content".
    • About 9 files.
    • Deferred: nothing uses the kit yet, and its easings are stale. Carve it later, once a surface needs it.
  3. Reduce Motion stills Home and the spinners (native):
    • Files: the MessagesLab reducesMotion pins and their three vendored edits plus patches and README, HomeReduceMotion with its two install sites, UpdateSheetView, the ImportProfileRow comment, Package.swift and ci-target-graph test deps, and the Home half of ReduceMotionLoopTests.
    • About 15 files.
  4. Web loop stops:
    • Files: theme.ts and types.ts (the statusIndicators theme key becomes data-status-indicators), the CSS rules in conversation, harnesses, acpmux styles, summary, home-channels and styles.css (the diff base-picker stops under Reduce Motion), the SubagentStack tooltip, static-status-loops.test.ts, and the theme half of motion-kit.test.ts.
    • About 11 files.
    • The attribute does nothing natively until slice 6.
  5. Status indicators setting (Animated / Static):
    • Files: StatusIndicatorAnimation, StatusIndicatorSettings.animation, the parser and the schema descriptor, StatusIndicatorConfig.isStatic, and StatusIndicatorPlan.make(isStatic:). Static draws StatusIconSet.still (CmuxNext: Codex-like status icons by default, plus static status glyphs #20410's glyphs) by default; a Debug tunable switches it to this PR's stillPlan shapes.
    • Also all the regenerated files from the list above: schema, defaults, mdm docs, xcstrings, strings.json and omissions.
    • About 14 files. The localization audit runs here.
  6. Static everywhere:
    • Files: shortLabel with its tooltip and VoiceOver label (StatusIndicatorState+Label, StatusIndicatorView+Label), the tab badge, indicator and accessibility wiring, the steady attention ring in Motion+Attention, AgentPaneTheme and AgentPaneView sending statusIndicators, StaticStatusIndicatorTests, the indicator half of ReduceMotionLoopTests, and status-indicators.md.
    • About 12 files.

Once slices 3 to 6 are open, close this PR. Its captures and test receipts are on the old base.

Opened, each cut from feat-cmux-next:

Slice PR What it ships
1 #21633 the 2 Hz blink cap
3 #21654 Reduce Motion stills Home's caret and typing dots, and the update spinner
4 #21657 the web loop stops; WebTheme and the agent pane theme send data-status-indicators
5a #21659 (merged) the static look in CmuxNextDesign: on under Reduce Motion, drawing the Static set's glyphs, with status.static.shapes as the Debug toggle
5b #21662 the Settings row (Animated / Static) and its strings; stacked on #21659
6 #21664 Static everywhere: tabs, the attention ring, the agent pane theme, and the tooltip and VoiceOver labels; stacked on #21659

Slice 2 (the motion kit) is deferred.

Follow-up slice, not started (found in review of #21664): StatusIndicatorView is an accessibility element only for spinner(). So the label that nameState() sets (StatusIndicatorView+Label.swift) probably isn't read in sidebar rows. VoiceOver needs the row (or the view) to expose it. That needs a small slice of its own with a VoiceOver check. #21664 keeps its reviewed head.

Follow-up nit, not started (found in review of #21654): remove the Reduce Motion notification observers in deinit, following AgentPaneView.swift:321. They're the motionObservers in MessagesLabHomeView.swift:32 and CmuxSidebarView.swift:146. Today they capture self weakly, so a closed view's turn does nothing, but the tokens stay registered.

Once these land, these two follow-ups are the only things left from this PR.

teamleaderleo added a commit that referenced this pull request Oct 18, 2026
…21659)

* test(cmux-next): the static look stills every indicator on the Static set (cx-sya4n)

Red: there is no static look (StatusIndicatorConfig.isStatic,
StatusIndicatorPlan.make(isStatic:)); under Reduce Motion an indicator
keeps its icon set's mark (compile failure).

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

* cmux-next: a static status look, on under Reduce Motion (cx-sya4n)

StatusIndicatorSettings.animation (animated or static) and Reduce Motion
make StatusIndicatorConfig.isStatic; StatusIndicatorPlan.make(isStatic:)
then draws the Static set's glyphs (StaticStatusGlyph, #20410) with no
animation, whatever the icon set. The Debug toggle status.static.shapes
draws #20511's StatusMark shapes instead. StatusIndicatorView takes the
look; tabs and the Settings row follow in later slices.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 18, 2026
…oop (cx-sya4n)

The agent theme's statusIndicators look becomes data-status-indicators
on the root. Under static the mic, model picker and harness spinners,
the floating breathe, the Thinking sweep, the reply caret, the jump
dots and Home's writing marker stop, and color-only marks get a shape
each; the diff base picker's spinner stops under Reduce Motion.
Subagent dots name their state in the tooltip. Carved from #20511.

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

* test(cmux-next): nothing flashes faster than twice a second (cx-sya4n)

Red: the attention ring's blinks are 0.3 s apart and the flash verb's
0.24 s; a Debug Settings loop period can go down to 0.1 s.

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

* cmux-next: nothing flashes faster than twice a second (cx-sya4n)

MotionLoop.minimumPeriod floors every loop period, Debug Settings
overrides included: pulse at least 0.5 s, flash at least 1.0 s. The
flash default is 1.0 s (was 0.6 s) and the flash verb's two blinks
peak half a period apart. Carved from #20511.

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

* docs(cmux-next): the spec docs list the 1.0 s flash and the 2 Hz cap

design-tokens.json, design-tokens.md and components/panes.md still gave
the 0.6 s flash with 0.3 s blinks; the loop sources point at the
current loopDefaults lines. motion.md: normal speed slows the flash and
the attention blinks, not the pulse.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 18, 2026
…es its state (cx-sya4n)

Tabs draw the static look's still marks (the badge too, never a dot
told apart by color alone); the pane attention ring is the steady ring
under Static; the agent pane's theme sends statusIndicators (static
under Static or Reduce Motion). Every StatusIndicatorView's tooltip and
VoiceOver label is the state's short name (StatusIndicatorState
.shortLabel, nine strings in every catalog locale), and a tab's
needs-input VoiceOver text names the blocked kind. Carved from #20511.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 18, 2026
…oop (cx-sya4n)

The agent theme's statusIndicators look becomes data-status-indicators
on the root. Under static the mic, model picker and harness spinners,
the floating breathe, the Thinking sweep, the reply caret, the jump
dots and Home's writing marker stop, and color-only marks get a shape
each; the diff base picker's spinner stops under Reduce Motion.
Subagent dots name their state in the tooltip. Carved from #20511.

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

* test(cmux-next): the static look stills every indicator on the Static set (cx-sya4n)

Red: there is no static look (StatusIndicatorConfig.isStatic,
StatusIndicatorPlan.make(isStatic:)); under Reduce Motion an indicator
keeps its icon set's mark (compile failure).

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

* cmux-next: a static status look, on under Reduce Motion (cx-sya4n)

StatusIndicatorSettings.animation (animated or static) and Reduce Motion
make StatusIndicatorConfig.isStatic; StatusIndicatorPlan.make(isStatic:)
then draws the Static set's glyphs (StaticStatusGlyph, #20410) with no
animation, whatever the icon set. The Debug toggle status.static.shapes
draws #20511's StatusMark shapes instead. StatusIndicatorView takes the
look; tabs and the Settings row follow in later slices.

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

* test(cmux-next): appearance.statusIndicator.animation parses and has a Settings row (cx-sya4n)

Red: cmux.json's animation key is ignored and Settings has no Status
Indicators row.

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

* cmux-next: Settings > Appearance > Loading Indicator > Status Indicators (Animated / Static) (cx-sya4n)

appearance.statusIndicator.animation reads into
StatusIndicatorSettings.animation, with a choice row (agent-settable
like its neighbours) and its four strings in every catalog locale. The
settings exports regenerate on a Mac (PR autofix).

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

* l10n(cmux-next): the Status Indicators help in all 21 catalog locales (cx-sya4n)

check-l10n wants every catalog locale; the help had the nine required
ones only.

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

* chore(cmux-next): settings exports for appearance.statusIndicator.animation

settings-schema.json (the row, its page slot and the schema hash), the
mdm JSON, plist and table, the source stamp, the defaults snapshot and
the Settings page strings. Written on Linux with the exporter's rules
ported to Python (DeterministicJSON, defaults_guard's stamp);
SettingsSchemaExportTests and ManagedPreferencesManifestTests check
them against the Swift exporter in CI.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 18, 2026
…update spinner (cx-sya4n) (#21654)

* test(cmux-next): Home's typing dots do not loop under Reduce Motion (cx-sya4n)

Red: the vendored list's typing bubble pulses and the transcript's
typing row breathes forever whatever Reduce Motion says; there is no
pin for cmux's Reduce Motion override to reach them (compile failure).

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

* cmux-next: Reduce Motion stills Home's caret and typing dots and the update spinner (cx-sya4n)

MessagesLabHomeView and CmuxSidebarView get a Reduce Motion pin (nil
reads the system setting) that the vendored caret, typing dots and
typing breath read; HomeReduceMotion keeps the pins equal to
Motion.reduceMotionOverride. The update sheet's symbol effect stops
while loops are off. Patches/ rebuilt for the three vendored edits.
Carved from #20511.

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

* test(cmux-next): Reduce Motion turning on mid-loop stills Home's typing bubble and row (cx-sya4n)

Red (review of #21654): a bubble already pulsing keeps its pulse and a
row already breathing keeps its breath when Reduce Motion turns on;
nothing re-applies the setting to running loops.

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

* cmux-next: Reduce Motion turning on mid-loop stills Home and the update spinner (cx-sya4n)

Review of #21654: the still branches now remove a running pulse or
breath (SidebarDrawing, Transcript), and the cmux hosts of the vendored
list and transcript observe the system setting and their pins, then
re-apply them to the typing bubbles, typing rows and caret already on
screen. The update sheet's spinner re-reads loops when the
environment's Reduce Motion or Motion's override changes. Patches/
rebuilt.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 18, 2026
…oop (cx-sya4n) (#21657)

* test(webviews): no status loop runs under Static or Reduce Motion (cx-sya4n)

Red: the agent pane's spinners, breathing dot, Thinking sweep, caret and
jump dots, Home's writing pulse and the diff base picker's spinner keep
looping, and the theme has no status indicators look to set.

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

* webviews: Static status indicators and Reduce Motion stop every web loop (cx-sya4n)

The agent theme's statusIndicators look becomes data-status-indicators
on the root. Under static the mic, model picker and harness spinners,
the floating breathe, the Thinking sweep, the reply caret, the jump
dots and Home's writing marker stop, and color-only marks get a shape
each; the diff base picker's spinner stops under Reduce Motion.
Subagent dots name their state in the tooltip. Carved from #20511.

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

* test: the static look reaches pages through the web theme, and the triangle keeps its box (cx-sya4n)

Red (review of #21657): no Swift theme sends statusIndicators, the
WebTheme bootstrap never sets data-status-indicators (Home's page never
gets it), and the static triangle (8x7) moves what sits beside the 6x6
dot.

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

* cmux-next: every page hears the static status look; the triangle keeps the dot's box (cx-sya4n)

WebTheme carries statusIndicators (static under Reduce Motion or
appearance.statusIndicator.animation static) and its bootstrap sets
data-status-indicators on <html>, so Home's page and every other page
read it; a PageWebView pushes its theme again when the setting or
Reduce Motion changes. The agent pane's theme sends the same value (the
two lines #21664 adds, identical). The static floating triangle draws
in the dot's 6px box.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 18, 2026
…tate (cx-sya4n) (#21664)

* test(cmux-next): the static look reaches tabs, the attention ring, the agent pane and every label (cx-sya4n)

Red: tabs keep their icon set's marks and colored dots, the attention
ring blinks, the agent pane's theme has no statusIndicators look and
indicators have no tooltip or VoiceOver label (compile failure).

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

* cmux-next: the static status look everywhere, and every indicator names its state (cx-sya4n)

Tabs draw the static look's still marks (the badge too, never a dot
told apart by color alone); the pane attention ring is the steady ring
under Static; the agent pane's theme sends statusIndicators (static
under Static or Reduce Motion). Every StatusIndicatorView's tooltip and
VoiceOver label is the state's short name (StatusIndicatorState
.shortLabel, nine strings in every catalog locale), and a tab's
needs-input VoiceOver text names the blocked kind. Carved from #20511.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 18, 2026
* cmux NEXT: a folded long thought reads "Thought for 12s"

A long thought folded once done (#20948, Debug toggle
agentPane.debug.foldLongThoughts) now reads how long the agent thought,
from its first chunk to its latest, clamped at zero for out-of-order
events. An undated thought, or one under a second, reads "Thought".

#20948's "Thought" plus first line stays as the Debug choice
agentPane.debug.thoughtHeader = preview, which the app sets on <html> as
data-thought-header. New string tools.thoughtFor in all 21 pane locales
(split from #20963).

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

* cmux NEXT perf-hitch2: an evicted terminal shows its last frame at once when shown again (GhosttyNextKit a19c884fa, cx-8w027.19.1)

A terminal the warm set evicted showed an empty pane until its new session
attached and drew. Ghostty emptied every released drawable
(kIOSurfacePurgeableEmpty in IOSurface.deinit), and a hidden surface releases
its swap chain, so even the layer's presented frame was blank (ghostty-next
PR 42: a plain release; the last owner frees the pages).

- A paused surface marks its presented frame volatile and makes it
  non-volatile before it shows again; a purged one leaves the layer empty
  until Ghostty's next frame, as before.
- Eviction keeps the presented IOSurface (no copy, volatile), at most the warm
  set terminal capacity, oldest first; a frame dropped by the bound leaves a
  480 px hover picture; memory pressure drops them with no renders.
- The new session shows it over its first surface (TerminalFrameHold
  begin(lastFrame:)) until its first content is parsed and drawn, at most 3 s;
  a purged frame or a hidden mount shows nothing. Hover cards picture it on
  demand.
- debug.surfaces: last_frames outcomes, per pane last_frame_holds, frame_held.
  Proof: scripts/cmux-next/evicted-terminal-frame-live.py.

Pins GhosttyNextKit release xcframework-a19c884fa8d132378ae838dcba95654b177b6723-apple-v6
(checksum 94ff3178... from SHA256SUMS, matching the downloaded zip) and moves the
ghostty-next gitlink, package-notices.json and vt-link-graph.json to it (only
pkg/macos/iosurface/iosurface.zig changed since e6a27a1fb).

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

* perf(cmux-next): a terminal mounted hidden after an eviction shows its last frame at its reveal (cx-8w027.19.1)

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

* perf(cmux-next): hover pictures reclaim a paused surface's volatile frame for the read; a frame in flight at the pause is not reclaimed; no last frame at capacity 0 (cx-8w027.19.1, review)

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

* perf(cmux-next): last-frame logic lives in CmuxNextTerminal (TerminalLastFrames evict/show/report); the frame shows at the reveal; CmuxNextApp does not grow (cx-8w027.19.1)

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

* release-notes(next): fit browser highlights to the What's New limits (#21716)

* release-notes(next): fit browser highlights to the What's New limits

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

* release-notes(next): live links, dev-build wording for remote tabs

Replaces cmux.com/whats-new (404) with real try-it actions, says the
remote browser tab entries are development-build features (their action
is DEV/Nightly only), and drops 'instant' from the tab-switch entry.

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

---------

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

* release-notes(next): fit app highlights to the What's New limits (#21717)

* release-notes(next): fit app highlights to the What's New limits

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

* release-notes(next): live links and exact profile-menu wording

Replaces two 404 links (cmux.com/whats-new, docs/agent-integrations) with
real actions or docs pages, and says what the profile button opens.

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

---------

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

* feat(next): workspace name source and revision, like tab names (hq#1957) (#21648)

* feat(next): workspace name source and revision, like tab names (hq#1957)

Workspaces now record whether their name came from a person or from
auto-naming, the way tab names do (NameSource { User, Auto } and a
name_revision), and the daemon decides whether an automatic rename lands.

- RegistryWorkspace and the mux Workspace carry name_source and
  name_revision, stored in two additive workspaces columns (legacy rows
  read user/0). Both commit paths re-check the stored row's name, source
  and revision inside the transaction.
- rename-workspace and workspace.rename accept source, expected_generation,
  expected_name_revision and release. An automatic rename never replaces a
  person's name (operation.failed, reason_code name_pinned), never empties
  one (name_empty), and loses to any rename after the revision it read
  (revision.conflict). source auto with release hands a pinned name back.
- Workspace projections (raw tree, list-workspaces, resource extra) expose
  name_source and name_revision; the daemon advertises
  workspace-name-source-v1.
- CLI: cmux workspace rename --source auto --expected-generation G
  --expected-name-revision N, and --release-to-auto.
- Spec, regenerated SDK bindings, cmux-app.d.ts and resource descriptor
  hashes follow; optchat-chief's RenameWorkspaceRequest literal gains the
  new fields.
- The app's DaemonCapabilities moves workspace-name-source-v1 from
  unservedByBundledDaemon to optional, since the bundled daemon now serves
  it.

Carved from #20710 onto current feat-cmux-next.

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

* fix(next): project workspace name provenance like the journal states it (hq#1957)

feat-cmux-next tests that landed after #20710's base failed on this slice:

- batch_close and resource_effects: the journal decorates every stored
  workspace with extra.name_source/name_revision, but the projected
  workspace values had no extra, so the public fold never matched and every
  commit restated every workspace. Every workspace value builder (deltas,
  full and scoped projection, normalized registry deltas, resource
  snapshots) now carries the same extra.
- key_close_cannot_rebind_a_live_durable_workspace_identity: creating a
  workspace at a key bound to another live workspace hit the name fence
  (revision.conflict) before the identity check. The name fence now
  applies only to the same workspace (numeric id), and the resource store
  checks identity before the name.

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

* refactor(next): workspace_resource_upsert lives with workspace renames (hq#1957)

Its name provenance parameter pushed mux.rs past the 1000-line god-file
budget (check-no-godfiles.sh); the helper moves to mux/workspace_rename.rs
unchanged.

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

---------

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

* fix(cmux-next): restore #21369 and #21384, undone by the Chief Dock squash (#21690)

* test(cmux-next): older history is asked for once per page

Fails on feat-cmux-next: a streaming commit near the top asks for the same
older page again, because 50a0c60 (#21194) restored the effect #21369
had keyed on the first row.

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

* perf(cmux-next): avoid duplicate older-history loads (#21369)

(cherry picked from commit 8f6f9cb)

* perf(cmux-next): gate find highlight repaints by transcript changes (#21384)

* perf(cmux-next): gate find highlight repaints by transcript changes

* fix(cmux-next): preserve find repaint triggers after fold reveals

* style(acpmux): format find highlight effect

* test(next): carry apps empty-state selector through PR 21384

* test(next): carry apps empty-state selector through find highlight PR

(cherry picked from commit afec4de)

---------

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

* acpmux: no-ask start modes by inherit or an approved preset (#21666)

* feat(acpmux): start a no-ask mode from a session in it or an approved preset

A `session/new` that asks for a mode that doesn't ask, without the
person, now has two ways through:

- Inherit: a caller that is itself an acpmux session in that exact mode on
  the same harness family. Each harness gets ACPMUX_SESSION_TOKEN, an HMAC
  of its session id under a per-run in-memory secret, and
  ACPMUX_SESSION_TOKEN_SOCKET. The token is read only from a unix socket
  connection's first `initialize`. The caller's mode is read at request
  time.
- Approved preset: `_acpmux/presets {name, approveMode: true}` from the
  person records the sha256 of the preset's contents. The approval counts
  only while the resolve and the spawn both used those exact contents, for
  the preset's own mode, with no request overrides other than a folder.

A start mode never applies to an adopt without the person, and a proven
caller session that asks never gets the full-access default for its
chats. The refusal names where to approve (cmux NEXT on this host, or the
preset to approve there once), with {reason, host, mode, preset} in
error.data. Web and peer clients never get the host name.

person_auto_start.rs runs in the hermetic test environment, and its
person-key pipe is close-on-exec so parallel tests' daemons never hold
each other's key pipe open.

Carved from #20653.

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

* test(acpmux): an approved preset with a route, and env that names the caller token

Red on 116ab01:
- an approved preset plus a request `route` passes the start gate and
  reaches the handler ("no route"), where it should need the person;
- a harness profile env that sets ACPMUX_SESSION_TOKEN reaches the agent
  in place of acpmux's caller token.

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

* fix(acpmux): a route counts as an override, env never sets the caller token

Review fixes for the start gate:
- A request `route` on an approved preset now needs the person, like its
  own model, effort, policy or env: a route swaps the provider base URL
  and keys.
- A profile's, preset's or request's env no longer replaces
  ACPMUX_SESSION_TOKEN or ACPMUX_SESSION_TOKEN_SOCKET. That env is
  layered after `caller_env`, so the spawn drops both keys from it.
- The CLI's daemon readiness pipe is close-on-exec from creation
  (`std::io::pipe`, pipe2(O_CLOEXEC) where the OS has it) instead of a
  `pipe` then `fcntl`, which raced with a fork on another thread.

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

---------

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

* cmux-next: the static status look everywhere; indicators name their state (cx-sya4n) (#21664)

* test(cmux-next): the static look reaches tabs, the attention ring, the agent pane and every label (cx-sya4n)

Red: tabs keep their icon set's marks and colored dots, the attention
ring blinks, the agent pane's theme has no statusIndicators look and
indicators have no tooltip or VoiceOver label (compile failure).

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

* cmux-next: the static status look everywhere, and every indicator names its state (cx-sya4n)

Tabs draw the static look's still marks (the badge too, never a dot
told apart by color alone); the pane attention ring is the steady ring
under Static; the agent pane's theme sends statusIndicators (static
under Static or Reduce Motion). Every StatusIndicatorView's tooltip and
VoiceOver label is the state's short name (StatusIndicatorState
.shortLabel, nine strings in every catalog locale), and a tab's
needs-input VoiceOver text names the blocked kind. Carved from #20511.

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

---------

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
Co-authored-by: safe-push <safe-push@cmux.local>
teamleaderleo added a commit that referenced this pull request Oct 18, 2026
* cmux-next: keep the Mac awake while agents work (sessions.keepAwake)

New Settings > Agents > Agent Sessions row, "Keep Mac Awake While Agents
Work": Off, While Plugged In (default) or Always. While a local agent runs a
turn (an acpmux session, or a tab's agent hooks or OSC 7501 working record),
idle system sleep stays off. The display can still sleep and closing the lid
still sleeps the Mac.

- schemas/settings/source/sessions.json is the row's source; the Swift
  descriptor, parser constants and choice enum are generated.
- KeepAwakeService holds one ProcessInfo activity for every reason, so the
  Keep Mac Awake action and the setting never end each other's hold. The
  action now goes through it and still holds on battery.
- PowerSourceMonitor follows IOKit's power-source notification on the main
  run loop (no polling); unplugging releases a While Plugged In hold.
- Agents may not set the key (userOnly).
- Strings in all 21 locales of the settings catalog.

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

* cmux NEXT perf-hitch2: an evicted terminal shows its last frame at once when shown again (GhosttyNextKit a19c884fa, cx-8w027.19.1)

A terminal the warm set evicted showed an empty pane until its new session
attached and drew. Ghostty emptied every released drawable
(kIOSurfacePurgeableEmpty in IOSurface.deinit), and a hidden surface releases
its swap chain, so even the layer's presented frame was blank (ghostty-next
PR 42: a plain release; the last owner frees the pages).

- A paused surface marks its presented frame volatile and makes it
  non-volatile before it shows again; a purged one leaves the layer empty
  until Ghostty's next frame, as before.
- Eviction keeps the presented IOSurface (no copy, volatile), at most the warm
  set terminal capacity, oldest first; a frame dropped by the bound leaves a
  480 px hover picture; memory pressure drops them with no renders.
- The new session shows it over its first surface (TerminalFrameHold
  begin(lastFrame:)) until its first content is parsed and drawn, at most 3 s;
  a purged frame or a hidden mount shows nothing. Hover cards picture it on
  demand.
- debug.surfaces: last_frames outcomes, per pane last_frame_holds, frame_held.
  Proof: scripts/cmux-next/evicted-terminal-frame-live.py.

Pins GhosttyNextKit release xcframework-a19c884fa8d132378ae838dcba95654b177b6723-apple-v6
(checksum 94ff3178... from SHA256SUMS, matching the downloaded zip) and moves the
ghostty-next gitlink, package-notices.json and vt-link-graph.json to it (only
pkg/macos/iosurface/iosurface.zig changed since e6a27a1fb).

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

* cmux-next: regenerate settings exports and defaults for sessions.keepAwake

Exports from the fleet export step at 4beacc0 (run-swift-testing-suites.sh,
export-settings); defaults snapshot from defaults_guard.py snapshot.

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

* perf(cmux-next): a terminal mounted hidden after an eviction shows its last frame at its reveal (cx-8w027.19.1)

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

* perf(cmux-next): hover pictures reclaim a paused surface's volatile frame for the read; a frame in flight at the pause is not reclaimed; no last frame at capacity 0 (cx-8w027.19.1, review)

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

* cmux-next: regenerate settings exports after merging feat-cmux-next

Exports from the fleet export step at 011a67c.

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

* restore-trace.py: trace a relaunch restore per pane with the snapshot sources that moved (cx-8w027.23)

* restore-trace.py: every pane in short, 50 ms reads (cx-8w027.23)

* perf(cmux-next): last-frame logic lives in CmuxNextTerminal (TerminalLastFrames evict/show/report); the frame shows at the reveal; CmuxNextApp does not grow (cx-8w027.19.1)

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

* cmux NEXT perf-restore: no pane re-run on connect, room badge from the strip's window, one topology per turn for every pane that changed (cx-8w027.23)

- PaneController.Snapshot.connected is gone: apply never read it, and the
  read re-ran every pane's snapshot when the daemon connected (16 per
  16-pane restore). A reconnect with a new daemon still re-maps through
  generation and surfaces.
- BrowserProfileService.roomDefault takes the room of the window the
  strip or sidebar avatar draws in (WindowState.profileID, observed), so a
  room's profile badge follows that window; the lookup of WindowManager
  state (not observed) stays only for one-time choices (a new tab's profile).
- A pane whose part of the focus topology changed marks the workspace
  (setNeedsTopology); FocusCoordinator builds and sends one topology per
  owner for the whole main-actor turn, at its end or before any state read
  or event (I4), instead of one per pane.

* cmux NEXT: no focus ring on the daemon recovery status label (#21679)

#20765 made the not-connected screen's status label selectable without
focusRingType = .none, so check:text-focus fails every feat-cmux-next PR.

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

* FocusCoordinator: state is a pure read; key routing and events reduce the pending topology; the turn-end reduction runs detached; what the settled observer queues runs (review of cx-8w027.23 slice 1)

* release-notes(next): fit browser highlights to the What's New limits (#21716)

* release-notes(next): fit browser highlights to the What's New limits

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

* release-notes(next): live links, dev-build wording for remote tabs

Replaces cmux.com/whats-new (404) with real try-it actions, says the
remote browser tab entries are development-build features (their action
is DEV/Nightly only), and drops 'instant' from the tab-switch entry.

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

---------

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

* release-notes(next): fit app highlights to the What's New limits (#21717)

* release-notes(next): fit app highlights to the What's New limits

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

* release-notes(next): live links and exact profile-menu wording

Replaces two 404 links (cmux.com/whats-new, docs/agent-integrations) with
real actions or docs pages, and says what the profile button opens.

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

---------

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

* feat(next): workspace name source and revision, like tab names (hq#1957) (#21648)

* feat(next): workspace name source and revision, like tab names (hq#1957)

Workspaces now record whether their name came from a person or from
auto-naming, the way tab names do (NameSource { User, Auto } and a
name_revision), and the daemon decides whether an automatic rename lands.

- RegistryWorkspace and the mux Workspace carry name_source and
  name_revision, stored in two additive workspaces columns (legacy rows
  read user/0). Both commit paths re-check the stored row's name, source
  and revision inside the transaction.
- rename-workspace and workspace.rename accept source, expected_generation,
  expected_name_revision and release. An automatic rename never replaces a
  person's name (operation.failed, reason_code name_pinned), never empties
  one (name_empty), and loses to any rename after the revision it read
  (revision.conflict). source auto with release hands a pinned name back.
- Workspace projections (raw tree, list-workspaces, resource extra) expose
  name_source and name_revision; the daemon advertises
  workspace-name-source-v1.
- CLI: cmux workspace rename --source auto --expected-generation G
  --expected-name-revision N, and --release-to-auto.
- Spec, regenerated SDK bindings, cmux-app.d.ts and resource descriptor
  hashes follow; optchat-chief's RenameWorkspaceRequest literal gains the
  new fields.
- The app's DaemonCapabilities moves workspace-name-source-v1 from
  unservedByBundledDaemon to optional, since the bundled daemon now serves
  it.

Carved from #20710 onto current feat-cmux-next.

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

* fix(next): project workspace name provenance like the journal states it (hq#1957)

feat-cmux-next tests that landed after #20710's base failed on this slice:

- batch_close and resource_effects: the journal decorates every stored
  workspace with extra.name_source/name_revision, but the projected
  workspace values had no extra, so the public fold never matched and every
  commit restated every workspace. Every workspace value builder (deltas,
  full and scoped projection, normalized registry deltas, resource
  snapshots) now carries the same extra.
- key_close_cannot_rebind_a_live_durable_workspace_identity: creating a
  workspace at a key bound to another live workspace hit the name fence
  (revision.conflict) before the identity check. The name fence now
  applies only to the same workspace (numeric id), and the resource store
  checks identity before the name.

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

* refactor(next): workspace_resource_upsert lives with workspace renames (hq#1957)

Its name provenance parameter pushed mux.rs past the 1000-line god-file
budget (check-no-godfiles.sh); the helper moves to mux/workspace_rename.rs
unchanged.

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

---------

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

* fix(cmux-next): restore #21369 and #21384, undone by the Chief Dock squash (#21690)

* test(cmux-next): older history is asked for once per page

Fails on feat-cmux-next: a streaming commit near the top asks for the same
older page again, because 50a0c60 (#21194) restored the effect #21369
had keyed on the first row.

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

* perf(cmux-next): avoid duplicate older-history loads (#21369)

(cherry picked from commit 8f6f9cb)

* perf(cmux-next): gate find highlight repaints by transcript changes (#21384)

* perf(cmux-next): gate find highlight repaints by transcript changes

* fix(cmux-next): preserve find repaint triggers after fold reveals

* style(acpmux): format find highlight effect

* test(next): carry apps empty-state selector through PR 21384

* test(next): carry apps empty-state selector through find highlight PR

(cherry picked from commit afec4de)

---------

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

* acpmux: no-ask start modes by inherit or an approved preset (#21666)

* feat(acpmux): start a no-ask mode from a session in it or an approved preset

A `session/new` that asks for a mode that doesn't ask, without the
person, now has two ways through:

- Inherit: a caller that is itself an acpmux session in that exact mode on
  the same harness family. Each harness gets ACPMUX_SESSION_TOKEN, an HMAC
  of its session id under a per-run in-memory secret, and
  ACPMUX_SESSION_TOKEN_SOCKET. The token is read only from a unix socket
  connection's first `initialize`. The caller's mode is read at request
  time.
- Approved preset: `_acpmux/presets {name, approveMode: true}` from the
  person records the sha256 of the preset's contents. The approval counts
  only while the resolve and the spawn both used those exact contents, for
  the preset's own mode, with no request overrides other than a folder.

A start mode never applies to an adopt without the person, and a proven
caller session that asks never gets the full-access default for its
chats. The refusal names where to approve (cmux NEXT on this host, or the
preset to approve there once), with {reason, host, mode, preset} in
error.data. Web and peer clients never get the host name.

person_auto_start.rs runs in the hermetic test environment, and its
person-key pipe is close-on-exec so parallel tests' daemons never hold
each other's key pipe open.

Carved from #20653.

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

* test(acpmux): an approved preset with a route, and env that names the caller token

Red on 116ab01:
- an approved preset plus a request `route` passes the start gate and
  reaches the handler ("no route"), where it should need the person;
- a harness profile env that sets ACPMUX_SESSION_TOKEN reaches the agent
  in place of acpmux's caller token.

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

* fix(acpmux): a route counts as an override, env never sets the caller token

Review fixes for the start gate:
- A request `route` on an approved preset now needs the person, like its
  own model, effort, policy or env: a route swaps the provider base URL
  and keys.
- A profile's, preset's or request's env no longer replaces
  ACPMUX_SESSION_TOKEN or ACPMUX_SESSION_TOKEN_SOCKET. That env is
  layered after `caller_env`, so the spawn drops both keys from it.
- The CLI's daemon readiness pipe is close-on-exec from creation
  (`std::io::pipe`, pipe2(O_CLOEXEC) where the OS has it) instead of a
  `pipe` then `fcntl`, which raced with a fork on another thread.

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

---------

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

* cmux-next: the static status look everywhere; indicators name their state (cx-sya4n) (#21664)

* test(cmux-next): the static look reaches tabs, the attention ring, the agent pane and every label (cx-sya4n)

Red: tabs keep their icon set's marks and colored dots, the attention
ring blinks, the agent pane's theme has no statusIndicators look and
indicators have no tooltip or VoiceOver label (compile failure).

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

* cmux-next: the static status look everywhere, and every indicator names its state (cx-sya4n)

Tabs draw the static look's still marks (the badge too, never a dot
told apart by color alone); the pane attention ring is the steady ring
under Static; the agent pane's theme sends statusIndicators (static
under Static or Reduce Motion). Every StatusIndicatorView's tooltip and
VoiceOver label is the state's short name (StatusIndicatorState
.shortLabel, nine strings in every catalog locale), and a tab's
needs-input VoiceOver text names the blocked kind. Carved from #20511.

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

---------

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

* fix(cmux-next): restore the URL chip hover card styles lost in the Chief Dock squash (#21699)

* test(cmux-next): every URL chip hover card class has a rule

Fails on feat-cmux-next: 50a0c60 (#21194) dropped the .cv-link-preview
rules #21199 added, while LinkChips.tsx still renders those classes.

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

* fix(cmux-next): restore the URL chip hover card styles (#21199)

50a0c60 (#21194, a stale-base squash) removed the nine .cv-link-preview
rules while LinkChips.tsx still renders them, so the card drew unstyled.
Restored byte for byte from 5106cc9.

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

---------

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

* acpmux: a saved model or effort pick reaches open New Tab pages (#21677)

* test(acpmux): a saved model or effort pick is announced to watchers

A New Tab prewarmed before a pick draws the old start effort and the chat
then starts on the pick. Fails: note_pick saves the new start default but
sends no _acpmux/harnesses_changed.

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

* fix(acpmux): announce a saved model or effort pick to watchers

note_pick makes the pick the harness's start in _acpmux/harnesses, but
told nobody, so a New Tab prewarmed before the pick kept drawing the old
start effort until the chat started on the pick. It now sends
_acpmux/harnesses_changed, as a saved mode pick already announces
start_mode_changed; pages re-read the catalog on it.

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

---------

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

* cmux-next: keep awake lives in its own target (CmuxNextKeepAwake)

Review of #21730: CmuxNextApp only shrinks (check-app-size.sh), so
KeepAwakePolicy, PowerSourceMonitor and KeepAwakeService move to a new
CmuxNextKeepAwake target with their tests. The App keeps a one-line start
and the Keep Mac Awake binding; its source is 10 lines under the base.

The power-source callback now hops to the main actor with a Task instead of
MainActor.assumeIsolated (a banned crash class).

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

* cmux-next: route test knows the keep-awake tests; one policy write per change

Review of #21730: a CmuxNextSidebar change now also selects
CmuxNextKeepAwakeTests (CmuxNextKeepAwake depends on CmuxNextBridge), so
test_a_shared_module_selects_its_dependents lists it. KeepAwakeService sets
the setting and the agents-working fact in one policy write, so the hold
cannot drop between the two.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
Co-authored-by: safe-push <safe-push@cmux.local>
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Already landed in pieces: #21662 (Status Indicators setting), #21657 (web loops stop under Reduce Motion), #21659, #21664 and #21654 (cx-sya4n). Closing.

This branch was previously deployed

2 inactive deployments
artifacts — c6d04a78 Deployed Oct 14, 2026 by manaflow-cmux-pr-autofix[bot] via publish to R2 #12256
cmux-tui-merge-tree — c6d04a78 Deployed Oct 14, 2026 by manaflow-cmux-pr-autofix[bot] via publish the merge tree #552
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hold cmux-next batch queue: keep this PR out of the batch (docs/ci/cmux-next-batch.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant