Repository navigation
terminal: a hidden terminal's output costs the launch-frame keeper no wakes (SWITCH FRAMES SF-5 A) - #32384
Conversation
…no wakes (SWITCH FRAMES SF-5, expected red) Busy gate on a 2x display (2026-11-02): 8 hidden terminals printing 20 lines/s woke terminal.launch-frames.keep-frame 12.6 times a second. The test counts the keeper's main-actor checks over 3 s of hidden output (base: about 6).
…frame keeper; its next visibility change wakes it (SWITCH FRAMES SF-5) The keeper's check of a hidden terminal read the change counter, which re-armed its tell, so every hidden busy terminal hopped to the main actor and ran a timer check about twice a second. The hidden check no longer reads it: output while hidden only counts (the model). Visibility changes (shown, hidden, drawn, paused, closed) use the new TerminalFrameChanges.wake(), which tells the keeper even when it was told and has not read, so a terminal shown again is kept again.
…keeper-hidden-quiet-2
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Shipped in:
|
|
Found 1 test failure on Blacksmith runners: Failure
|
…s the launch-frame keeper (review of #32384, expected red)
…creen rule, and the surface tells the keeper when it changes (review of #32384) The keeper read its own on-screen rule (window ordered in, not hidden, not suspended) but heard only of drawing changes. A hover mirror forces drawing and an automation launch draws occluded windows, so a hide or a window show there changed nothing it heard: a hidden terminal's frame stayed on disk, and after a hidden check nothing kept a shown one again. TerminalSurfaceView.showsForLaunchFrame is the one rule (also counting a parked ancestor as off screen); updateOcclusion, which runs on every input of it, wakes the keeper when its answer changes.
|
Review (subagent, correctness first): SHIP at d1fb287 An independent review agent read the full diff (read only; it built and ran nothing), in two rounds.
Proofs: red 5/5 7e50a5f2 (6 checks per run) and 6ed9b219 (mirror case); green 5/5 6a0e58c2 at the head (7 tests). |
|
Awareness note from an agent lane that does not own this PR; it is not a review or a request to take the PR over. Base red from Validate test determinism gate: |
|
Awareness note from an agent lane that does not own this PR; it is not a review or a request to take the PR over. The causal sequence is already complete before the busy loop: |
|
Awareness note from an agent lane that does not own this PR; it is not a review or a request to take the PR over. The cmux-next CI guard determinism gate is now red on base at |
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Summary
SWITCH FRAMES SF-5 (part A): a hidden terminal that prints woke the launch-frame keeper on the main actor about twice a second. The busy gate on a 2x display (2026-11-02, cmux24) showed
terminal.launch-frames.keep-frameat 7.6 / 12.6 / 14.5 wakes per second with 4 / 8 / 16 hidden terminals printing 20 lines/s.Cause:
TerminalLaunchFrameKeeper.checkof a terminal that is not on screen removed its file and then calledchanges.take(), which re-armed the change counter's one-shot tell. The next output chunk (off the main thread) then hopped to the main actor, which scheduled a timer checkidle(500 ms) later, which took again: a loop of one hop and one timer per hidden busy terminal every ~500 ms, for nothing (its file was already gone).Fix:
TerminalFrameChanges.wake(), which tells the keeper even when it was told and has not read since. So a terminal shown again is kept again, and a terminal that becomes hidden is still removed.TerminalLaunchFrameKeeper.checkscounts checks for the test.TerminalSurfaceView.showsForLaunchFrame(in a window that is ordered in, not hidden, parked or suspended), andupdateOcclusionwakes the keeper whenever that answer changes. Before, the keeper heard only of drawing changes, so where drawing does not change (a live hover mirror forces it; an automation launch draws covered windows) a hide or a window show went unheard.Testing
TerminalLaunchFrameKeeperTests.aHiddenBusyTerminalCostsTheKeeperNoWakes(new): a kept terminal is hidden, then prints 20 lines/s for 3 s; it expects 0 keeper checks, and that showing it again keeps it again.aShownTerminalsFrameIsOnDiskWithNoQuitAndTheNextLaunchShowsIt, failed in 1 of those 5 base runs (its showAhead check); it passed 5/5 at the head.aHideWhileMirroredStillTellsTheKeeper(review follow-up): red 5/5 at 7eeebc0 on its assert (step 6ed9b219; the same unrelatedshowAheadcheck failed in 1 of those runs, as on the base); green 5/5 of all 7 suite tests at the head d1fb287 (step 6a0e58c2).Busy-terminal A/B (lead's gate), one mini
cmux24, 2x virtual display at 120 Hz, bench builds of the same feat base 507438d: base
test/sf5-ab-base-bench(build bcd1b411) vs fix = #32384 (at f2fa334) + #32380 (build 9b6ddda3). Hidden terminals each print 20 lines/s; a quiet workspace is shown; 20 s windows.Observed / Not run
Known, not fixed here
aShownTerminalsFrameIsOnDiskWithNoQuitAndTheNextLaunchShowsItfailed itsshowAheadcheck in 2 of 10 runs at bases without this change (and 0 of 10 with it); it looks like an existing flake of that test, not of this PR.Changelog
Proof
No capture: no visible change.