Skip to content

cmux NEXT Home: a Chief's avatar shows its state like the Codex pet, and never bounces - #33395

Merged
teamleaderleo merged 3 commits into
feat-cmux-nextfrom
lane/chief-avatar-state-motion
Nov 3, 2026
Merged

teamleaderleo merged 3 commits into
feat-cmux-nextfrom
lane/chief-avatar-state-motion

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Leo, 2026-11-03: "oh you guys completely misinterpreted the "animated" thing... don't bounce the circle. i mean like if you ask codex, codex (idk if you wanna ask sol 6.1 xhigh or astra high) their dot thing has a few different animations for different scenarios."

His first ask (2026-11-01: "i also want them animated like how chatgpt animates their dots") became #30534, which bobs the whole avatar up and down (Float, the default), faster while the Chief works. That is the bounce he turned down. The reference he points at is the Codex app's pet: one distinct animation per scenario, not one motion at two speeds.

What changes

A Chief's avatar in the Home list and pinned grid shows what its Chief is doing, one look per state. The avatar never leaves its place: no bob, bounce or slide. The only transforms are a small scale or turn about its centre.

State When Ring (default)
idle nothing running at rest; a soft glint sweeps across the face once every 7 s (a pet's blink), clipped to the avatar picture's own shape
working its turn is running (InboxRow.isTyping) a quarter arc in the accent runs round the avatar's edge, 1.6 s a turn, linear
waiting a reply is unread a still thin ring in the accent
failed a send failed (InboxRow.hasFailedSend) a still thin red ring

A state change plays one cue of at most 240 ms (design/motion-and-polish.md). When a turn ends (working to idle or waiting), the avatar pops 4%. A failure gives it a quick shake of a few degrees. A reply that arrives with no turn seen fades its ring in. A reload of the same state replays nothing.

Versions (gallery)

The list ships with four styles, and ChiefAvatarMotionGalleryTests draws each one in every state plus the two cues, 8 frames each. It writes them to Packages/Shared/CmuxMessagesLab/.build/chief-avatar-motion-gallery.png:

  • Ring (default): the table above.
  • Glow: a soft glow behind the avatar's shape. None at idle; accent while working, with a glint sweeping every 1.8 s; accent while waiting; red when failed. Same cues.
  • Tilt: the avatar turns its head. A 3° glance every 6 s at idle and a ±4° rock (1.4 s) while working. Ring's still rings and cues.
  • Still: no motion and no ring.

I picked Ring. Each state reads differently at row size, even at a glance and in a dense list. Working also matches the rest of the app's "running" language (an arc going round), so it reads as working without a second mark. Switch styles in Debug Settings > Home > Chief avatar motion (home.avatarMotion). A saved Float, Breathe or Sway value no longer matches a choice and falls back to Ring.

What it does not touch

Stop conditions and cleanup

Every loop and cue is removed by each of the following. The still waiting and failed rings stay, as static indicators:

  • Reduce Motion (system or the cmux pin), calm mode, ui.animationSpeed off, and Home's motion switch, through the existing avatarLoopGate and reduceMotionDidChange path. Working's arc stands still as a quarter ring, so the state still reads.
  • App inactive, or the window not key, hidden or minimized: the list's existing setWindowActive tracking. No new observers.
  • Row back in the pool: SidebarAvatarMotion.stop removes everything, rings included.
  • Style Still: nothing is drawn.

Performance

  • No main-thread work per frame. Each look is Core Animation on the avatar layer and at most two named sublayers of it (a ring CAShapeLayer and a masked glint band), run by the render server. There is no timer or display link.
  • A reload that keeps the state keeps the running loop (asserted by test), so a list update adds no animation work. Per configure, the cost is one look comparison and a frame fit, O(1) per visible row.
  • No vendored file changes, so Patches/ is untouched.
  • No same-harness before/after frame numbers. The previous loop was also render-server only.

Tests

ChiefAvatarMotionTests (MessagesLab), rewritten for the states:

  • each state has its own look, and no style in any state animates the avatar's position;
  • a state change plays a short cue (pop ≤ 1.04 scale, shake), and a reload or Reduce Motion plays none;
  • Reduce Motion and the host's gate stop every loop and keep the still rings;
  • an inactive app or hidden window stops every loop;
  • a pooled row clears, and Still draws nothing;
  • a style change restyles every avatar at once;
  • loops close and are phase-seeded per conversation.

Observed / Not run

  • Observed: python3 scripts/cmux-next/prepush-gate.py: no new violations. Fleet compile and the MessagesLab suites: results in a comment below.
  • Not run: a DEV build watching the list live. Leo tries the build.

Spec: design/motion-and-polish.md (240 ms cue cap, every loop stops). Prior art: #30534 (replaced here), #32861 (open, same default line).

No capture: the looks are in the gallery sheet; Leo tries the build.

Changelog

Changed: A Chief's avatar in Home shows what the Chief is doing (a glint at rest, an arc while it works, a ring when a reply waits or a send failed) instead of bobbing.

Agent session: agent session

🤖 Generated with Claude Code


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


Summary by cubic

Changes the Chief avatar in the Home list and pinned grid to show what its Chief is doing—one look per state, like the Codex pet—instead of bobbing. The avatar never leaves its place; the only transforms are a small scale or turn about its centre.

  • Idle glints across the face occasionally; working runs a quarter arc round the edge; an unread reply draws a still accent ring; a failed send a still red ring.
  • A state change plays one cue of at most 240 ms: a 4% pop when a turn ends, a quick shake on failure, and a ring fade-in when a reply arrives with no turn seen.
  • Under Reduce Motion no loops run, but the working arc stands still as a quarter ring and the still rings stay, so the state still reads.
  • Replaces Float, Breathe and Sway with Ring (new default), Glow, Tilt and Still; saved old setting values fall back to Ring.
  • A state change cues per conversation id, not per row layer, so a row redrawn on another layer still plays its cue.

Written for commit fe426d3. Summary will update on new commits.

View guided diff Turn on auto-fix

…and never bounces

Leo 2026-11-03: "don't bounce the circle. i mean like if you ask codex, ... their dot thing has a
few different animations for different scenarios." #30534 read his first ask ("animated like how
chatgpt animates their dots") as a bob of the whole avatar. Each state now has its own look on the
avatar's place: idle glints now and then, working runs an arc round the edge, an unread reply draws
a still accent ring, a failed send a still red ring; a turn ending pops 4% and a failure shakes a few
degrees (240 ms cues). Ring is the default; Glow, Tilt and Still are the gallery's variants.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-a1179b2311aff0f037aa
@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review e6fd0c4: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr33395 -f ref=lane/chief-avatar-state-motion -f platform=macos -f backend_mode=local

Default changes

This pull request changes 2 defaults at e6fd0c418a26 (what everyone gets without configuring anything):

  • tunable-source.CmuxNextHome/HomeTunables.swift:avatarMotion: "5b5e921725a2994a774c213cd7f0c62bbd3a99e843c6c23bf6e31dab54dc27d2" -> "04cd6fdc88208ffeb8962eaabbaa0396b84c5a470c04ac39be11f3c76f9b6189"
  • tunable.home.avatarMotion: "float" -> "ring"

Signed off on this commit by review (codex, router-review).

Merge receipt: merged as eb1c8c05ef24 with these defaults changed, signed off by review (codex, router-review).

Regenerate the snapshot with python3 scripts/ci/defaults_guard.py snapshot.

Shipped in:

@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Nov 3, 2026
…yer that drew it

Fleet red at b2874ee: ChiefAvatarMotionTests.aStateChangePlaysOneShortCue (a changed row is drawn
on another layer, which had no state before it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-a1179b2311aff0f037aa
…ns and pops drawn 3x

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-a1179b2311aff0f037aa
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol high, sweep, via the review router; full diff from merge base af0ae5b to this head): SHIP at fe426d3

No P1/P2 findings.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex, router-review): SHIP at e6fd0c4

