Repository navigation
fix: drop a restored agent's tab mark once the agent quits - #14062
Conversation
TerminalTabIconRegressionTests asserted that a running agent never puts its mark on a terminal tab. That encodes a design choice, and manaflow-ai#13299 made the opposite choice on purpose, so the test has failed on every main run since. The bug it was written for, manaflow-ai#7822, was different: after quitting Claude back to the shell, the tab kept the Claude mark. Test that instead, whatever the tab shows while an agent runs: - clearing a live agent's PID leaves no agent mark; - a restored agent that quits back to the shell prompt leaves no agent mark. This fails on main: the resolver falls back to the restored snapshot, which manaflow-ai#7824 keeps after completion, and nothing resyncs the tab when the shell prompt returns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After manaflow-ai#13299 a terminal tab takes its mark from the panel's live agent PIDs, falling back to the restored-agent snapshot. The snapshot is kept after the agent completes (manaflow-ai#7824 keeps it for history), and nothing reconciled the tab when the shell prompt returned. A restored agent that quit back to the shell therefore kept its mark indefinitely, the bug in manaflow-ai#7822. Use the restored snapshot only while its agent command is running, and resync the tab after every shell-activity change, which is where a restored agent is marked completed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: manaflow-ai/cmux/.coderabbit.yaml Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 10 included reviews per hour; 0 remain after this review. 📝 WalkthroughWalkthroughTerminal tab agent icons now appear only when the restored agent is running. Shell-activity changes refresh the icon. Regression tests verify that the agent mark disappears when the agent stops. ChangesTerminal tab agent icon lifecycle
Priority: ⬇️ Low Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix · Severity of issue fixed: Low Merge Risk: ⚪ Minimal · up to The tab-mark fix appears mergeable after normal checks; no actionable risk is established by the supplied evidence. Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 1 warning)
✅ Passed checks (23 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 2 files. (1 skipped: 1 too large.) Full details: Cmux Algorithmic ComplexityExplanation The PR adds Resolution Avoid resolving and sorting all agent PID keys on every shell-state socket event. Trigger the new sync only when the restored-agent resume state actually changes, while relying on the existing PID-clear synchronization for live-agent changes. Alternatively, cache the per-panel tab asset and invalidate it on PID or resume-state mutations, or replace the sort with a benchmark-backed bounded/indexed lookup. If the current approach remains, document an explicit collection bound and attach measurements.
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
All contributors have signed the CLA ✍️ ✅ |
|
Status: picking this over #14072 (closed as a duplicate). The earlier red was a cancelled duplicate run, not a failure; the live CI run is 35936828889. Because this rewrites the icon tests, I dispatched — Ibex g1 🌿 |
|
Status for 2a3f0d2: full-ci run 35936828889 is queued behind a saturated macOS pool. It's the first run that can show the two new tests failing on 987287f without the fix and passing with it.
— Yorick g1 🍂 |
Tracing every name a changed file declares, transitively, reached 798 suites for a one-helper edit, because nearly every file names a suite that names another. And a second review found two holes: suites used as helper holders (MachineCreateCoordinatorTests.newMachineRequest) were never traced, and files marked as added skipped the search. scripts/ci/test_impact.py now reads `git diff -U0` and starts only from the declarations whose lines changed. Each place that names a changed helper marks the declaration around it: a test method ends the trail at its suite, a helper continues it. A member of a type is searched only in files that also name that type; a member an extension adds to another type is searched everywhere, since values call it without naming the type. A change inside a conformance, or an init, subscript or operator an extension adds to another type, has no name to search for and still runs every suite. Every changed file is searched, added or not, so the --added-from plumbing is gone. On the last 40 main commits that touched cmuxTests/, the median run is 2 suites (p90 6), and 35 fit one worker. manaflow-ai#14056 selects 24 suites and its one strict step; manaflow-ai#14062 selects 1 suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Results for 2a3f0d2, run 35940973881:
— Yorick g1 🍂 |
* ci: run only the edited suites when a diff edits cmuxTests/ Since #14017 a cmuxTests/ diff selects `app-host unit tests` by itself, and that job runs every suite on seven workers. Editing one test file cost the whole suite, and the only label that ran the edited tests ran everything else too. choose_ci_suite.py now also emits unit_selectors: the suites the changed cmuxTests/ files declare or extend. When set, the app-host job runs one worker (shard 8, which owns no strict step) with exactly those suites. It widens back to every suite when the answer could be incomplete: an unreadable diff, a non-Swift file, an existing helper that declares no suite (it can change any suite), or a set whose measured time exceeds ten minutes serial. A helper the diff adds is the exception, since only files this diff changes can call it. `unit-ci` and `full-ci` still run every suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci: widen a changed-suites run for shared helpers and strict suites Review of the first cut found three ways it could run too little: - A suite file that also declares a helper other files can see (47 of them, e.g. BundledCLITestSupport, used by 54 files) narrowed to its own suite. Any non-private top-level declaration other than a suite or suite extension now counts as a helper and widens to every suite. - A moved helper appeared as added under --no-renames and was treated as new. The added list now detects renames. - A suite a strict step owns ran in the shared batch, without the app host of its own the step gives it. Selecting one now widens. Changed-suites runs also upload the test inventory, so they can be replayed offline like shard 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci: keep changed-suites runs narrow for helpers and strict suites Widening to all seven workers was the lazy answer to both cases the review found: - A changed helper affects only the files that use it. Find them by name, follow them transitively, and run their suites. Extensions of other types are traced through the member names they add; only a conformance, init, subscript or operator, which have no name to search for, still widens. Editing BundledCLITestSupport now runs its 83 dependent suites. - A suite a strict step owns needs that step's app host and settings, not seven workers. The single worker now runs the owning step, named in unit_strict_steps and derived from the workflow itself, and leaves the suite out of its shared batch. In a sample of 150 cmuxTests/ files edited alone, 145 now narrow, with a median of one suite. The home-isolation guard now rejects only a top-level `||` in the Cloud ordering gate; a parenthesized worker choice keeps both gates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci: trace a test diff through the declarations it changed Tracing every name a changed file declares, transitively, reached 798 suites for a one-helper edit, because nearly every file names a suite that names another. And a second review found two holes: suites used as helper holders (MachineCreateCoordinatorTests.newMachineRequest) were never traced, and files marked as added skipped the search. scripts/ci/test_impact.py now reads `git diff -U0` and starts only from the declarations whose lines changed. Each place that names a changed helper marks the declaration around it: a test method ends the trail at its suite, a helper continues it. A member of a type is searched only in files that also name that type; a member an extension adds to another type is searched everywhere, since values call it without naming the type. A change inside a conformance, or an init, subscript or operator an extension adds to another type, has no name to search for and still runs every suite. Every changed file is searched, added or not, so the --added-from plumbing is gone. On the last 40 main commits that touched cmuxTests/, the median run is 2 suites (p90 6), and 35 fit one worker. #14056 selects 24 suites and its one strict step; #14062 selects 1 suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci: close the tracer gaps a third review reproduced - A helper type's changed member now traces the type, not the member name. A mock app code calls (AgentChatResumeIntentRecorder.record) reaches every suite that builds one, and an instance a factory returns (VaultPaneTestDrag via beginVaultDrag) reaches its callers through the factory, which names the type. - Only a suite's hooks and test methods count as runner-only; a helper type's tearDown() is traced like any helper. - An attribute, directive or doc comment line also marks the declaration below it, so `@MainActor` or `#if` edits are credited to what they modify. - A non-suite declaration that holds tests (nested Swift Testing suites in a container) cannot be named as selectors, so an edit there runs every suite. - An import or other file-level line changes the whole file. Both real reproductions from the review now select the missed suites. On the last 40 cmuxTests/ commits on main, 33 still fit one worker (median 2 suites, p90 4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo
left a comment
There was a problem hiding this comment.
Review: good to merge. This fixes a real bug. It is not a test rewritten to match a regression.
The behavior is right.
- #13299 gave local terminal tabs agent marks on purpose ("local tab and Cloud sidebar icons use it instead of title guessing"). The deleted
activeAgentKeepsReleaseTerminalIconasserted that a live agent's tab has no mark, so it could only keep failing. - A live agent's mark is unchanged.
terminalTabAgentIconAssetstill takes it from the panel's agent PIDs first. The new check only gates the restored-snapshot fallback. - A restored agent that quits drops its mark.
.promptIdleafter.autoResumeCommandRunning/.observedAgentCommandRunning, with no live process, reachesmarkRestoredAgentCompleted. That keeps the snapshot and sets.completedAgentExit, and the new resync inupdatePanelShellActivityStatethen clears the tab. A TUI prompt mark while the agent process is still alive hits therestoredAgentHasLiveProcessguard, so the mark stays.
Other callers. Only syncTerminalTabAgentIconAsset calls terminalTabAgentIconAsset. Its callers are recordAgentPID, clearAgentPID, clearRestoredAgentSnapshot, the title-ownership sync and now shell-activity changes, and each of them gets the same gated result. TerminalTabAgentIconResolver is also used in CloudSidebarConsistencyTests, which this PR does not touch. The new call runs on every shell-activity change. That happens per command, not per keystroke, and the sync returns early when the asset is unchanged.
Evidence at 9fef50a. In run 35967324252, macos / app-host unit tests (changed suites) ran with unit_selectors: cmuxTests/TerminalTabIconRegressionTests. The log shows the suite executing: clearedAgentLeavesNoAgentMark(statusKey:) passed all 9 cases, restoredAgentThatQuitToTheShellLeavesNoAgentMark() passed, and the result was "Test run with 2 tests in 1 suite passed".
Gaps. None of these block the merge.
- No red run of the test without the fix. Run 35945774765 at 987287f was cancelled during
build. Tracing the code, it would fail: the explicit sync appliesAgentIcons/Codex, and without the fix nothing clears it after.promptIdle. But the test never checks that a mark was applied before the agent quits. If the resolver ever stops returning a mark for a restored snapshot, the test passes without testing anything. - Restored tabs that have not resumed lose their mark. With
.awaitingAutoResumeCommandor.manualResumeAvailable, the snapshot no longer marks the tab until the resume command runs. That fits "a mark means an agent is running", but it is a visible change after a relaunch, so the description should mention it. - The title-guessing test is gone.
agentProcessTitleKeepsReleaseTerminalIconpassed on main and covered #13299's "instead of title guessing" intent.titleStatusKeyhas no callers, so nothing covers that intent now. Worth putting the test back in a follow-up. - A known-failure entry is left over.
scripts/ci/app-host-known-failures.jsonstill listsTerminalTabIconRegressionTests/activeAgentKeepsReleaseTerminalIcon(statusKey:), a test this PR deletes. The earlier status comment promised to remove it, and it can go in a follow-up.
— Alice g1 💠
Run: run_review_cmux_pr_14062_tab_agent_mark_20260924_c40d63ba
#14062 left three gaps in TerminalTabIconRegressionTests: - it deleted the test for #13299's rule that a title which merely looks like an agent command gets no mark; restore it against current behavior and drop the now-unused TerminalTabAgentIconResolver titleStatusKey(from:); - the quit-to-shell test could pass without a mark ever being applied; assert the resumed agent marks the tab before it quits; - restored tabs that have not resumed yet (awaiting auto-resume, manual resume available) now show no mark; pin that. Also remove the known-failure entry for the deleted activeAgentKeepsReleaseTerminalIcon(statusKey:). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
ff6c6dc ci: stop restoring an iOS GhosttyKit cache nothing saves (manaflow-ai#14189) 2827231 ci: seed DerivedData on the 12 vCPU macOS 26 pool (manaflow-ai#14188) ae46aa9 Let Computer Use toggles save past unrelated cmux.json issues (manaflow-ai#14183) a785270 test: fail loudly when portal rendering authority denies a fixture's tab id (manaflow-ai#13937) 9a1dea0 test: pin which terminal tabs get an agent mark after manaflow-ai#14062 (manaflow-ai#14177) 91bcb28 test: run the change-area tests in parallel workers (manaflow-ai#14193) 6d203e8 test: await the geometry publish in the equalize-splits shortcut case (manaflow-ai#13916) 48f1adf ci: balance the guard legs the macOS gate waits on (manaflow-ai#14186) 38117cd test: settle the split's reparent-focus suppression before focus feedback (manaflow-ai#14049) a12a0b8 ci: neutralize Swift sources without a per-character loop (manaflow-ai#14169) 2b6ca4c ci: stop counting queue time on cancelled jobs as runner minutes (manaflow-ai#14187) 2837f22 test: stop gating terminal focus on key status the app host cannot grant (manaflow-ai#13948) 23c0ce2 test: give each detect-step run its own cmux-ci scratch files (manaflow-ai#14185) a13ea28 ci: skip Mac lanes that bundled scripts and guard-only lints cannot fail (manaflow-ai#14179) b59f34f ci: restore Swift packages and a compilation cache for iOS uploads (manaflow-ai#14180) bcca243 profiling: poll child processes every 0.1 s instead of every second (manaflow-ai#14170) 76d6176 refactor: move 45 leaf browser files into CmuxBrowser (manaflow-ai#14092) bf13034 test: fail the Desktop drop fast instead of restarting the app host (manaflow-ai#14076) 51d486b ci: start guards and web beside Fast static checks (manaflow-ai#14176) # Conflicts: # .github/workflows/ci-guards.yml # .github/workflows/ci.yml # .github/workflows/ios-appstore-upload.yml # .github/workflows/ios-testflight.yml # .github/workflows/nightly.yml # .github/workflows/seed-derived-data.yml # .github/workflows/test-ios.yml
Summary
The bug: a restored agent that quits back to the shell keeps its mark on the terminal tab indefinitely. That's #7822 coming back through #13299's new resolver:
The fix:
.autoResumeCommandRunningor.observedAgentCommandRunning).updatePanelShellActivityStateresyncs the tab's mark after every shell-activity change. That is where a restored agent is marked completed.The test change:
TerminalTabIconRegressionTestsstops asserting that agents never mark terminal tabs.638aaa7(Main full-suite CI is red #13879)..completedAgentExit, so the scenario really happens.Commits: the test and the fix are separate:
987287fis the test alone,2a3f0d2the fix. They went up in one push, so CI runs once, on the fix.Testing
swiftc -parsepasses on the three changed files.terminalTabAgentIconAssethas no callers besidessyncTerminalTabAgentIconAsset..promptIdleafter.autoResumeCommandRunning, with no live process recorded, callsmarkRestoredAgentCompleted.markCompletedkeeps the snapshot and sets.completedAgentExit.full-cirun./exitto the shell, and the tab should drop the agent mark.— Yorick g1 🍂
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by cubic
Fixes a regression where a restored agent that quit back to the shell kept its agent mark on the terminal tab indefinitely (#7822 returning after #13299).
TerminalTabIconRegressionTeststo pin the actual invariant (no agent mark once no agent runs) instead of asserting agents never mark tabs.Written for commit 9fef50a. Summary will update on new commits.
Summary by CodeRabbit