bench(terminal-resize): probe the drag, accept held terminals, A/B key resizes in one launch

Merged
LA
1d ago
+559-67

Summary

The terminal-resize bench (split-spring.sh --suite=terminal-resize) measured nothing in the FRAME OWNER S4 A/B. Both arms failed "no terminal resized in the drag" in every light and STRESS round (cmux24: OFF job b945a41db0fdb7242ba64532, ON job a88aa2f9047582ad48f9d6de). There were two causes:

  1. terminalDrag.holdSurface (on by default since #32006) keeps each terminal's size and text through a divider drag. The bench drag ends where it began, so no terminal resizes in either arm. The run could not tell this case from a drag that missed the divider.
  2. The bench counted only Ghostty's full_resize. A held grid's new size draws through the reflow preview, which Ghostty counts as full_swap (job b5834b1d8ebc36c55386d7cd: full_resize 0, full_swap equal to the grids sent).

Changes:

  • Probe. Each drag layout first runs one drag that ends 40 pt (--probe-pt) left of the divider, then one drag back. The probe checks that the divider moved, that a terminal fits a new grid, and that the render counters counted it. It prints its own TERMINAL-RESIZE-PROBE line. If it fails, the layout fails before any timed round.
  • Resized means that full_resize + full_swap grew (terminal_resize_verdict.resized).
  • Held drag. Each drag round records holds_terminals (debug.tunables get terminalDrag.holdSurface) as it ran. A held round needs a counted frame and a view layout count above 0, not resized terminals.
  • Key-resize layouts. keys has 4 terminals in a 2x2 grid; keys-stress uses the STRESS session. Both press resizePaneRight then resizePaneLeft (Ctrl-Shift-L, Ctrl-Shift-H); use --key-repeat and --key-gap-ms for a held key. Per round: frame intervals and main ms per frame (all frames, and the frames that laid out a view), missed refreshes, and per press the most grids one terminal got (debug.terminal_final_grid; a round fails when the trace does not start).
  • One-launch A/B. --ab KEY runs each drag and key round with the boolean tunable off and on in one launch (debug.tunables set). The arm order alternates per round. terminal_resize_arms pools each arm into p50/p95/p99/max. The bench resets KEY after each A/B layout. Each round keeps its raw per-frame arrays in the report.
  • bench_ab.py puts an --ab round in its arm's series (<layout>/off, <layout>/on).
  • One STRESS session per launch, shared by stress, keys-stress and split-resize-stress (a second build refuses: STRESS has 60 workspaces in all).

The default layouts and the timed drag rounds do not change. The probe runs before the timed rounds. split-spring.sh does not change, so its measure version stays the same.

Testing

Red, then green (each red commit fails for its reason, the next commit makes it pass):

  • 382dbb9c5b7 (red: 4 failures, 3 errors) → b89e408b703: the held drag, the key layouts, the arms.
  • bc0d8785dd7 (red: 2 errors) → 83b0c67714d: a swap counts as a resize.
  • e2acda51d45 (red: 2 failures in the resize tests, 1 error in bench-ab) → 436bab30919: the review's findings 2, 5 and 7.

At the head 26d8678795e:

  • python3 scripts/cmux-next/tests/test_bench_terminal_resize.py: 25 tests pass.
  • python3 -I scripts/measure/tests/bench-ab.test.py: 33 tests pass.

Reviews: the independent review at b89e408b703 was BLOCK (comment 6632166848, 7 findings). The re-review at 436bab30919 was SHIP (comment 6633157756): findings 1, 2, 3 and 7 fixed; 4 and 5 partly fixed (low); 6 written down. 26d8678795e changes only text: the lines the review named and two more lines of the module docstring. The short re-review at 26d8678795e was SHIP (comment 6633574554).

Observed / Not run

  • Observed:
    • the unit tests above, and prepush-gate.py passes;
    • a dry run against a fake app socket that holds terminals through a drag (--layouts=keys,light --ab=motion.frameOwner.terminals): the probe line printed first, the held light drag passed, the keys arms alternated, and the key was reset after each layout; a drag that missed the divider failed at the probe and exited 1;
    • a real run of the earlier bench head 8c981e32979 on cmux24 (job b5834b1d8ebc36c55386d7cd): the keys layouts measured (one grid per press in both arms). Its drag layouts failed at the probe because of cause 2, which 83b0c67714d fixes.
  • Not run yet: a real run at this head. pixel-perfect's S4 A/B runs it from fix/frame-owner-s4-abbench-nomerge (the S4 ON code plus these commits up to 83b0c67714d).
  • Low items left open (reviews):
    • with --key-repeat above 1, grids are counted per press window, not per press;
    • a timed STRESS drag that missed the divider while other views lay out could pass (the probe just before it hits the same divider);
    • on a DEBUG build, the reset also removes a value the developer set before the run;
    • with --ab terminalDrag.holdSurface, the layout field drag_holds_terminals shows the last arm (the verdict reads each round's holds_terminals);
    • the tunable store stays active after an A/B layout (later layouts run with default values);
    • no unit test runs the round loops of measure() and measure_keys() (the fake-app dry runs above are their evidence).

Changelog

none