Skip to content

fix(cmux NEXT agent pane): a failed screencapture is an error, not an Esc (carries part of #20827) - #27892

Merged
teamleaderleo merged 7 commits into
feat-cmux-nextfrom
lane/ao-carry-20827-capture-status
Oct 30, 2026
Merged

teamleaderleo merged 7 commits into
feat-cmux-nextfrom
lane/ao-carry-20827-capture-status

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

Carries the AgentPaneCapture half of #20827 (Austin's error-handling audit for #20776). Those commits credit Austin with Co-authored-by, and the string and its 21 translations are his. #20827 stays open for its owner to close.

The rest of #20827 is superseded: base moved the Changes view's git runs onto AsyncProcess (92598cb, fcafb74: SIGTERM at the deadline, SIGKILL after the grace, awaited without a thread), which covers its AgentPaneGitProcess deadline rewrite. Its SSHRunner change was reformatting only.

What it fixes

composer.capture (the composer's + > Attach Screenshot… / Attach Window…) counted every screencapture run that started as a pick. One that ended with an error (no Screen Recording grant, a crash) answered the page {cancelled: true}, the same as an Esc, and any partial file it wrote stayed in the temp folder.

  • AgentPaneCapture.capture returns .taken or .failed(reason), and takes an executable seam so tests can use a stub.
  • With a file written, the exit status decides: 0 is the image, anything else is .failed and the partial file is removed. (A partial file at status 0 can't be told apart from a capture.)
  • With no file, an end that prints nothing is an Esc (.taken, no image), whether this macOS version exits 0 or non-zero on Esc. An end that prints why is .failed, even at status 0.
  • A signal is always .failed. On .failed the system's message is logged (agent-pane.capture) and the page gets capture.failed with "The screen capture failed."
  • File operations go through FileManager.checked (the guardrail's seam).

Test

AgentPaneCaptureTests (CmuxNextAppTests), with stub screencapture scripts:

  • aFailedScreencaptureIsAFailureNotACancel: prints a reason and exits 1. Expects .failed. Red at 6c1f6a398d6.
  • anErrorSaidWithStatusZeroAndNoImageIsAFailure: prints a reason, exits 0, writes no file. Expects .failed. Added after review.
  • aFailedPickLeavesNoFile: writes a partial file and exits 1. Expects no file left. Red at 6c1f6a398d6.
  • anEscapedPickIsTakenWithNoImage and aQuietNonzeroExitWithNoImageIsACancel: an Esc under either exit convention stays a cancel. Green on both commits.

Observed / Not run

  • Observed: scripts/cmux-next/prepush-gate.py: no new violations.
  • Not run here: the Swift suites. This host builds no Swift, so the red at 6c1f6a398d6 and the green at the head come from this PR's CI. I have not checked a real screencapture Esc status on macOS; the quiet-exit rule is there so either convention stays a cancel.

Risk: normal. One reply path in the agent pane; the page already treats a failed call as no attachment (App.tsx .catch(() => null)).

No capture: no visible change on success or Esc; a failed capture answers an error instead of a cancel.

Changelog

Fixed: A screenshot attach that fails no longer reads as a cancel and leaves no temporary file behind.

Agent session: agent session

🤖 Generated with Claude Code

teamleaderleo and others added 2 commits October 30, 2026 03:25
From #20827: composer.capture treats any screencapture that started as
a pick, so one that exits with an error (no Screen Recording grant, a
crash) answers the page {cancelled: true}, and a partial file it wrote
is left in the temp folder. AgentPaneCapture.capture gets a result type
and an executable seam for a stub; the failure path is unchanged here.

Co-authored-by: Austin Wang <austinwang115@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-b22822edaca3611659c9
…led and leaves no file

From #20827's AgentPaneCapture half. composer.capture now reads
screencapture's end: an Esc (no file, nothing said, whatever status the
macOS version gives it) is still {cancelled: true}; an end that says
why, leaves a partial file or comes from a signal answers the page the
capture.failed error ("The screen capture failed.", Austin's string and
translations), logs the system's message, and removes the partial file.

The rest of #20827 (AgentPaneGitProcess deadlines) is superseded by base
moving those runs onto AsyncProcess.

Co-authored-by: Austin Wang <austinwang115@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-b22822edaca3611659c9
@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

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

Review 6e2306b: 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=pr27892 -f ref=lane/ao-carry-20827-capture-status -f platform=macos -f backend_mode=local

red: cmux-next checks (god files, concurrency, crash safety, l10n); no finished base run on 97133a4 to compare; do not merge until green; see lane-rules

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/41071509570 at a6ca7ea failed: cmux-next generated files (shared build).
Those jobs last passed at 8e867c5. This pull request is one of 5 merged since: #28088, #27811, #27892, #28349, #28078.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

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

blacksmith-sh Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Found 28 test failures on Blacksmith runners:

Failures

Test View Logs
/tmp/tmp36fkuopq/summary-missing.json (live):/summary-missing.json (live): View Logs
/tmp/tmpeqoru131/summary-fail.json (live):/summary-fail.json (live): View Logs
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows/
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows
View Logs
[new_workspaces] the window draws no frames/[new_workspaces] the window draws no frames View Logs
[tabs] debug.tab_switch timed 0 of 40 switches/
[tabs] debug.tab_switch timed 0 of 40 switches
View Logs
[tabs] the window draws no frames/[tabs] the window draws no frames View Logs
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='', primary='failure')/
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='', primary='failure')
View Logs
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='', primary='success')/
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='', primary='success')
View Logs
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='blacksmith-16vcpu-ubuntu-2404', primary='failure')/
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='blacksmith-16vcpu-ubuntu-2404', primary='failure')
View Logs
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='blacksmith-16vcpu-ubuntu-2404', primary='success')/
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='blacksmith-16vcpu-ubuntu-2404', primary='success')
View Logs

