fuzz: arm the terminal in morph, and hold it to one switch per chord - #760
Merged
Conversation
morph sent its layout chords into an unarmed XAML island. The island drops synthesized keys until a real click lands on its own pixels, and #752 fixed that for the mouse-fuzz harnesses without reaching this one. The cost was invisible because nothing measured it: the harness kept sending, the app kept ignoring, and the oracle only ever read what did happen. Measured against a clean build, it converted 38% to 52% of the chords it sent - 13, 14 and 20 of 34, and 16 of 31. Run outside the suite, which minimizes other windows first, it reliably converted none at all: the startup probe reached a window that had never been clicked and traced no switch whatsoever. So arm before every chord, the same shape as Enable-Chords in mouse-fuzz-tab-close-selection.ps1. Three quarters across and down lands in the terminal under either layout; the window center only does while the strip is narrow, and this harness spends its run changing that. The ad-hoc re-arm click the tab-color branch already carried becomes a call to the same helper. It now converts 1.44 to 1.65 switches per chord - 49 of 34, 39 of 26 and 51 of 31 - so the floor moves from a third to one. That is a statement worth making: every layout chord begins a switch. The surplus above 1 is chords that arrived mid-switch, held as _pendingLayoutTarget and replayed on completion. The tail settle is waited on rather than slept through. Three times as many switches means the last one is far more likely to still be in the air when the finally kills the process, and the verdict reads the trace afterwards, so a fixed 1200ms started reporting a begin with no end as a switch that never finished. It polls for balance instead, which also returns sooner than the sleep did in the ordinary case.
Review of the floor at a ratio of one turned up three ways it blames the wrong thing. The floor divided by every chord attempted, including the ones Chord() refused and never delivered. At a third that slack was harmless; at one it charges a foreground steal to the build and then reports it as "the chords went out but the router never saw them" about chords that were never sent. Divide by the delivered ones instead. That is only a safe denominator because the miss cap above has already rejected a run that lost most of them - on its own, every chord refused would leave zero delivered and a floor of nothing, which is the hole the attempted count was there to close. Arming moved out of the tab-color catch. Enable-Chords throws where the click it replaced was best-effort, so a refused arming click was landing as one more 'tab-color-error' and leaving the island unarmed. The iterations after it would send chords nobody handled - none of them asserted on - and the run would go quietly useless until the next toggle threw. And Enable-Chords now checks the window before it measures it. Rect swallows GetWindowRect's return, so a window that has gone away comes back as zeros, which aimed the click at the top-left of the screen and reported a foreground miss for a window that no longer existed. The caller already treats that as PRODUCT_FAIL; say so here too.
deblasis
force-pushed
the
fuzz/morph-arm-terminal
branch
from
August 26, 2026 12:08
ca12265 to
3a9d481
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follows #758, now merged, and rebased onto it.
#758 made morph report a run that switched nothing. This is why it was switching so little.
morph sent its layout chords into an unarmed XAML island. The island drops synthesized keys until a real click lands on its own pixels, and #752 fixed that for the mouse-fuzz harnesses without reaching this one. The cost was invisible because nothing measured it: the harness kept sending, the app kept ignoring, and the oracle only ever read what did happen.
Measured against a clean build it converted 38% to 52% of the chords it sent - 13, 14 and 20 of 34, and 16 of 31. Run outside the suite, which minimizes other windows first, it reliably converted none at all: the startup probe reached a window that had never been clicked and traced no switch whatsoever.
So arm before every chord, the same shape as
Enable-Chordsinmouse-fuzz-tab-close-selection.ps1. Three quarters across and down lands in the terminal under either layout; the window center only does while the strip is narrow, and this harness spends its run changing that. The ad-hoc re-arm click the tab-color branch already carried becomes a call to the same helper.What the floor can now say
1.44 to 1.65 switches per chord, so
$MinBeginRatiomoves from a third to one: every layout chord begins a switch. The surplus above 1 is chords that arrived mid-switch, held as_pendingLayoutTargetinMainWindow.AnimateTabLayoutToand replayed on completion.The margin is the surplus and it is not guaranteed - two chords inside one switch share the single pending slot, so the second is lost - which is why the floor is exactly 1 rather than something above it.
The tail settle had to change with it
Three times as many switches means the last one is far more likely to still be in the air when the
finallykills the process, and the verdict reads the trace afterwards. A fixed 1200ms started reporting a begin with no end asswitch begin/end mismatch ... (a switch never finished)- a false exit 2. It polls for balance now, which also returns sooner than the sleep did in the ordinary case.Validation
just fuzz "-Only morph"just fuzz-selftestSELFTEST OK, exit 0just fuzz-listIt also passes on the first attempt now. Every earlier suite run needed the retry, because the first attempt failed the startup probe.
Noticed, not addressed
The trace shows consecutive
SWITCH begin vertical=Truepairs with no end between them, so the app can animate a switch to the layout it is already showing.AnimateTabLayoutToguards on_verticalTabsVisible == verticalat theOnConfigChangedcall site but not on the_pendingLayoutTargetreplay path. It is benign -ghosts=0throughout and every begin is answered - but it is a 340ms animation and a morph cycle for a no-op. Worth its own look, not this PR.Review round
Reviewed at high effort before merge; three findings, all fixed in
fuzz: measure the morph floor against the chords that landed.Chord()refused and never delivered. Harmless at the previous ratio of a third; at one it charges a foreground steal to the build and reports it as "the router never saw them" about chords that were never sent. It divides by the delivered ones now, which is only safe because the miss cap above has already rejected a run that lost most of them.Enable-Chordsthrows where the click it replaced was best-effort, and it sat inside the tab-colortry/catch- so a refused arming click landed as one moretab-color-errorand left the island unarmed, with the iterations after it sending chords nobody handled. Hoisted out of the catch.RectswallowsGetWindowRect's return, so a window that had gone away came back as zeros and aimed the arming click at the top-left of the screen, reporting a foreground miss for a window that no longer existed. Checked before it is measured, and reported asPRODUCT_FAILto match the caller's own convention.