Reviewer: codex gpt-6.1-sol xhigh, fast tier.

Follow-ups: 3. Each lands as an issue in this repo that links this receipt; the lane that opened the PR fixes it forward.

SHIP at e6fd0c4
Visual verdict: no images; Leo tries the build

  1. [P2] [follow-up] SidebarAvatarMotion.swift:101: keepCue: true preserves an active pop/shake when Reduce Motion activates, the window deactivates, or Still is selected. Clear the cue when motion becomes disabled.

  2. [P2] [follow-up] SidebarAvatarLooks.swift:81: Resizing updates the glint band’s bounds but retains animation coordinates calculated for the old size. Enlarging a pinned avatar from 40 to 76 points leaves the band resting inside its face for 90% of the idle loop. Recompute the sweep when bounds change.

  3. [P2] [follow-up] ChiefAvatarMotionGalleryTests.swift:152: Supply gallery or native captures for this head; none were available.

AppKit tests were unavailable on this Linux box without Swift.

Follow-ups:

  • Cancel cues when motion becomes disabled.
  • Update glint trajectories after resizing.
  • Attach visual captures.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Fleet at e6fd0c4 (step d7ec8ee73e705a21ac145000): ChiefAvatarMotionTests (7 tests) and ChiefAvatarMotionGalleryTests pass. The gallery sheet is the step's artifact: cmux-ci artifact d7ec8ee73e705a21ac145000 OUTPUT. Its rows are Ring, Glow, Tilt and Still. Its strips are idle, working, waiting, failed, the turn-ending pop and the failure shake.