...and 18 more test failures. View all on Blacksmith

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

…and no image is a failure

Review of #27892: with no file, the status no longer decides; an end
that says why is capture.failed even at status 0, and only a silent one
is an Esc. With a file, status 0 is the image (a partial file at status
0 cannot be told from a capture).

Co-authored-by: Austin Wang <austinwang115@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-b22822edaca3611659c9
…ff the main thread

Review of #27892: under NonisolatedNonsendingByDefault, capture() ran
on its caller's actor (the main actor for composer.capture), so the new
FileManager.checked.fileExists/removeItem touched the disk on the main
thread. @Concurrent runs it on the global executor, as the module's
other async disk work does.

Co-authored-by: Austin Wang <austinwang115@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-b22822edaca3611659c9
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Independent review (codex gpt-6.1-sol high; Astra via SR2 hung): SHIP at 6e2306b

No concrete correctness defects found. The capture result handling, signal/status classification, partial-file cleanup, off-main file operations, localization, and regression tests are consistent with the PR description. I did not modify files or run builds.

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

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol high; carried): SHIP at 3f9fe6e

Carried from the independent SHIP at 6e2306b across clean merges of feat-cmux-next (and, where present, an empty CI re-run commit): the PR's own diff against the base is byte-identical (same git patch-id --stable). The base merge picks up the base reds fixed since (next-autoland: "base red since fixed").

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

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol high; carried): SHIP at 8689d49

Carried from the independent SHIP at 6e2306b across clean merges of feat-cmux-next (now with #28036, the base-red fix): the PR's own diff against the base is byte-identical (same git patch-id --stable).

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

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol high; carried): SHIP at b16d04b

Carried from the independent SHIP at 6e2306b across a clean merge of feat-cmux-next 97133a4 (past 4154db0, the CloudAppLinkOwnerAnswerTests test-target compile fix, and the String tables fix): the PR's own diff against the base is byte-identical (same git patch-id --stable).

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Reds at b16d04bd1b0af993fbb5f7be4b476b77a6b445c3: none is this PR's own (base feat-cmux-next 97133a48ad0b, compared with base push run 41035698726 at dda90181daa5)

Check What failed Verdict
PR checks / link (job 123981304606) the "Update the PR's review line" step, a GitHub API write; it reads no code infra (API), not this PR
Familiar intent review warning (advisory) / warn HttpError: API rate limit exceeded for installation advisory and infra; not re-run, per the shared-quota rule
cmux-next fleet Release compile (job 123981579452) no result for b16d04bd1b0af993fbb5f7be4b476b77a6b445c3 from the controller (attempt 1): the compile controller was out of disk until about 10:00Z infra; no compile ran. Base push run 41022185110 at 84bca8e compiled green, and this PR's own diff is byte-identical since
cmux-next checks (job 123979102036) String tables (21 languages), No literal state roots, Sidebar-Look trailer, plus GitHub API unavailable ... rate limit exceeded base: the same red checks are in base job 123995865326 at dda9018. check-l10n.sh gives 112 errors on base 97133a4 and the same 112 here (this PR adds one key, which adds no error)
nightly-guards / Guard status (job 123983948592) routed guard jobs base red (NOW.md base-red list)

No crash, signal or cut-short test run is among them. The independent SHIP (carried, same patch-id) is in the comments above. Landing pinned to b16d04bd1b0af993fbb5f7be4b476b77a6b445c3.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (independent Codex gpt-6.1-sol, high, via the review router; full diff from merge base 97133a4 to this head): SHIP at b16d04b.

No P1/P2 findings.

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

This branch was previously deployed

1 inactive deployment
ci-routing-agent — b16d04bd Deployed Oct 30, 2026 by teamleaderleo via wait for same-key tree publisher #81973
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant