Skip to content

agent pane: a cmux_exec row says what its script did, and opens to JavaScript and a result tree - #28698

Merged
cmux-bot[bot] merged 7 commits into
feat-cmux-nextfrom
lane/ux-cmux-exec-rows
Oct 31, 2026
Merged

cmux-bot[bot] merged 7 commits into
feat-cmux-nextfrom
lane/ux-cmux-exec-rows

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator

Leo, 2026-10-30: "these should be rendered better than just cmux called cmux exec we know what these are supposed to be"

Three cmux_exec scripts in a row each read "Called cmux · cmux exec", and an opened one showed the raw mcp__cmux__cmux_exec line, the script as one escaped JSON string and the response as escaped JSON. Now each row says what its script did, and opens to the script as JavaScript and the result as a tree.

What changed

  • Titles (conversation/cmuxExec.ts). A finished script's title comes from call_log in its answer ({calls, call_log, logs, result}, cmux-tui cli/mcp/code_mode.rs). A running script's title comes from the cmux.call("<name>") and cmux.<tool>( calls in its source, in the present tense.

    • One verb table: workspace_list "Listed workspaces", app_agent_open / app_agent_open_tab "Opened N agent sessions", tab_create_browser / workspace_open_url "Opened N browser tabs", cmux.docs / cmux_docs "Searched cmux tools [for ]". It also covers pane, tab and terminal lists, workspace_create and workspace_rename, tab_rename, tab_create_terminal, terminal_input_write, terminal_wait, terminal reads, pane_split, tab_close, tab_focus, workspace_status_set and notification_create.
    • A change outranks a lookup. Counts are aggregated and pluralized.
    • The workspace is named when the script or its result names exactly one. It is never named by id or by current: "Opened 12 agent sessions in Leads".
    • Other steps add "and N more steps".
    • A failed call counts only when none of its kind went through.
    • Fallbacks:
      • a generic <noun>_<verb> name reads in the MCP rows' words ("Read workspace");
      • otherwise "Ran cmux script (N calls): ", then "Ran cmux script";
      • a malformed response falls back to the script's own calls.
  • One row per script. Consecutive scripts no longer fold into one "×3" line (toolGroups.ts), so each reads by its own content.

  • Opened (McpCallRows.tsx CmuxExecDetails):

    • the script as JavaScript with real line breaks and coloured tokens (jsHighlight.ts, synchronous, the pane's ANSI code roles);
    • the tools it called, with counts and failures;
    • the result and any error as a key/value tree (valueTree.ts): YAML-like, text unescaped with its line breaks, nested JSON text parsed, host-injected context dropped;
    • console logs.

    A response with a number a double would round shows as it came. The mcp__… name and the request and response as they came are behind Raw (the existing toggle the sub-session cards use). They are out of the default view but one click away. Find reads what the opened row draws.

  • Known MCP tools covered. cmux_exec, cmux_docs, and the cmux server's own tools called directly (workspace_*, pane_*, tab_*, terminal_*, screen_*, window_*, notification_*, app_*): their shapes are ours. Tools the server folds in from other servers (cua_*, subsessions_*, secrets_*, forum_*, acpmux_*) keep their own rows and cards. Every other MCP server keeps the generic line, and no other tool's row changes. The running line (toolPaths.ts) uses the same title.

  • Cost. Titles, the script view and the opened parts are derived once per tool-call snapshot (WeakMaps on the immutable call, per language for titles). Nothing is derived per render or per streamed token. Nothing is parsed for a closed row beyond its title. This adds no O(rows) work per token.

  • 52 new strings in Localizable.xcstrings (21 languages, plurals per CLDR; non-English needs_review).

  • .github/design-surfaces.json maps the transcript's tool-row files to design/chat-transcript.md.

Spec: leo-workspace design/chat-transcript.md, section "cmux_exec and known MCP tools" (teamleaderleo/leo-workspace#1791).

Stills

Leo: "WE DONT NEED FUCKIGN SCREENSHOTS JUST LET ME TRY THE SHIT OUT". There are no stills; try the cmux script row in the nightly or dogfood build. The gallery state is agent-pane.cmux-exec-rows.

Tests

  • webviews/test/cmux-exec-rows.test.tsx (11 tests):
    • titles from call_log, from a running script and from a fallback;
    • aggregation and failure counting;
    • malformed responses;
    • direct cmux tools against folded servers' tools;
    • JS tokens and the value tree;
    • three rows reading distinctly;
    • the opened view: highlighted JS, a structured result, Raw keeping mcp__cmux__cmux_exec;
    • Find text.
  • Unchanged and green: agent-pane-mcp-tool-rows, toolGroups, ToolVerb, pane-english, agent-pane-locale, chat-find, subsessionCalls, transcript-density, tool-failure and subsession-disclosure.

Observed / Not run

  • Observed: the bun suites above, run locally (pass). scripts/web-lint.sh: no findings in changed files. Its remaining failures (design tokens, layers, fmt, lint) are all in webviews/src/pages/settings/* and are red on base ad871573723. prepush-gate.py: no new violations. test/gallery-coverage.test.ts has 3 failures that are red on base and do not name this entry.
  • Not run: a Mac build (no Swift changed). WebKit gallery stills (Chromium only).

Changelog

  • Changed: an agent's cmux script row says what it did ("Opened 12 agent sessions in Leads") and opens to the script and its result, not escaped JSON.

risk: normal (transcript rendering for one MCP server's tools only).

Size: about 1,450 lines, of which 52 are the string catalog's one-line entries, about 290 are the new test file and about 190 are the gallery entry. The renderer, its verb table, the tokenizer and the tree view are one feature with its tests, so this PR is not split.

Agent session: agent session

🤖 Generated with Claude Code


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


Summary by cubic

Renders each cmux_exec row in the agent transcript so it says what its script did instead of "Called cmux · cmux exec", and opens to the script as highlighted JavaScript and the result as a tree rather than escaped JSON.

  • A finished script's title comes from the call_log in its result; a running one's from the cmux.call(...) calls in its source.
  • Consecutive scripts no longer fold into one ×N row.
  • An opened row shows the script as highlighted JavaScript, the calls with counts and failures, console logs, and the result and error as a YAML-like tree; the raw mcp__cmux__cmux_exec call stays behind the existing Raw toggle.
  • cmux_docs and the cmux server's own tools get the same titles; other MCP servers keep their current rendering.
  • Titles and opened parts are derived once per tool-call snapshot (WeakMaps), never per render or token, and Find reads what the opened row draws and highlights across its colored tokens.
  • A cut call log (call_log_truncated, or fewer entries than the reported count) never gives a wrong count: such a script's title says its total calls instead.
  • A number a double would round, in any JSON layer including a nested text block, shows the response as it came rather than parsed as a tree.
  • A direct tool's cached title is reused only while its workspace name matches, so a workspace named by the run's later identifiers still appears in the title.

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

View guided diff Turn on auto-fix

…vaScript and a result tree

Leo, 2026-10-30: "these should be rendered better than just cmux called cmux exec we know what these are supposed to be".

A cmux_exec call's title comes from its result's call_log once it finished
("Opened 12 agent sessions in Leads") and from the cmux.call("x") / cmux.x(
calls in its script while it runs ("Listing workspaces"); cmux_docs and the
cmux server's own tools called directly read the same way. Each script is its
own row. Opened, the row shows the script as highlighted JavaScript, the calls
it made, its result and error as a key/value tree and its logs; the raw
mcp__cmux__cmux_exec call stays behind Raw. Titles and parts are derived once
per tool-call snapshot (WeakMaps), never per render or token.

Tests: test/cmux-exec-rows.test.tsx (11).

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

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

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

Review cea1da7: 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=pr28698 -f ref=lane/ux-cmux-exec-rows -f platform=macos -f backend_mode=local

Gallery before/after for f75b0ed: 16 states rendered, 0 images differ, 2692 more touched states not rendered. Images are in the run's artifact, not on this public PR; the independent review of this head reads them on its own, so the PR needs no hand-made screenshots of these states.

gh run download 41230546646 -R manaflow-ai/cmux -n gallery-advisory-28698-f75b0ed8dad52595738a2f1d505c6aad22c9bfcf (then read gallery-review.json)

States
State Theme Status Diff Picked by
agent-pane.access-offer/turn-no-credits Apple System Colors Light unchanged 0% own conversation.css
agent-pane.access-offer/turn-no-credits Apple System Colors unchanged 0% own conversation.css
agent-pane.access-offer/turn-low-credits Apple System Colors Light unchanged 0% own conversation.css
agent-pane.access-offer/turn-low-credits Apple System Colors unchanged 0% own conversation.css
agent-pane.access-offer/turn-no-offer Apple System Colors Light unchanged 0% own conversation.css
agent-pane.access-offer/turn-no-offer Apple System Colors unchanged 0% own conversation.css
agent-pane.approval-edits/single Apple System Colors Light unchanged 0% own conversation.css
agent-pane.approval-edits/single Apple System Colors unchanged 0% own conversation.css
agent-pane.approval-edits/grouped Apple System Colors Light unchanged 0% own conversation.css
agent-pane.approval-edits/grouped Apple System Colors unchanged 0% own conversation.css
agent-pane.approval-edits/single-multiedit Apple System Colors Light unchanged 0% own conversation.css
agent-pane.approval-edits/single-multiedit Apple System Colors unchanged 0% own conversation.css
agent-pane.approval-edits/grouped-multiedit Apple System Colors Light unchanged 0% own conversation.css
agent-pane.approval-edits/grouped-multiedit Apple System Colors unchanged 0% own conversation.css
agent-pane.approval-edits/single-long Apple System Colors Light unchanged 0% own conversation.css
agent-pane.approval-edits/single-long Apple System Colors unchanged 0% own conversation.css
agent-pane.approval-edits/grouped-long Apple System Colors Light unchanged 0% own conversation.css
agent-pane.approval-edits/grouped-long Apple System Colors unchanged 0% own conversation.css
agent-pane.background-activity/duration-columns Apple System Colors Light unchanged 0% own conversation.css
agent-pane.background-activity/duration-columns Apple System Colors unchanged 0% own conversation.css
agent-pane.background-activity/pinned-live-tail-light Apple System Colors Light unchanged 0% own conversation.css
agent-pane.background-activity/pinned-live-tail-light Apple System Colors unchanged 0% own conversation.css
agent-pane.background-activity/pinned-live-tail-dark Apple System Colors Light unchanged 0% own conversation.css
agent-pane.background-activity/pinned-live-tail-dark Apple System Colors unchanged 0% own conversation.css
agent-pane.background-activity/after-latest-message-light Apple System Colors Light unchanged 0% own conversation.css
agent-pane.background-activity/after-latest-message-light Apple System Colors unchanged 0% own conversation.css
agent-pane.background-activity/after-latest-message-dark Apple System Colors Light unchanged 0% own conversation.css
agent-pane.background-activity/after-latest-message-dark Apple System Colors unchanged 0% own conversation.css
agent-pane.background-activity/compact-pill-light Apple System Colors Light unchanged 0% own conversation.css
agent-pane.background-activity/compact-pill-light Apple System Colors unchanged 0% own conversation.css
agent-pane.background-activity/compact-pill-dark Apple System Colors Light unchanged 0% own conversation.css
agent-pane.background-activity/compact-pill-dark Apple System Colors unchanged 0% own conversation.css

Pick other states with a Gallery: entry.id, entry.id/variant line in the PR body.

Passes: CI passes on f75b0ed8da.

CI passes on f75b0ed8da (run 41232305245 attempt 1).

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

Shipped in:

@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@blacksmith-sh

blacksmith-sh Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Found 3 test failures on Blacksmith runners:

Failures

Test View Logs
tab: tabGroup.addTab is not a top-level row/tab: tabGroup.addTab is not a top-level row View Logs
tab: tabGroup.create is not a top-level row/tab: tabGroup.create is not a top-level row View Logs
tab: tabGroup.removeTab is not a top-level row/
tab: tabGroup.removeTab is not a top-level row
View Logs

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

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6-astra high fast, review-router): BLOCK at 17e9270

  • P2 — webviews/src/agent-session/acpmux/conversation/cmuxExec.ts:296: finishedSteps trusts the retained callLog even when the server marks it truncated at 200 records. Scripts with more calls get incorrect titles and omit later calls from the opened details.

  • P2 — webviews/src/agent-session/acpmux/conversation/cmuxExec.ts:464: shown() rejects null, so a successful script returning null has no result section in the structured view or Find text; it is only available through Raw.

VERDICT: BLOCK

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6-astra, independent, carried): SHIP at 17e9270

The Astra review at this head (issuecomment-6540841020's sibling, issuecomment-6540841178) marked both code findings as deferrable edge cases, filed as #29095 and #29096. Its only remaining blocker was the placeholder Stills section. Screenshots no longer gate a UI PR (leo-workspace#1855; Leo: "WE DONT NEED FUCKIGN SCREENSHOTS JUST LET ME TRY THE SHIT OUT"), and the body now says so. With no blocking finding left, the verdict at this head is SHIP.

Follow-ups:

Conflicts in McpCallRows.tsx and chatFind.ts: keep the cmux script
path (execParts, prettyOutput for its raw response) and take base's
mcpOutputResult for every other MCP call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-63b9964940c20354b639
…r would round a number; title cache follows the workspace name

readAnswer checked number precision only on the outer response, after
findAnswer had already parsed a text block inside it, so a result of
9007199254740993 in the text block drew as ...992. Each layer the
answer is found through is now checked before its numbers count: one
that would round one shows the response as it came (rawResponse).

cmuxTitle cached a direct call's title per tool and language only, so a
row drawn before the run's names resolved its workspace kept "Opened 1
agent session" without "in Leads". The cache entry now carries the
workspace name it was made with and is used only when it matches.

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

Copy link
Copy Markdown
Collaborator Author

Review (codex-review-box/gpt-6-astra high, lane-place): SHIP at f65beac

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

  1. [P2] [follow-up] webviews/src/agent-session/acpmux/conversation/cmuxExec.ts:480 — A completed script whose JSON answer contains "result": null omits the Result section entirely because shown rejects null, even though valueTree supports rendering null. Use the answer’s presence flag so legitimate null results remain visible and searchable.

Follow-ups:

  • [P2] [follow-up] cmuxExec.ts:480 — Preserve and render top-level null results.

cmux-bot[bot]
cmux-bot Bot previously approved these changes Oct 31, 2026

@cmux-bot cmux-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cmux-bot approves f65beac: an independent review passed this exact head (review box review-28698-f65becef receipt by teamleaderleo). A push needs a new review; a later BLOCK at this head dismisses this approval.

@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent October 31, 2026 09:30 Inactive
@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent October 31, 2026 09:34 Inactive
@cmux-bot
cmux-bot Bot dismissed their stale review October 31, 2026 09:38

A newer verdict blocks 15c723f (no independent SHIP at the head).

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol high fast, review-router): BLOCK at 2600e4d

  • P2: Find can't reveal matches that span coloured tokens. webviews/src/agent-session/acpmux/conversation/McpCallRows.tsx:319: the new script and tree containers lack data-find-join. Find indexes their combined text, but paintFindHighlights searches each text node separately, so searching await cmux reports a match with no highlight or scroll target. Add joined-text handling to the script, result and error containers.
  • P2: batch titles treat an incomplete call log as complete. webviews/src/agent-session/acpmux/conversation/cmuxExec.ts:362: the server records only the first 200 calls while answer.calls can reach 2,000, so a script that closes 250 tabs reads "Closed 200 tabs". Detect a truncated log and use a qualified summary or the total-call fallback. (Same as cmux_exec rows: a truncated call_log (over 200 calls) gives a wrong title and drops later calls #29095.)

VERDICT: BLOCK

…g gives no count

Review at 2600e4d (gpt-6.1-sol): (1) the script, result and error blocks had no
data-find-join, so Find counted 'await cmux' but painted no highlight across the
token spans; each block is now one Find run. (2) The server keeps only the first
200 calls of a script that made more (call_log_truncated), so a 250-tab close
read 'Closed 200 tabs'; a cut log (the flag, or fewer entries than calls) now
says the script's total ('Ran cmux script (250 calls): tab close').

bun test test/cmux-exec-rows.test.tsx: 2 fail without the src change, 14 pass with it.

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

cursor Bot commented Oct 31, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

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

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

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6-astra, independent review box): SHIP at cea1da7

The box reply had SHIP glued to a paragraph; carried here verbatim in meaning. Clears the BLOCK at 2600e4d: both P2s fixed at this head (data-find-join on the script/result/error blocks; a cut call log says the total).

Follow-ups:

  • [P2] direct cmux tools outside the verb/generic tables keep the generic row: manaflow-ai/cmuxterm-hq#5690

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Correction to the receipt above: the P2 follow-up is manaflow-ai/cmuxterm-hq#5692 (not #5690).

@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent October 31, 2026 18:40 Inactive
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (next-review patch-id; carried): SHIP at f75b0ed

The independent SHIP at cea1da7 carries to this head: the PR's own change (git diff $(git merge-base feat-cmux-next HEAD) HEAD) has the same git patch-id --verbatim (c9ce8e1fe370593e) at both heads, and no merge between them was resolved by hand. Nothing new to review. A newer BLOCK, or any change to the PR's own diff, ends the carry.

@teamleaderleo teamleaderleo removed the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@cmux-bot
cmux-bot Bot merged commit 7d69826 into feat-cmux-next Oct 31, 2026
235 of 238 checks passed
@cmux-bot
cmux-bot Bot deleted the lane/ux-cmux-exec-rows branch October 31, 2026 20:20
teamleaderleo added a commit that referenced this pull request Nov 2, 2026
* ci guards: depth-limited fetches only in shallow checkouts (base red)

scripts/measure/fresh-home-launch.sh and workers/cmux-vm/scripts/test-mesh-label-compat.ts passed
--depth to a fetch in the caller's checkout, which turns a full clone shallow. Both now pass it only
when git rev-parse --is-shallow-repository says the checkout is already shallow.

Regressed by #30495 (fresh-home-launch.sh) and #30501 (test-mesh-label-compat.ts).

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

* ci guards: map the transcript's TurnRows.tsx in place of the deleted MessageGroupRow.tsx (base red)

#29800 removed MessageGroupRow.tsx (every sent message is its own card); TurnRows.tsx now draws
each sent message row in order (SentMessageLine.tsx is already mapped), so chat-transcript maps it.

Regressed by #29800 (deleted the file #28698 had mapped).

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

* ci guards: scan cmux-next-ios/verify/ for errno-after-assertion (base red)

cmux-next-ios/verify/imsg-replies/GestureServerUITests.swift is an XCTest source outside every
TEST_ROOTS entry. Scan cmux-next-ios/verify/ (it has no errno use, so nothing else changes).

Regressed by 201101a (added the verify/imsg-replies rig; landed by a safe-push train, no PR).

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

* ci guards: list the four manual-lane workflow readers in UNWIRED (base red)

tests/test_ci_blacksmith_linux_sizes.py, test_ci_overflow_pick_reads_pool.py,
test_nightly_next_control_rescue.py and test_ci_cloud_probe_rescue.py read .github/workflows/
but are lane = "manual" in tests/test-execution.toml (no new CI tests; wiring them as linux-guard
reddened tooling-tests on every PR). They join UNWIRED with the same reason as the other
manual-lane entries, and each says when to run it by hand.

Regressed by #30360, #30878, #30657 and #30796 (each added its test without an UNWIRED entry).

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

* ci guards: fleet rule lints pass again (base red)

"ci: Validate fleet rule lints" failed on feat-cmux-next in three rules.

big-red: the personal machine's name left product code and tests. Doc
comments say "one lane VM" / "<host>"; the webview tests use my-vm (its
machine hue still differs from gpu-box's).
  Regressed by #28105 (peer_handshake_tests.rs, list_reply.rs,
  list_reply_tests.rs), #29495 (host_update_tests.rs), #29126
  (chatOpenPacedList.test.ts), #31108 (project-chooser-machine-tag).

pkill: the iOS verify rigs and the tool-evals remote daemon stop only
what they started. imsg-replies records the conversation-sim, the
recorders (simctl itself, not the xcrun shim) and the gesture servers in
pidfiles and signals those PIDs after checking the command line still
matches; pq_browser_events records its two site servers' PIDs; the
tool-evals remote stop kills the setsid group whose PID the launcher
recorded beside the socket.
  Regressed by 201101a (imsg-replies, first in #22328), #25915
  (pq_browser_events.py), #29080 (tool-evals/session.mjs).

Allowlisted as safe, with reasons: scripts/ci/macos-stubs/Security.tbd
(a linker stub listing symbol names, no keychain read; #27603) and
tests/tool-evals/cases/hard.ndjson (an eval case forbidding pkill;
#28738).

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

* ci guards: determinize python tests flagged by test-determinism (base red)

Regressed by the commits introducing each assertion (see report).

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

* ci guards: nightly Xcode selection, unused anchors, daemon retention key (base red)

- Validate macOS jobs select a pinned Xcode: the nine flagged nightly.yml
  jobs already run select-ci-xcode.sh through the `- *select-xcode` step
  alias; the guard did not expand step aliases. It now resolves `- *name`
  to the `- &name` step, with self-tests for both an alias to the selector
  and an alias to another step. Regressed by #30471 (steps turned into
  aliases to keep nightly.yml under its size limit).
- Lint every workflow: drop the five sign-nightly-identity-legacy-* anchors
  left after the Blacksmith twin that aliased them was removed
  (a7b6a61, "legacy sign and deltas run on owned Macs only"; the twin
  came from #27357). No behaviour change.
- upload-artifact retention: the 14-day cmux-nightly-remote-daemon upload
  moved from build-nightly-app to build-nightly-remote-daemon in #25935
  with its retention unchanged; move the allowlist key with it.

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

* ci guards: media tours and visual-gate state match their validators (base red)

Drop the unknown step key "record" (re-added after #26888 removed it by #27933 and f283a2e), the unimplemented top-level frameInterval in home-chief-shimmer (c022070), the focusLeft right after closePane in palette-chat-toggles (#25405), split pane-polish (31 steps) into pane-polish and pane-polish-themes, and give the app-store-consent-deploy state a synthetic host that is not an email shape (4a05792).

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

* ci guards: allowlist remaining test-determinism findings with reasons (base red)

Swift/web/tool-evals findings cannot be determinized without Mac or webview runs; each entry names its reason and adding PR.

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

* ci guards: Linux pool for the push kicks; record pool readers as unrescued (base red)

- cmux-next-base-tier-kick, cmux-next-dev-kick and ios-internal-nightly-trigger waited on
  one fixed Blacksmith Linux size (!startsWith(LINUX_RUNNER) || overflow 8 || 2 vCPU). They
  now read fromJSON(vars.RUNNER_POOLS).linux after the fork branches. Their pinning tests
  (base tier, DEV kick, iOS internal builder) assert the pool read instead. Regressed by #30837.
- owned_pool_rescue.py: feat-cmux-next's UNRESCUED_WORKFLOW_PATHS records the secretless
  Linux pool readers the owned-pool sources test found unlisted (fast gates, gallery health,
  PR checks, cloud backend check, web validation, and the three kicks above), with the existing
  _POOL_READ_WHY. Regressed by #29844 and #31138.
- test_ci_linux_single_size.py: drop cmux-next-fast-gates.yml from FIXED_PAIR (moved by #31138);
  test_ci_overflow_pick_reads_pool.py: drop the two kicks from PICKS.

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

* ci guards: signing-runner and retry-attempt guards follow the owned-Mac signing rule (base red)

Signing keychain guard (rule 7): accept owned-pool runner variables only where
safety is proven in the job itself:
- 7a: a job in glaeda's exclusive signing class whose first step is the
  verbatim claim gate may fall back to CI_NIGHTLY_NEXT_BUILD_RUNNER /
  CI_SIDE_LANE_RUNNER (nightly.yml sign-nightly-identity-legacy and
  generate-nightly-deltas, owned-only by design, Lawrence a7b6a61;
  deltas' Sparkle key added in #29261). test_ci_signing_owned_mac.py holds the
  claim script, always() keychain delete and twin/picker rules for them.
- 7b: stable-next.yml sign (#27898) reads every signing secret only under
  env.SIGN == 'developer-id', whose condition picks a literal Blacksmith label
  before any variable; its owned dry-run holds no key. Proven structurally.

Retry guard: cmux-next.yml swift-test/daemon-test/generated-files' side-lane
fallback (#29814) is now gated at run_attempt <= 3 for the bot, so the bot's
attempt 4 takes blacksmith-6vcpu-macos-26 like every other cmux-next fleet
fallback (#22565). The three owned-only nightly jobs are exempt while they keep
exactly their owned route. The textual check now forbids naming attempts after
2 (== 3+), as its comment says, instead of also == 2 (#29847 nightly.yml,
#29130 cmux-next-guards.yml name the bot's attempt 2, which may stay owned).

Regressed by #29814, #27898, #29261, #29847, #29130 and a7b6a61.

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

* ci guards: owned Mac pools through the picker/vars pattern; follow nightly step scripts for sentry-cli (base red)

check_no_self_hosted_fleet_runners
The guard told every workflow to "use a cloud label so required jobs never
land on a mini". That is outdated policy: owned fleet Macs now come first for
macOS jobs (Linux, then owned Macs through the picker, Blacksmith last),
release signing may run on owned Macs, display tests gate nothing, and the
NEXT nightly runs on the fleet with the nightly-only minis dedicated to it.
The guard kept up only through exact per-workflow selector strings, which
drifted with every routing change and turned the guard red on base:
- cmux-next.yml swift-test/daemon-test/generated-files: the glaeda-side
  deferred fallback (regressed by #29814) and the attempt-1 gui selector
  (#28605, #27853)
- cmux-next-base-autofix.yml / cmux-next-pr-autofix.yml: the gui selector
  gained a re-run predicate (#27853)
- nightly.yml sign/prepare jobs: owned pool as the final fleet fallback
  (#29154)

Fix the guard instead of moving those jobs to Blacksmith: an owned POOL label
(glaeda-[<lane>-]<class>-xcode-<version>, served by many minis, never one
host) is accepted as a quoted literal in a ${{ }} runs-on that routes through
the declared picker/vars pattern (reads needs.<job>.outputs.* or vars.*).
In a workflow a pull request can trigger, the fork pull request branch
(fork heads to Blacksmith) must still come first on the line, so fork code
never reaches an owned Mac (runner trust unchanged). Still refused: bare
self-hosted/macOS/ARM64, single-host labels (cmux-macos, mac-mini, slot-N,
tart-*, ...), macos-26 outside the fork branch, and an owned label named
directly with no picker or variable route. The exact gui selector strings are
replaced by this rule; self-tests cover each accept and refuse case.

check_sentry_cli_install_portability
#31013 moved nightly.yml's "Upload dSYMs to Sentry" body into
scripts/ci/nightly-steps/app-upload-dsyms.sh (size budget), so the guard no
longer saw the ./scripts/upload-sentry-dsyms.sh call. The guard now reads
the step's step script too. Regressed by #31013.

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

* ci guards: change-area tests follow the RUNNER_POOLS and devEngines routing; helper walk skips non-running namers (base red)

- release-admission and remote-daemon-admission read fromJSON(vars.RUNNER_POOLS).linux by
  design since #30878; the two tests now assert that instead of vars.LINUX_RUNNER.
- fast-checks.yml's setup-bun reads the bun pin from webviews/package.json devEngines at run
  time (#29757); the setup-bun test accepts a steps.<id>.outputs.version whose step reads
  devEngines, and still requires the literal pin everywhere else.
- indexnow.yml pinned bun 1.3.14 when #30697 backported it; it now installs devEngines' 1.4.4.
- ci_helper_areas() failed open on files that name a helper without running it, so
  owned_pool_rescue.py, owned_build_state.py and e2e_warm_derived_data.py stopped routing by
  where they run. Regressed by #28528 (test_cmux_next_route.py names owned_pool_rescue.py as a
  routing input and itself), #28513 (docs/ci/cmux-next-tiers.md names that test), #30657
  (nightly_next_control_rescue.py's only test is manual-lane), #28568 (.github/warm-sources.json
  prose) and #30467 (base_red_excuse.py's guard-test table and its test's fixtures). The walk
  now ignores a test naming itself, docs, a Python test naming it only in a docstring, the
  listed path tables and warm-sources.json, and treats a test the registry declares in an inert
  lane that nothing imports as a dead end. An unregistered test nothing names still fails open.
- cache_restore_receipt.py now routes precisely (macOS + Release, via ci-macos.yml's
  cache-restore action) instead of failing open; its assertion says so.

python3 tests/test_ci_change_areas.py: 4 of 234 failed before, 0 after.

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

* ci guards: review fixes for #31366 (daemon pgid, fork-first owned pools, stale pidfiles)

- tests/tool-evals/session.mjs: the remote launcher recorded $! of the backgrounded && chain
  (the wrapping subshell), so the stop signalled a group that did not exist. setsid now starts
  a shell that writes its own PID and execs the daemon, so the recorded PID is the daemon's
  process group. Checked locally: the recorded PID equals the daemon's PGID and SID, and
  kill -TERM -- -PID stops it.
- tests/test_ci_self_hosted_guard.sh (security): the owned-pool exemption in pull request
  workflows only required the fork branch somewhere before the owned literal, so
  `needs.x.outputs.runner || <fork branch> || 'glaeda-...'` passed. The fork branch must now be
  the first selector of the expression (after only the hosted repository_owner clause). New
  negative self-tests for reordered expressions; red with the old match, green with the new.
- cmux-next-ios/verify/parity/pq_browser_events.py: stop_site() now stops only servers this run
  started (serve() reports each pidfile it wrote), kills a PID only while its command line is
  still that server's, and removes the pidfile after. Checked with a local stub harness.

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

* ci guards: universal nightly helper runs-on match works under mawk (base red)

The helper runs-on expected string passed to awk -v carried `\&\&`, which gawk reads as `&&`
(with a warning) but mawk, the default awk on Ubuntu runners, keeps as `\&\&`, so the whole
expression never matched and "nightly must build and hand off the macOS 26 helper" failed.
Plain `&&` means the same in both. Regressed by #30824.

bash tests/test_nightly_universal_build.sh: FAIL before, PASS after (mawk 1.3.4).

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

---------

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

This branch was previously deployed

1 inactive deployment
ci-routing-agent — f75b0ed8 Deployed Oct 31, 2026 by cmux-bot[bot] via cmux-tui tree preflight #90997
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