Red then green: at b2874ee, aStateChangePlaysOneShortCue failed (step d12bc717be8725d3b5e03e7c). A changed row is drawn on another layer, so its cue lost the previous state. fe426d3 keys the last state by conversation instead.

Compile check at e6fd0c4 (job 4398ca4252516670ba116cf2): one error, in CmuxNextAppTests/RowBadgeSpinnerDensityTests.swift:32 (main-actor cases in @Test). That file comes from #33120, is untouched here and is the same on base, so it is a base red. The sources compile.

@teamleaderleo
teamleaderleo merged commit eb1c8c0 into feat-cmux-next Nov 3, 2026
144 of 147 checks passed
@teamleaderleo
teamleaderleo deleted the lane/chief-avatar-state-motion branch November 3, 2026 09:17
lawrencecchen added a commit that referenced this pull request Nov 3, 2026
…ic red since 25c69ee (#33986)

* swift-logic-classes: classify the false counts of the tip's swift-logic red since 25c69ee

The tip watcher's swift_logic_lines row has been red since 2026-11-02
05:38Z (last green 25c69ee: 415218). Part of the rise is not new
Swift logic: declarations whose reviewed row did not follow a module
move, and view, strings, tunables and debug code that the default rule
counts in full because no row lists it. This commit corrects the table
only (no code change). Each row carries its reason.

- 24 rows follow their declaration's module move (class, shares and
  reason kept): DesktopNotifier and NotificationPermission* to
  CmuxNextNotifications (#32438), LinkHintController to CmuxNextBrowser
  (#32537), FirstFrameGate, LinkDragItem, LinkDropPlan, StaticURL,
  PageInstallCounter, AgentDialogLine, NewTabLayoutChoice,
  RunningPrograms, AgentChatMute, CloudAppOp*, PaneFullWidthTabs,
  PaneTabBarChoices, WindowRegistryStore. LOGIC rows stay LOGIC.
- 3 stale rows follow a move that also changed the kind (enum to
  struct): WhatsNewTunables, NewTabTunables, NotificationPermissionStrings;
  the stale CloudMoveStrings enum row is replaced by its struct.
- 48 new PLATFORM rows: the vertical tab list's AppKit drawing, pointer,
  accessibility and pane extensions (#32127; its row layout math,
  VerticalTabListLayout, stays counted), Core Animation avatar looks
  (#33395), view hosts (BrowserPageBars, TerminalStatusLabel,
  SidebarContainerView+PaneReach, AgentPaneView+MoveCover), the person
  click check of consent prompts, the glyph bitmap cache, the thin Swift
  side of the Rust Cloud start rules (CloudStartCore), the at-cap dialog
  spec, localized strings, Debug Settings tunables, and DEBUG, bench or
  DEV playground and fixture code.

Not changed: every real rise stays counted (for example
NativeDeviceEnrollment, NativeCloudLinkChild, GitHubConnections,
VerticalTabListLayout, DisplayBackdropBinding, CreationInputBuffer);
REWRITE R-SWIFT-MIRRORS names the wave that removes each. The green
reference 415218 is not changed.

swift_logic_lines at 2b3d7cd (python3 -I scripts/cmux-next/
swift-logic-lines.py --rev 2b3d7cd --json, then with --table of
this commit): 431786 -> 427957 (-3829). mirror_lines 71688 unchanged.
table_rows_missing 36 -> 8 (stale rows of deleted declarations);
table_rows_invalid 0. scripts/cmux-next/tests/test_swift_logic_lines.py:
16 tests OK.

* swift-logic-classes: the review's three rows count their rules; reasons say what the code is

Review of #33986 (FIX at cb155fb, comment 6610083921): three rows
put a rule under PLATFORM. Each now counts it:

- RefusalStrings+Spaces: MIXED 0/46. Its spaceDelete (lines 14-19) is a
  Swift copy of the daemon's own rule (cmux-tui-core
  state/room_delete.rs:148 and personal_mutations.rs:241: the default
  room cannot be deleted), so its share is MIRROR and it counts in
  mirror_lines; the localized strings stay platform.
- TerminalStatusLabel: MIXED 65/0. Lines 17-23, 46-47, 49-76 and 88-131
  (81 of 125) are the reveal-wait state machine.
- LinkHintController: MIXED 35/0. #32537 moved and rewrote it; who gets
  each key while the page answers (hold, release, replay marks, key
  kinds, the 500 ms answer bound, outcome handling: about 87 of 251
  lines) is logic for REWRITE R-INPUT.

Text: the reasons of the strings and tunables rows say what the code is
and cite the table's own precedent rows, not a "table rule" no document
has; the AgentPaneView #1 row of AgentPaneView+MoveCover describes the
Chromium move it holds; LinkHintStrings moved to its own file, not to
another module; CloudStartCore's reason names its one hand-copied
fallback line. Three stale rows follow their moved code:
PersonalGroupLifecycle (LOGIC, now a struct in CmuxNextAppCore), the
NSResponder extension (PLATFORM, now in CmuxNextFocusUI), and
DiffTabReuse (LOGIC, now enum DiffTabChange and an extension of
DiffOpenSource in CmuxNextDiff; both rows LOGIC).

swift_logic_lines at 2b3d7cd with this table: 428126 (base table
431786; the first version of this PR 427957); mirror_lines 71694.
At feat f07e8d9: 433351 -> 429651. table_rows_missing 5,
invalid 0; scripts/cmux-next/tests/test_swift_logic_lines.py: OK.

* swift-logic-classes: LinkHintController counts the key routing its reason names; textInputHasKeyboard is listed

Re-review of #33986 (FIX at 791c494, comment 6611048817):

- LinkHintController: MIXED 35/0 -> 57/0. The reason already named
  hold, release, replay marks, key kinds, the answer bound and outcome
  handling as logic; those are lines 22-24, 28-31, 34-35, 45-46,
  52-77, 79-85, 98-105, 107-130, 132-140, 142-149, 180-200, 202-210
  and 217-237: 144 of 251 lines. The reason now lists them.
- extension NSWindow in CmuxNextFocusUI/TypedTextInput.swift (#31865):
  new LOGIC row. textInputHasKeyboard is the rule which input has the
  person's keyboard; the view-shaped default (an NSWindow extension)
  counted its 13 lines as 0.
- Reasons cite repository facts only (crates, files, PRs), not plan
  names outside the repository; the strings rows no longer quote a
  row count that changes.

swift_logic_lines at 2b3d7cd with this table: 428194 (base table
431786). At feat f07e8d9: 429719. mirror_lines 71694 / 71698.
table_rows_missing 5, invalid 0. scripts/cmux-next/tests/
test_swift_logic_lines.py: OK.
teamleaderleo added a commit that referenced this pull request Nov 3, 2026
…lays once on a state change (#33534)

* cmux NEXT Home: a Chief's avatar runs nothing at idle; Ring's glint plays once on a state change

claude-sr2-coord (raised by fs-lane-2) on #33395: "The idle state's glint every 7 s is a loop running at
idle. Check it against the 'no CPU/wakeups at idle' budget ... If it costs anything, run the glint only on
hover or on a state change." Idle (Ring and Tilt) now adds no animation; only working loops. Ring glints
once (240 ms) when the state changes out of working or into a new rest state. A cue still playing ends
when motion stops (#33475, item 1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-a1179b2311aff0f037aa

* Home avatar looks test: the pinned tile restyles while working (idle runs nothing)

Fleet red at ed861f1: aStyleChangeRestylesEveryAvatarAtOnce read the idle tile's loop, which this
change removes on purpose.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-a1179b2311aff0f037aa

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-bot[bot] <348531757+cmux-bot[bot]@users.noreply.github.com>
teamleaderleo added a commit that referenced this pull request Nov 4, 2026
…g on you and done (#34416)

* cmux NEXT Home: a Chief's avatar shows thinking, tool running, waiting on you and done

Relayed by Chief (Leo, 2026-11-03): "Replace the Chief avatar's bounce with per-state animations
like the Codex status dot: thinking, tool running, waiting on you, done." #30534 added the bounce;
#33395 replaced it with Ring/Glow/Tilt looks for idle, working, waiting (unread) and failed. This
splits working into thinking (the arc) and tool running (a slowly turning ring of dots, from the
run's activity line), makes waiting mean an open question or approval (a still orange ring), and
turns an unread reply into done (a green check badge on the lower trailing edge, the Chief card's
done mark). Only a running turn loops; done and waiting are still.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-7285061ffdc2a24af93a

* Chief avatar tests: no try to the right of an operator

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-7285061ffdc2a24af93a

---------

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

This branch was previously deployed

1 inactive deployment
ci-routing-agent — e6fd0c41 Deployed Nov 3, 2026 by teamleaderleo via cmux-tui tree preflight #104997
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant