Repository navigation
Frame owner S4: terminals follow their pane in every frame of a key resize (switch-on path; default stays off) - #35470
Conversation
…n.frameOwner.terminals on than off (red) Films ab-s4d-sw-22 (build 645ad29d, px-row-drag-3r, judge v7): in a divider drag, the terminal's content edge lagged its pane edge mostly 2 refreshes with the switch on (p2's moving edge -4 px, 156 of 667 frames), against 1 refresh with it off (-2 px, 117 of 676). TerminalFrameOwnerDragCadenceTests drags a real Ghostty surface one size per refresh of its window's display link (a FrameClient on the window's FrameScheduler), first with the switch off, then on. At each refresh it reads how many refreshes old the size of the shown frame is, and with the switch on counts Ghostty's state for the newest size (ready: drawn, not presented yet; pending: not drawn yet). On may not be later than off (median and p95). Display workers only. Red command: cmux-ci run --display --label gui-session-v1 --class light --script scripts/ci/package-test-lane.sh --arg=suite --arg=Packages/macOS/CmuxNext --arg=TerminalFrameOwnerDragCadenceTests
…trailing, bottom) while its new size waits (red) S4 ON PR defect (b), found by the Codex review at 15c8848. In a key resize the host follows its pane (FRAME OWNER S3, #34170) and gives the surface its size on the hold's still edges (PaneMotionHold.producerFrame). With motion.frameOwner.terminals on, while a new size waits for its frame, place() kept the old frame on the wanted frame's leading edge, and on its top edge unless the surface hold said bottom. So with a trailing hold the kept frame's trailing edge moved off the still edge (by the size change), and with a bottom hold its bottom edge did, until the new size showed. aKeptFrameStaysOnTheEdgesItsHoldNames: a trailing hold with a narrower final size, then a bottom hold with a shorter one. While each size waits, the kept frame keeps the rest size on the still edges; the landed frame is the producer frame. Red command: cmux-ci run --class light --script scripts/ci/package-test-lane.sh --arg=suite --arg=Packages/macOS/CmuxNext --arg=TerminalSurfaceCommitSizeTests
…s host follows (trailing, bottom) Fixes aKeptFrameStaysOnTheEdgesItsHoldNames (previous commit). With motion.frameOwner.terminals on, while a new size waits for its frame, the kept frame now sits where FRAME OWNER S3's PaneMotionHold.producerFrame puts the surface: on the trailing edge when the followed hold keeps its trailing edge still, and on the bottom edge when the hold or the surface hold says bottom. Before, it rode the wanted frame's leading edge, and its top edge unless the surface hold said bottom. - TerminalSurfaceView.place(_:anchor:trailing:) and anchoredFrame(..., trailing:) carry the horizontal edge (TerminalFrameOwnership.trailing). - TerminalHostView: surfaceAnchor is bottom when the surface hold or the followed hold says bottom; surfaceTrailing is the followed hold's trailing. With the switch off nothing changes: place() sets the frame it gets, as before; only the kept frame (switch on) uses the edges.
…the bare-window version did not reproduce) f355d15 resized a bare terminal from a display-link tick: step 569b148813d9d863e5bb673b passed with the switch off and on (the shown frame 0 refreshes old in all 52 refreshes), so it did not reproduce the films' row drag lag (ab-s4d-sw-22: content edge 2 refreshes behind with the switch on, 1 off). In a drag the surface keeps its start size; only its origin moves. aRowDragMovesTheTerminalWithItsPaneEdgeNoLaterWithTheSwitchOnThanOff now uses the real path: p1 above p2 in a LayoutRootView in a window on screen, a terminal in each, and the app's divider drag (ScreenContentView.handleDrag), one 2 pt move per refresh of the window's display link, switch off then on. After each Core Animation commit it reads, in the committed layer tree, p2's pane host top edge and its surface layer's top edge. On may not lag in more refreshes, or by more, than off. Display workers only.
…t path per frame (for the ON row-drag film) FRAME OWNER S4 defect (a): with motion.frameOwner.terminals on, the films' row drag shows the terminal's content edge 2 refreshes behind its pane edge (1 with it off). The committed layer geometry has no lag in either mode (the real-layout test f863559), and the film's trace shows the panes moving the same way, so the extra refresh is in the present path. Debug and bench builds only (DEBUG || CMUX_BENCH_HOOKS): a debug.motion trace sampler `terminal`, per display frame and shown surface: its layer rect (window points, top-left), the shown IOSurface size, Ghostty's render counters (frames, off-main presents, fresh presents, display passes, main draws, late presents), whether its window's commit owns its frame, the presenter's state for it (TerminalFramePresenter.debugState: request, watch, commit or -) and ghostty_surface_host_frame without presenting. It reads only while the trace records. Registered once by the first surface view.
…ne's size in every frame, and its program resizes once per press (red) Lawrence on dogfood 363e39f2: "tabbar moves smoothly on ctrl shift hjkl but browsers/terminals do not anymore". The key resize's hold (RESIZE KEYS K3, TerminalSurfaceView.restAt) holds each surface at the press's final size for the whole spring; with FRAME OWNER S1/S3 the tab strip and the host follow the pane every frame, so the terminal's pixels freeze, then jump. With motion.frameOwner.terminals on (S4), the size still changes once per press. Contract (lead, cmux-ad m6): pixels per frame, program size per press. TerminalKeyResizeFrameTests, on the real path (LayoutRootView p1 | p2, a spring with Reduce Motion off and S3's content policy on, PaneResize.press), switch on: in every committed frame of the spring each terminal's surface layer is within 1 device px of its pane area's size in that frame or the frame before; each PTY gets at most one grid for the press; ready-or-keep holds. It prints the frame intervals (p50, p95, max). Display workers only. Red command: cmux-ci run --display --label gui-session-v1 --class light --script scripts/ci/package-test-lane.sh --arg=suite --arg=Packages/macOS/CmuxNext --arg=TerminalKeyResizeFrameTests
… wall-clock time (idle run-loop turns ended it before the spring moved) Step 2780d048a25b653555a50640 at 418d4a8 failed its precondition, not the contract: "moving 1 >= 4". It stopped after 12 commits with no change, and a run-loop turn is not a refresh: 12 idle turns passed before the spring's first frame. The test now keeps every committed state that differs from the last one for 1.5 s of wall-clock time; settle waits for 300 ms with no change. Still red-first: no fix in this commit.
… itself (the display-link spring gave 2 states) Step dd347b7008f47da4762b1d62 at 73c411a failed the precondition again: "moving 2 >= 4". In 1.5 s the committed state changed twice, so the display-link spring did not animate on that host. The test now drives the layout as TabStripFollowsPaneTickTests does: driver.isManual, then per frame advanceFrame(1/120), its layout pass and its Core Animation commit (both presses). It also times each frame's main work (step + layout). Still red-first: no fix in this commit.
…inal's top-left on its pane's (the film gate), and sizes the pane has had pixel-perfect's film on dogfood 437f5a22 (px-keys-single-3k-s1r): the terminal right of the divider has its left ring on its pane's left edge in 14 of 50 motion frames, off by -8 to +21 px in 20, cut off in 16. A size check alone misses a wrongly placed frame. Per committed frame and terminal the test now requires: the surface's top-left on its area's top-left (1 device px, in the window's root layer); a size the pane had in this press, this frame or earlier; the final size after the landing; at most one PTY grid per press. Still red-first: no fix in this commit.
…p-left anchor (scenario kept from S4 defect (b)) The case that once showed a kept frame on the wrong edge (red step d509169a15477622073341e5) stays, with the lead's m6 rule as its expectation: in a key resize whose S3 hold keeps the pane's trailing or bottom edge still, when the pane narrows or gets shorter the terminal asks its new size, keeps its last frame anchored on its area's top-left (never another edge), and lands at the area. The size now comes from the pane's area (ResizingContainer.terminalHeight added), as in the app. Red on the current code: the followed surface keeps its start size at the still edge (FRAME OWNER S3 + K3), so no new size waits: 'the narrower size did not wait for its frame'. The fix is the next commit.
…aneContentView, so the key hold follows the pane as in the app (red) Step f3f4a7f9378f4d1455c4e12b (6cf19a0, cmux11) ran the stepped spring (27 frames, main per frame p50 0.08 ms, max 0.22 ms; one PTY grid per press) and failed 48 frames: "layer 500x600, pane area 450x600". But its panes were plain NSViews, so the key hold held the whole content (RESIZE KEYS K3 without FRAME OWNER S1/S3): that is a red for the K3 hold, not the app's path. The panes are now the app's PaneContentView (its tab strip over its content area): the key hold follows the pane in every frame (S1/S3), as in the app, and only the producer holds. Per committed frame and terminal: top-left within 1 device px of its area's, a size its pane has had in this press, never stretched (layer px == the shown IOSurface's px); after the landing the final size; at most one PTY grid per press. Display workers only. Still red-first: no fix in this commit.
…resize shows each terminal at its pane's size in every frame; the PTY gets one grid per press Lawrence on dogfood 363e39f2: "tabbar moves smoothly on ctrl shift hjkl but browsers/terminals do not anymore". In a key resize the host follows its pane (FRAME OWNER S3), but the surface kept one size per press: restAt held it at the final size (or S3 at the start size) on the hold's still edges, so the content froze, then jumped (film on 437f5a22: the terminal right of the divider had its left ring on its pane edge in 14 of 50 motion frames). Contract (lead, cmux-ad m6): pixels per frame, program size per press. With the switch on: - TerminalHostView.followedSurfaceFrame gives the surface the area it shows in, in every frame. The frame owner (S4) shows each size's frame with its bounds, or keeps the last frame anchored top-left until it is drawn (one size in flight, the newest next): never stretched, never off the pane's top-left. No main-thread draw or wait: set_size goes once per landing, and the drawing is on Ghostty's renderer thread. - TerminalSurfaceView.restAt still reports the final grid at the press (the PTY's one resize), but no longer holds the surface at the final size. - The kept frame's edge is the top-left again: S4 defect (b)'s code (the kept frame on the S3 hold's trailing or bottom edge, b0a6f60) is reverted. Its test keeps its scenario with the top-left expectation (previous commit). Switch off: unchanged (S3's producer hold on the still edges). Green command: cmux-ci run --display --label gui-session-v1 --class light --script scripts/ci/package-test-lane.sh --arg=suite --arg=Packages/macOS/CmuxNext --arg=TerminalKeyResizeFrameTests and the terminal suites (TerminalSurfaceCommitSizeTests, KeyboardResizeHoldTakeoverTests, KeyboardResizeContentHoldTests, DividerDragTerminalSteadyTests, ...).
…me-owner-s4-on-merge
…the drag's +1 refresh) The drag cadence suite (f863559) drove a real-layout row drag to find the +1 refresh that films showed in drags. It never reproduced it, and pixel-perfect's alternating rounds (STATUS 11574) showed the +1 refresh comes and goes between launches in both modes: it is not caused by motion.frameOwner.terminals. The suite holds no regression, so it goes before the ON PR.
… ON step) Terminals are ready-or-keep by default: in every Core Animation commit a terminal's bounds and its pixels change together, or both keep their last size, and in a key resize each terminal shows its pane's size in every frame while its PTY gets one grid per press (cmux-ad m6). Release and RC read the default, so this is the step that ships it. The switch stays for the A/B and goes in the deletion PR. What's New: release-notes/next/highlights/resize-keys-terminals-follow.md.
…nHold lost containersFollow in #35161) #35161 deleted motion.frameOwner.contentPolicy and PaneMotionHold's containersFollow argument (a content area's hold always lets its containers follow now). The merge e37c317 had no text conflict, but aKeptFrameStaysOnTheEdgesItsHoldNames still passed the argument, so CmuxNextAppTests did not build (fleet step 97850bf9a5ad4c353d88329d at 8c981e3: "extra argument 'containersFollow' in call", lines 99 and 118). The hold means the same without it.
…esize tests check the m6 contract This takes back b3ab63b (default on and its What's New highlight) on this branch only, for one commit. With the default on, 7 tests fail at c642350 (fleet step e9e6a30c3d9effb058d77e64): they check FRAME OWNER S3's held surface, one size per press, which the lead's m6 contract replaces (pixels per frame, program size per press). The next commit changes them to that contract and is red with the switch off. The commit after it turns the switch on again.
…s per frame and one grid per press (red with the switch off) The lead's m6 contract replaces FRAME OWNER S3's held surface: in every frame of a key resize each terminal shows its pane's size (or keeps its last frame, top-left, until the new size is drawn: TerminalKeyResizeFrameTests), and its PTY gets at most one grid per press. These tests checked the opposite, one surface size per press: - PaneContentPolicyTests: aKeyPressShowsEachSurfaceAtItsPanesSizeAndSendsOneGrid (was ...SizesEachSurfaceOnce...), aHeldKeyShowsEachSurfaceAtItsPanesSizeAnd SendsAtMostOneGridPerPress (was ...SizesEachSurfaceOnlyInAPressTurn), and aPressAndItsReverseInOneHoldSendEachGridInItsTurn (the surface follows in every frame and rests at its start size). - KeyboardResizeContentHoldTests.theTerminalFollowsItsPaneInEveryFrameAndSends OneGridPerTerminal (was theProducerResizesOnce...). - KeyboardResizeReflowAfterLandingTests.theTerminalFollowsItsPaneInEveryFrame AndItsGridGoesAtThePress (was theProducerLaysOutOnce...). These windows draw nothing, so each size shows at once. The grid checks do not change. TerminalLaunchFrameKeeperTests: the two resize cases wait (2 s at most) for the surface to show its new size. With the frame owner it takes that size in the commit that shows its frame, a refresh or two after the layout, and the keeper saves it then. These cases pass with the switch off and on. Red at this commit, with motion.frameOwner.terminals off (the previous commit). The next commit turns it on.
… ON step) Terminals are ready-or-keep by default: in every Core Animation commit, a terminal's bounds and its pixels change together, or both keep their last size. In a key resize, each terminal shows its pane's size in every frame, and its PTY gets one grid per press (cmux-ad m6). Release and RC read the default, so this step ships S4. The switch stays for the A/B, and the deletion PR removes it. This is b3ab63b again (taken back for one commit by f231b3c). The contract tests of 2da280e go green here. What's New: release-notes/next/highlights/resize-keys-terminals-follow.md.
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
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)
Passes: CI passes on CI passes on Written by web and size checks: green Default changesThis pull request changes 1 default at
Signed off on this commit by Merge receipt: merged as Regenerate the snapshot with Shipped in:
|
…3e39f2: terminals stopped following their pane)
This comment has been minimized.
This comment has been minimized.
|
Review (independent subagent): BLOCK at 665b187 Scope: the full diff from the merge base CorrectnessF1 (P2, blocks): after the landing, a stale size in flight can send extra PTY grids for one pressTwo of the three reads found this on their own. Where: Sequence, with the switch on:
Result: the view reports three grids for one press (final, stale, final). With the daemon, When: the stale size must fit another grid than the final size. That is likely when the request is some refreshes old (a slow terminal, frames held by mode 2026, which agent TUIs use), or when the final size is near a cell edge. One slow terminal is enough for all terminals of its group: a group whose member is not ready keeps every member ( Why the tests do not see it: the policy tests use windows that are never on screen, so Ghostty answers NONE and no request stays in flight. Fix: never report a grid from a size in flight that is not the wanted size. For example, in Regression test: in F2 (P2, needs Lawrence's call): a pane that grows down hides a prompt on its last row for most of the springAll three reads found this. Where: With the switch off, S3 kept the surface at its final size on the moving bottom edge when the prompt was in the lower half, so the prompt stayed in view (the reason is in the comment at The PR's Frames lines say "in every frame the terminal shows its text at its pane's size" and "No content stops following its pane". They do not name this case. (Columns cropped or padded on the right while the grid differs from the view are part of "program size per press". The bottom prompt is different: S3 kept it in view on purpose, and this PR removes that.) Fix: keep the bottom anchor for a growing pane whose F3 (P3): a terminal tab shown during a key spring takes the old S3 holdWhere: a terminal that was hidden when the spring began and is shown during it (a tab switch). Result: the surface takes the final size at once (Ghostty still answers HIDDEN, because Fix: in Tests
Evidence for the default flip (E1: holds the flip)
Performance (from the code; I measured nothing)For each moving terminal in each frame, with the switch on (with it off, only the origin changes):
Possible fixes: do not send Checked, no defect found
Tally: 13 candidates; kept: 3 correctness findings (F1 to F3), 4 test findings (T1 to T4) and 1 evidence hold (E1); refuted: 1 (a blocking termio mailbox push in the per-frame path: the daemon surface resizes inline with Review (independent subagent): BLOCK at 665b187 |
…e landing, a terminal must take new sizes, a low prompt rides the bottom (red) The independent review of #35470 at 665b187 (BLOCK, comment 6635411077): - F1: aTerminalWhoseFrameWaitsAtTheLandingGetsOneGridForThePress (display workers). p1 holds its frames with mode 2026, with a locked grid as the daemon locks it. At the landing a size is still in flight, and the landing's release reported that stale size's grid, then the final grid again: one press sent 3 grids. The test requires that a size waited and that one was in flight at the landing, so it cannot pass vacuously. - T1: aKeyResizeShowsEachTerminal... also requires each terminal to take a new size in more than 3 spring frames. The size check allowed the start size, so a terminal that never moved passed. - F2: aPromptOnTheLastRowRidesTheBottomOfAPaneAKeyGrowsDown. A pane that Ctrl-Shift-J grows down keeps a prompt on its last row in view (S3's rule, which a split and a close still keep): the surface keeps the final size on the moving bottom edge. With the switch on, the surface took the pane's size in each frame and showed the final grid's top rows, so the prompt was out of view for the whole spring. Expected red at this commit: F1 (3 grids for p1) and F2 (the surface never taller than its pane). T1 is green here: it guards the m6 fix.
…o grid; a low prompt rides the bottom of a pane a key grows down Fixes for the independent review of #35470 at 665b187 (BLOCK, comment 6635411077): - F1: updateSurfaceSize holds the report while the size in flight is not the wanted size (frameOwnership.inFlight != wanted). The landing's release sized the surface from a stale spring size whose frame was still waiting, and it reported that grid, because the spring was over. Then the newest size reported the final grid again: final, stale, final. Now the wanted size reports its grid when it goes to Ghostty (requestWantedFrame), and it matches the final grid the press sent, so the press sends one grid. A forced report waits the same way and goes with the wanted size. - F2: restAt keeps S3's bottom hold in a key resize when motionAnchor answers .bottom (a pane that grows down, with the prompt in its lower half). followedSurfaceFrame keeps that hold with the switch on. Before, the surface took the pane's size in each frame and showed the final grid's top rows, so the prompt was out of view for the whole spring. The PTY still gets one grid, and every other key resize follows its pane in every frame. Red tests: 712df02.
…rface hold (review of #35470 at 447f784, G1) With the switch on, restAt returned early for a followed host with a .top anchor, so an older hold stayed until the landing: - a low prompt's bottom hold (F2) after a reverse press: p1 kept the earlier final size on the bottom edge while it shrank; - the hold a terminal took when it joined mid-hold, before its area handed it the follow: after a second press the landing's release sized from it, so the PTY got grid1, grid2, grid1, grid2 for two presses. restAt now releases the hold (TerminalHostView.releaseSurfaceHold) before it returns .top. With the switch on, followedSurfaceFrame ignores a .top hold, so no frame moves; the release only drops the stale state. Red tests: 7abac06. Also the two comments that described only S3's producer hold (PaneMotionHold.swift, PaneContentAreaView.swift) now say what the switch does.
… (the new-tab version passed without the fix) aTerminalThatJoinsAKeyHoldGetsOneGridPerPressThroughASecondPress passed at its red commit 7abac06 (fleet step 1b0702f8dbf54ae7091c8dad). A tab added new mid-hold hears the follow in PaneContentAreaView.didAddSubview before its surface joins the spring, so it never takes a hold. The path in the review's G1 (b) is a parked tab shown again: it is unhidden before the area hands it the follow in its next layout pass, so its surface joins with the OFF path's hold at the final size. The test now parks the second terminal at rest, shows it again mid-spring, presses a second time, and requires one grid per press and none after the landing.
|
Review (independent subagent): SHIP at c4d6c93 Scope: the full diff from the merge base Result: no P0, P1 or P2 defect in the code. With the switch off (the default, and the only state that Release can have), every changed line is inert. With the switch on, I found only P3 items. Each needs a rare press sequence or lasts one or two refreshes, and none is worse than the switch-off path. One P2 test gap (T5) should be fixed before Correctness (switch on only; none blocks)H1 (P3, DERIVED): the low-prompt bottom hold keeps the final width and the hold's first edgesWhere: Two sequences, each with a shell prompt in the lower half:
Why it matters: both break the m6 contract (top-left, the pane's size) in the F2 path. Both need two presses in one spring. Neither is worse than the switch-off path, which uses the same S3 branch (and in (a) cuts the lines from the H press). Fix (one change for both): with the switch on, the bottom hold keeps only the height, and only while it covers the area. In Regression tests (red at this head; H2 (P3, DERIVED): after the G1 release, the older hold's request in flight still landsWhere: Sequence: J with a low prompt asks Ghostty for the final size. K comes before that frame shows (a slow renderer, or a program that holds its frames). The release in Fix: in the release path, when a request is in flight for a size that is not the wanted size, cancel it and call H3 (P3, DERIVED; decide before the flip): no reflow preview in a key spring, so a low prompt in a pane that shrinks is cut until the announcementWhere: With the switch on, the surface has the area's size in each frame, so Why it matters: the prompt can blink at the start of each such press, and the Frames line does not say it. Fix: name it in the Frames line now, and decide in the flip PR. The choices: accept it and write it in the contract; or keep the surface at the final size while that size is smaller than the area and the cursor is low (the S3 rule for a pane that shrinks); or a Ghostty preview at a given grid (a fork change). H4 (P3, the same on the base's switch-on path): a bottom hold whose frame waits shows a band at the topWhere: Until the final frame shows, the kept start-size frame rides the moving bottom edge, so a band of terminal ground shows at the top of the pane for one or two refreshes. If the program holds its frames for more than H5 (P3, contrived): a grid that the F1 rule holds can wait with no follow-upWhere: At rest, a size is in flight and another size is wanted. A report then comes (a font size change), and the F1 rule holds it. If the wanted size goes back to the size in flight before it lands, G2 (from the review at
|
…ecks, with the switch off The independent review of #35470 at c4d6c93 (T5, P2) found that the default path (motion.frameOwner.terminals off) lost its checks when the m6 contract tests turned the switch on. The base's five checks come back as their own tests, with the switch pinned off after each await: - PaneContentPolicyTests: aKeyPressSizesEachSurfaceOnceWithTheSwitchOff (4 directions), aHeldKeySizesEachSurfaceOnlyInAPressTurnWithTheSwitchOff, aPressAndItsReverseRestsAtTheStartSizeWithTheSwitchOff; - KeyboardResizeContentHoldTests: theProducerResizesOnceWithTheSwitchOff; - KeyboardResizeReflowAfterLandingTests: theProducerLaysOutOnceWithTheSwitchOff (left, right). They are the base's tests at bb23623 with new names; they guard behavior that does not change, so they pass on the base and at the head. PaneGroundCoversPaneTests.ownFrames takes the value to pin.
…s in a key resize The independent review of #35470 at c4d6c93 (P3 notes) found comments that state the switch-on behavior as a fact: - the help of motion.frameOwner.terminals did not say that it changes the key resize; - TabStripFollowsPaneTickTests.Content ("A terminal no longer holds"); - the PaneContentPolicyTests suite header; - TerminalHostView.releaseSurfaceHold ("The spring landed"), which restAt also calls at a press with the switch on; - TerminalLaunchFrameKeeperTests' resize waits (they pass at the first check with the switch off, the default). Comments and the debug help text only; no behavior changes.
…off and on The lead (on the review of #35470 at c4d6c93, T5): run the tests with the switch off and with it on, so both paths are covered. The default (off) is what ships. Each test this PR changed that has a meaning in both states now takes the switch as an argument, pinned after each await: - PaneContentPolicyTests: the F3 guard (off: at most one new size from the frame it shows, then the area's size at the landing), F2 (off: S3's bottom hold keeps the same rule), G1 (a) (off: from the reverse press each surface keeps one size, p1 its start size), G1 (b) (one grid per press in both); - TabStripFollowsPaneTickTests: the strip-and-terminal test (off: the surface takes at most one new size in the press; strips, hosts and grids as with it on); - KeyResizeFirstTickPinTests (both tests), PaneGroundCoversPaneTests (the key-press and held-key ground tests), TerminalLaunchFrameKeeperTests (the two resize saves). The m6 contract tests stay switch-on, and the base's one-size checks are their switch-off counterparts (c0c7f9b). TerminalKeyResizeFrameTests and TerminalSurfaceCommitSizeTests test the frame owner itself, which runs only with the switch on.
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
Review (independent subagent): SHIP at 6522020 Scope: the full diff from the merge base Result: no correctness defect. T5 is fixed, and the order of the lead (run the tests with the switch off and with it on) is met. The P3 items H1 to H5 from my last review are not changed. They are only on the switch-on path, and the body records them for the flip PR. (c) The source since
|
|
Swift logic follow-up (tip watcher swift-logic row, STATUS 13027 "SWIFT LOGIC CULPRITS"; lead order). This PR raised
plans/cmux-next/layer-ownership.md says Rust owns every state, list and rule and Swift is the platform shell. Please move the new logic of this PR in a follow-up: to Rust (the crate that owns the area; the owner lists in STATUS 12147-12165 name the target per area), or at least out of the app into its package. If a declaration above is platform code (AppKit, Core Animation, OS calls, strings, tokens, DEBUG tooling), reply here and the table gets a PLATFORM row with your reason instead. The swift-logic row is a report, not a gate: nothing blocks on this comment. |
…'s width and never leaves a band (#35914) * test(next): S4 H1: a low prompt's bottom hold keeps its pane's width and never leaves a band above the terminal (red) Review of #35470 at c4d6c93, H1 (switch on), two sequences: - a 2x2 grid, Ctrl-Shift-H on p4 then Ctrl-Shift-K before the landing (p4 grows up with its prompt in the lower half): the bottom hold kept the final width on the hold's trailing edge, so the moving left edge cut off every line; - Ctrl-Shift-J on p1 twice, then Ctrl-Shift-K: the K press kept the bottom hold at its final size, shorter than p1's area mid-spring: a band of ground above the terminal until the landing. * fix(next): S4 H1: with the switch on, a low prompt's bottom hold keeps only its height, on its pane's width Review of #35470 at c4d6c93, H1 (switch on only): the bottom hold that keeps a low prompt in view (F2) placed the press's final size on the hold's first edges. After a press on the other axis (H, then K on a bottom-right pane) the final width sat on the trailing edge and the moving left edge cut off every line; after two grow presses and a shrink (J, J, K) the K press's final size was shorter than the area mid-spring, so a band of ground showed above the terminal. followedSurfaceFrame now keeps, with the switch on, only the hold's height, and only while it is taller than the area: the bottom on the area's bottom edge, the area's leading edge and width. A single J or K press does not change. The switch-off path is unchanged. * test(next): S4 H1 review: a kept frame of a low-prompt hold not taller than its area stays top-left (red); the band test proves its bottom hold happened Review of #35914 at 03c050e (BLOCK, item 1): with the switch on, followedSurfaceFrame gives the area when the bottom hold is not taller than it, but the surface's anchor stays .bottom, so a frame kept while a new size waits rides the bottom edge: its top rows are cut and the text moves with the bottom edge. TerminalSurfaceCommitSizeTests draws, so it sees the kept frame. Item 2: the band test now expects the bottom hold to happen. * fix(next): S4 H1 review: the surface's anchor comes from the same decision as its frame Review of #35914 at 03c050e (BLOCK): with motion.frameOwner.terminals on, a low prompt's bottom hold that is not taller than its area gave the surface the area's frame, but the frame owner still got the hold's .bottom anchor, so a frame kept while a new size waits (1 to 4 refreshes in a window that draws) rode the bottom edge: its top rows were cut and the text moved up by the height the area lost, or a band of ground showed above it while the area still grew. followedSurfaceFrame now returns the frame and its anchor (.bottom only while the hold is taller than the area), and install, holdSurface, releaseSurfaceHold and layout() place the surface with that one result. Switch off: the same frame as before; place does not read the anchor. The restAt doc says what the switch-on host keeps.
…erminal's reflow preview at its pane's grid; no low-prompt bottom hold cmux-ad m14 (switch motion.frameOwner.terminals on): in every frame each terminal shows pixels drawn at its pane's size, with its prompt in view. F2: restAt held a low prompt's surface at the press's final size on its pane's bottom edge (#35470, #35914): taller than its pane, its top rows cut for the whole spring. H3: TerminalGridPolicy.previewsViewGrid turned the reflow preview off while a reported final grid did not fit the view (the old m6 rule), so a shrinking pane drew the owner's grid from its top-left, its prompt cut until the owner announced the new grid. One decision now drives the frame, the hold and the preview: TerminalHostView.surfaceFollowsArea (the host follows its pane through a key hold with the switch on). - followedSurfaceFrame gives the surface its area's frame, whatever hold it has; the low-prompt branch of #35914 had no key-spring use left and is gone. - restAt holds nothing for a following host and lets go of an earlier hold (a join's), whatever the anchor (G1 released only for .top). The final grid still goes once per press (reportFinalGrid). - updateReflowPreview passes it to previewsViewGrid(followingPane:), which keeps the preview on: Ghostty's ReflowPreview reflows a copy of the terminal to the view's grid with Terminal.resize, which keeps the cursor row in view. Switch off, and splits, closes, zooms and divider releases (no following host): unchanged. TerminalGridPolicyHoldTests covers the new parameter.
…ane's size with its prompt in view (m14, no low-prompt hold) (#36199) * test(next): S4 m14 (c1): a low-prompt terminal draws the grid its pane fits in every frame of a key spring (red) cmux-ad m14 with motion.frameOwner.terminals on: in every frame each terminal shows pixels drawn at its pane's size, with the prompt in view. Two failures remain for a terminal whose prompt is low (pixel-perfect films jjk-3kz): - F2: a pane a press grows keeps its surface at the final size on its bottom edge (the low prompt's bottom hold), taller than its pane: its top rows are cut; - H3: a pane that shrinks draws the owner's grid from its top-left (the reflow preview is off while the final grid does not fit), so its prompt is cut until the owner announces the new grid. TerminalKeyResizeFrameTests (display): p1 over p2 with low prompts and an IO that announces each grid it is asked for, as the daemon does (MANUAL_MIRROR). J, K, and J J K on p1. After each frame's sizes land: each surface has its area's frame, and the grid Ghostty drew is the grid the shown frame fits. Switch-on expectations of F2's hold change to m14's (the surface has its area's frame and holds nothing): - PaneContentPolicyTests: aPromptOnTheLastRowRidesTheBottomOfAPaneAKeyGrowsDown (now ...StaysInViewInAPaneAKeyGrowsDown; the switch-off arm is unchanged), aLowPromptBottomHoldAfterAPressOnTheOtherAxisKeepsItsPanesWidth (now aLowPromptPressOnTheOtherAxisKeepsTheSurfaceOnItsArea), aLowPromptBottomHoldNeverLeavesABandAboveTheTerminal (now aLowPromptGrowDownTwiceThenUpKeepsTheSurfaceOnItsArea), and the switch-on arm of aReversePressAfterALowPromptGrowsDownFollowsItsPane; - TerminalSurfaceCommitSizeTests: aKeptFrameOfALowPromptHoldNotTallerThanItsAreaStaysTopLeft (now aFollowedSurfaceTakesItsAreasFrameWhateverItsHold): a hold taller than the area no longer moves the surface to the bottom edge. * fix(next): S4 m14 (c1): with the switch on, a key spring draws each terminal's reflow preview at its pane's grid; no low-prompt bottom hold cmux-ad m14 (switch motion.frameOwner.terminals on): in every frame each terminal shows pixels drawn at its pane's size, with its prompt in view. F2: restAt held a low prompt's surface at the press's final size on its pane's bottom edge (#35470, #35914): taller than its pane, its top rows cut for the whole spring. H3: TerminalGridPolicy.previewsViewGrid turned the reflow preview off while a reported final grid did not fit the view (the old m6 rule), so a shrinking pane drew the owner's grid from its top-left, its prompt cut until the owner announced the new grid. One decision now drives the frame, the hold and the preview: TerminalHostView.surfaceFollowsArea (the host follows its pane through a key hold with the switch on). - followedSurfaceFrame gives the surface its area's frame, whatever hold it has; the low-prompt branch of #35914 had no key-spring use left and is gone. - restAt holds nothing for a following host and lets go of an earlier hold (a join's), whatever the anchor (G1 released only for .top). The final grid still goes once per press (reportFinalGrid). - updateReflowPreview passes it to previewsViewGrid(followingPane:), which keeps the preview on: Ghostty's ReflowPreview reflows a copy of the terminal to the view's grid with Terminal.resize, which keeps the cursor row in view. Switch off, and splits, closes, zooms and divider releases (no following host): unchanged. TerminalGridPolicyHoldTests covers the new parameter. * test(next): S4 m14 (c1): a key press during a close spring follows only the panes that were at rest The join question of c1, with motion.frameOwner.terminals on: a close spring holds a terminal at its final size and a key press comes before it lands. ScreenContentView.applyPresentation starts a key hold only for a pane not on a layout spring, so the pane still on the close spring does not follow: its surface keeps the close's hold (moved to the key press's final size) until the spring lands. The pane at rest when the key came follows the key hold and has its area's frame in every frame. Both rest at their areas after the landing.
Summary
FRAME OWNER REFACTOR stage S4 for terminals, the key-resize step (cmux-ad m6). #33956 landed the frame owner for terminals behind
motion.frameOwner.terminals, off by default (feate5ccc759105). This PR makes the switch-on path follow the pane in every frame of a keyboard resize, fixes the independent review's findings, changes the key-resize tests to the m6 contract, and keeps the default path's checks with the switch off. The switch stays off by default (lead's order): with it on, a held key resize with 7 or more moving terminals drops more refreshes than with it off (below). The flip goes in its own PR after the Ghostty fork change that cuts the per-frame cost, and only when that A/B is no worse with ON.The m6 contract (lead). In every frame of a key resize, each terminal shows pixels at its pane's size in that frame, or the previous frame's pixels clipped and anchored top-left. The pixels are never stretched and never leave a background gap. The program's grid (the PTY size) changes at most once per press.
The defect. Lawrence on dogfood 363e39f2: "tabbar moves smoothly on ctrl shift hjkl but browsers/terminals do not anymore". In a key resize, the terminal host follows its pane in every frame (FRAME OWNER S3). But the surface kept one size per press:
restAtheld it at the press's final size, or S3 held it at its start size, on the hold's still edges. So the content froze, then jumped. Film on 437f5a22 (px-keys-single-3k-s1r): the terminal right of the divider had its left ring on its pane's left edge in only 14 of 50 motion frames. It was off by -8 to +21 px in 20 frames and cut off in 16.With the switch on:
TerminalHostView.followedSurfaceFramegives the surface the area it shows in, in every frame.restAtstill reports the final grid at the press: that is the PTY's one resize. It no longer holds the surface at the final size.b0a6f600e45); it is reverted. Its test keeps its scenario and expects the top-left anchor.With the switch off, the behavior is the same as before this PR (S3's producer hold on the still edges).
Fixes from the independent review at
665b1874e6e(BLOCK, comment 6635411077), red first:TerminalSurfaceView.updateSurfaceSize,30eccfc5903). TestaTerminalWhoseFrameWaitsAtTheLandingGetsOneGridForThePress(display): red at1578c8aaed7(=712df02da95+ the test fixec9992f5a2a), stepcd3f57162b30f380684570f4: p1 got 61x33, 55x33, 61x33 for one press. Green atec9992f5a2a, stepe62615850f8eb172be13f40e.restAtandfollowedSurfaceFrame,30eccfc5903). TestaPromptOnTheLastRowRidesTheBottomOfAPaneAKeyGrowsDown: red at712df02da95, step88c13d12f2e07855a8ca5037("the terminal kept its final size on 0 frames"); green at30eccfc5903, step59488e61824f720f9e84bf19.5d62688a264is reverted bycc2c26ec9a3. Its red test passed without it (stepaef1ba3d3e128b7e7a5b8b01): the tab is unhidden in the same layout pass in which its area hands it the hold, so no frame shows the stale hold. The test stays as a guard.447f784c3cf, BLOCK, comment 6639299648): with the switch on,restAtreturned early for a followed host with a.topanchor and left an older surface hold until the landing. A J press with a low prompt then a K press kept the bottom hold through the reverse spring; a terminal that joined mid-hold then got a second press sent grid1, grid2, grid1, grid2.restAtnow releases the hold first (5aab4968d19). Tests:aReversePressAfterALowPromptGrowsDownFollowsItsPanered at7abac06b51d, step1b0702f8dbf54ae7091c8dad("24 frames showed a surface off its pane's size after the reverse press");aTerminalThatJoinsAKeyHoldGetsOneGridPerPressThroughASecondPresspassed there in its first form (a tab added new mid-hold hears the follow indidAddSubviewbefore its surface joins, so it never takes a hold), soc4d6c936c92moves it to the parked-tab path the review named: red at4e8f53fc46b(=7abac06b51d+ that test), stepf257aa893b08d66204c893d1("the landing asked the PTY for [80x44, 88x44] after the presses' grids"; the reverse-press test fails there too); green atc4d6c936c92, step4e80341dde6de693eb6a5a67(PaneContentPolicyTests passes).c4d6c936c92, SHIP, comment 6640562380; P2, to fix beforeready-to-land): the default path (switch off) lost its "one surface size per press" checks when the m6 tests turned the switch on.c0c7f9b0100adds the base's five checks back as their own tests, with the switch pinned off after eachawait:PaneContentPolicyTests.aKeyPressSizesEachSurfaceOnceWithTheSwitchOff(4 directions),aHeldKeySizesEachSurfaceOnlyInAPressTurnWithTheSwitchOff,aPressAndItsReverseRestsAtTheStartSizeWithTheSwitchOff,KeyboardResizeContentHoldTests.theProducerResizesOnceWithTheSwitchOffandKeyboardResizeReflowAfterLandingTests.theProducerLaysOutOnceWithTheSwitchOff(left, right). They are the base's tests atbb23623917fwith new names. They guard behavior that this PR does not change, so they pass on the base and at the head: restored coverage, not a fix, so there is no red step.162ea37166bcorrects the comments the review named (P3): the switch's help text, thePaneContentPolicyTestsheader,TabStripFollowsPaneTickTests.Content,TerminalHostView.releaseSurfaceHoldand theTerminalLaunchFrameKeeperTestswaits.6522020512a(the lead: run the tests with the switch off and with it on) gives the switch as an argument,[false, true], to each other test this PR changed that has a meaning in both states: in PaneContentPolicyTests the F3 guard, F2, and G1 (a) and (b); the TabStrip strip-and-terminal test; both KeyResizeFirstTickPinTests tests; the key-press and held-key ground tests of PaneGroundCoversPaneTests; and the two resize saves of TerminalLaunchFrameKeeperTests. With the switch off, each checks the base's rule: at most one new surface size per press, none at the landing, and one grid per press. The m6 contract tests stay switch-on; their switch-off counterparts are the five restored tests. TerminalKeyResizeFrameTests and TerminalSurfaceCommitSizeTests test the frame owner itself, which runs only with the switch on.TerminalFramePresenter.debugState(of:)is debug-only;place,requestWantedFrameandlandRequestedFramehaveperf-hotmarkers; the contract comments say what each switch state does.Files on the per-frame and per-key paths since the films and typing were measured (
8c981e32979):TerminalSurfaceView.swift(F1: the grid report decision; no present change),TerminalSurfaceView+PaneMotion.swift(F2: the press start; G1,5aab4968d19: with the switch on,restAtreleases an older surface hold at a press) andTerminalHostView+PaneMotion.swift(F2: the surface frame of a low prompt in a pane that grows). pixel-perfect refilmed the key journeys atded0cb09a56(the code of447f784c3cfwith the default on; see Observed). G1 changes no frame of those journeys: it acts only when a press comes while an older hold is on the surface (a low prompt's bottom hold, or a terminal that joined mid-hold), and the films have no such press; the two G1 tests check its frames.TerminalFramePresenter.swiftandTerminalSurfaceView+FrameOwner.swiftchange only by a debug guard and comments;TerminalHostView.swiftandTerminalDragTunables.swiftchange only by comments and the switch's help text (162ea37166b).Why the default stays off. In a held key resize with 7 or more moving terminals, ON misses more refreshes: keys-stress held, cmux24 job
c33bd85dbb064376b348d0c2(ref6a266873a17, the app code of665b1874e6e), 6 rounds per arm: 44 missed with OFF and 55 with ON, ON higher in 5 of 6 rounds; the mean main time of a motion frame grows by 0.27 ms with 7 moving terminals. Single presses, the 4-terminal key resize and drags are equal or better, and ON adds no layout per frame (16.9 view layouts per frame in both arms).--rounds=6 --key-repeat=6) is no worse with ON (pixel-perfect's rule: a finding if ON misses more in at least 5 of 6 rounds). The switch deletion follows the flip.Frames (what the person sees in each frame with the switch on, before and after; REVIEW.md section 12). With the default off, nothing changes for a person in this PR:
terminalDrag.holdSurfacekeeps the text through the drag and reflows once at the release).The default stays off (
447f784c3cf). This branch had the default flip (b3ab63bded6, then1e0df52dcc6); the lead reversed it: no new red, and a stage turns ON only when no layer is worse than OFF.TerminalDragTunables.framesOwnedByCommitdefaults tofalse. The key-resize contract tests turn the switch on for their frames (PaneGroundCoversPaneTests.ownFrames(); the store is process-wide, so an async test turns it on again after eachawait), and the default path's guards pin it off (ownFrames(false)). The What's New highlight moves to the flip PR.Tests changed to the m6 contract (cmux-ad m6 (c), for terminals). These test windows draw nothing, so each size shows at once:
aKeyPressShowsEachSurfaceAtItsPanesSizeAndSendsOneGrid,aHeldKeyShowsEachSurfaceAtItsPanesSizeAndSendsAtMostOneGridPerPress,aPressAndItsReverseInOneHoldSendEachGridInItsTurn.KeyboardResizeContentHoldTests.theTerminalFollowsItsPaneInEveryFrameAndSendsOneGridPerTerminal.KeyboardResizeReflowAfterLandingTests.theTerminalFollowsItsPaneInEveryFrameAndItsGridGoesAtThePress....WithTheSwitchOff, with the switch pinned off: each surface takes one size per press, none in a spring frame and none at the landing. The other key-resize tests this PR changed run in both switch states (6522020512a).aKeyResizeKeepsEachStripAndTerminalOnItsPaneInEveryFrameputs a real terminal in each pane: in every frame each strip is on its pane and each terminal at its pane's size, one grid per press. Red on featbb23623917f(step705edba00632aa949c1144e5: the surfaces held at the press's final size), green with this PR (step03f179c8c4a3ed7ec729e21a).Debug only (DEBUG and CMUX_BENCH_HOOKS). The
debug.motiontrace records, per frame and per terminal: the layer rect, the shown frame size, Ghostty's present counters, the presenter state and the host frame state (TerminalSurfaceView+MotionSample.swift,TerminalFramePresenter.debugState(of:)). The film judge uses it to tell which present path drew each frame.Merge of feat (
bb23623917f). #35161 deletedmotion.frameOwner.contentPolicyandPaneMotionHold.containersFollow, so containers always follow. The merge commit removes the key-resize test'scontentPolicyOverridepin, andc642350625aremoves thecontainersFollow:arguments from the kept-frame test.Not caused by the switch, and not held here (pixel-perfect's alternating rounds, STATUS 11574). A whole-window +1 refresh in drags comes and goes between films, in both modes. With build 5eb5406 it followed ON three times in round 1, then showed in two OFF films and in no ON film in round 2. Its owner is not found yet. A drag-cadence test written to find it (
f863559be0c) never reproduced it and is removed here.Testing
Display run at this head: cmux-ci display step
d33333ef60fbc8bb4250b0f0(--display --label gui-session-v1, ref6522020512a03fd915d9a1f9e23c357e7633da20, done on cmux9s-Mac-mini, exit 0): TerminalKeyResizeFrameTests 2 tests, TerminalFrameOwnerMainThreadTests 1 test, TerminalSurfaceCommitSizeTests 6 tests; all passed. The 42 terminal suites at this head: step167bfcdf2e06625c1688bc1d, all passed.TerminalKeyResizeFrameTests(display workers) uses the app'sPaneContentView(tab strip over content area), with a terminal in each of p1 | p2, in a window on screen. It presses Ctrl-Shift-L once on p2, so p2's left edge moves (the film's case). The test steps the spring itself. After each frame's commit, it checks each terminal:It also prints each frame's main-thread time (the step plus the layout).
Red, then green (fleet steps):
TerminalKeyResizeFrameTests: red atb367fadbdae, step4cea036c2b42cb8eab55aab0: misplaced 15, unsized 28 (p2 kept the final size on the still trailing edge; p1 showed its final size before its pane had it). Green atc642350625a, step6082bec2c6cef89d9a1f48d2: misplaced 0, unsized 0, stretched 0, PTY grids per terminal [1, 1]; main ms per frame p50 0.30, p95 0.44, max 0.79 (0.27 / 0.41 / 0.55 at the red). At1e0df52dcc6(the default on): step989a83316987d65da365e75egreen: misplaced 0, unsized 0, stretched 0, PTY grids per terminal [1, 1]; main ms per frame p50 0.34, p95 0.63, max 0.96.aKeptFrameStaysOnTheEdgesItsHoldNames(an S3 hold with a size change): red at0ffa721f8d4, stepcaf779c086289d39ce7fda9d, with "the narrower size did not wait for its frame". Green atc642350625ain stepdac29204614b3d8bbb99c5a7(TerminalSurfaceCommitSizeTests passed).2da280e5c61(switch off), step95093a235a1f69730aa52111: PaneContentPolicyTests, KeyboardResizeContentHoldTests and KeyboardResizeReflowAfterLandingTests fail, each with a surface held at the press's final size while its area moves (for example "frame 1: p1's surface (534.0, 772.0) in a (591.0, 772.0) area"); no grid check fails; TerminalLaunchFrameKeeperTests passes. Green at1e0df52dcc6in the step below.All test suites that drive a terminal surface (every CmuxNextAppTests suite that uses
TerminalSessionorTerminalSurfaceView): at1e0df52dcc6, stepda4c624b9f266aaf509def51: 41 suites pass, and TerminalFrameOwnerMainThreadTests needs a display worker (it runs in the display step). At447f784c3cf(default off): step28e2a76129822912cbeb9023: all 42 suites pass; display suites, step538dccf3f6002506fb3b7549: all 3 pass. At5aab4968d19(the G1 fix): step62747f12ead93b25cd1c67f3: all 42 suites pass; display step840f73703081fc6a206fd781: all 3 pass.c4d6c936c92changes one test only. At162ea37166b(T5's restored tests and the comments): stepc8543eac4261795bf6b12ce6: all 42 suites pass. At the final head6522020512a(the tests in both switch states): step167bfcdf2e06625c1688bc1d: all 42 suites pass, and each both-state test ran its two cases (for exampleaKeyResizeKeepsEachStripAndTerminalOnItsPaneInEveryFrame(direction:switchOn:)with 8 test cases,aReversePressAfterALowPromptGrowsDownFollowsItsPane(switchOn:)with 2); the five...WithTheSwitchOfftests pass. Display stepd33333ef60fbc8bb4250b0f0: all 3 suites pass; the key resize shows misplaced 0, unsized 0, stretched 0, PTY grids per terminal [1, 1]; main ms per frame p50 0.32, p95 1.29, max 13.21 (one frame; the same app code gave max 0.89 at5aab4968d19on another worker; this test does not judge main time, the A/B does). Receipt: SHIP at6522020512a(comment 6642054231, the same reviewer as the SHIP atc4d6c936c92). Atc642350625a, before the test changes, 38 passed and the 7 old-contract tests above failed (stepe9e6a30c3d9effb058d77e64).A/B in one launch (#35407's bench; ref
fix/frame-owner-s4-abbench-nomerge=6a266873a17, whose app sources are those of665b1874e6e: before F1, F2 and G1. The decision to keep the default off does not depend on them; the flip PR runs the A/B at its own head). Command:split-spring.sh --suite=terminal-resize --config=bench --vdisplay=120 --layouts=keys,light,stress,keys-stress --ab=motion.frameOwner.terminals. Thekeyslayout is 4 terminals in a 2x2 grid with Ctrl-Shift-L, then Ctrl-Shift-H. Each round runs both arms, and the arm order alternates per round.b5834b1d8ebc36c55386d7cd, 3 rounds per arm; the light and stress drag layouts did not run because of a bench counter defect, fixed in bench(terminal-resize): probe the drag, accept held terminals, A/B key resizes in one launch #35407):597e886a2188c237394e92ac, ref6a266873a17, all probes passed: the light drag moved -40 pt and 2 terminals took a new grid; STRESS 7):--key-repeat=8, step0ac1a7fbbc0903d1d6413981, cmux-austin-mini-1, a saturated host: the STRESS drag misses about 400 refreshes per arm there): keys 69 / 70, keys-stress 380 / 455 (ON higher in 5 of 6 rounds), light drag 76 / 41, STRESS drag 419 / 406 (OFF / ON); one grid per press in both arms. On cmux24 (jobc33bd85dbb064376b348d0c2,--key-repeat=6) keys-stress held is 44 / 55, ON higher in 5 of 6: the reason the default stays off (below).Films (pixel-perfect, round ab-s4on-sw: build of
8c981e32979, the same app code; cmux24, 120 Hz at 2x; one launch per journey with the switch set and read back in every film; judge 3e.1). m6 failing film frames, OFF / ON:Whole-window "late" frames (E7) in held keys. The first held-2 film had 155 late frames with ON and 6 with OFF. A repeat (ab-s4rep: 3 OFF/ON pairs of held keys in one launch) gave OFF 5, 147, 4 / ON 109, 160, 162. E7 follows each film's fitted clock: in the late films, the tab strips and the terminal content both sit one trace sample behind the fixed clock, for the whole film, and this happens in OFF films too (off2). The person-side measure is the refreshes from each key-down (the app's input journal) to the first divider move in the film, with no trace involved. First press of each film (the only one that starts from rest): OFF 2, 2, 3 / ON 1, 2, 2; single-3k OFF 2 1 1 1 / ON 2 2 1 1; held-2 first press OFF 1 / ON 1. No press is slower in ON as a whole, so the E7 difference is a trace-clock effect, not latency a person sees. A second repeat with per-film debug.frames (ab-s4rep2: 3 more pairs) confirms it: the fitted clock was +1 in all 6 films, and E7 was OFF 73, 160, 158 / ON 158, 158, 162. Over all 7 held-key films per arm, OFF was late as a whole in 4 and ON in 7: the late state follows the run's conditions in both arms. First press, refreshes to the first divider move, over 6 films per arm: OFF mean 2.17, ON mean 1.67.
Typing (pixel-perfect, typing-load-l6 in-build ABBA on cmux22, build of the same app code, the switch set after each relaunch; key to committed frame, ms): ON p50 3.33 and 3.07, p95 8.87 and 8.75, p99 11.40 and 10.65, max 29.6 and 16.3; OFF p50 3.04 and 3.04, p95 8.98 and 8.78, p99 11.42 and 11.97, max 20.6 and 27.8. Mean p99 ON 11.03 vs OFF 11.70 ms; dispatch-to-commit p99 ON 3.74-3.94 vs OFF 5.09-5.64 ms. Typing is not worse with ON: PASS.
Main thread in moving frames (ab-s4rep2, held keys, frames with a view or window layout; debug.frames raw per film), OFF / ON per film:
followedSurfaceFrameis// perf-hot: 4ms motion-frame(owner PL-motion-core). With the switch on it returns at once, with no hold arithmetic. The keys A/B above is its before (off) and after (on). The benches thatperf-hot-benches.jsonnames for that path, basebb23623917fagainst665b1874e6e(the switch on by default there), on aws-m4pro-9 (the windows were not key,-inactive):split-spring.sh --suite=pane-perf(split, close, New Column, zoom and drag with terminals), 3 rounds, base first (steps2ebdfd09d4e2f2955eab6e18,7953a9ae8bf86998b219d294): missed refreshes 1x2 1 / 7, 4x2 6 / 5, STRESS 24 / 24. Main-thread layout per frame p95 within 0.3 ms in every row.6524b0b7715245a0f244440a,9bac0780bb89bdf00476ea0e): 1x2 4 / 4, 4x2 6 / 8. So the 1x2 difference of the first run was order or noise.--suite=anim-perf(tabs, splits, columns, workspace switch, sidebars, Home, chats, palette), 3 rounds (steps894bfe132a38aa27953d00e0,b85d56b55bb8a5c8595a6024): dropped frames 53 / 51 over 27 animations; no row moves by more than 2. Both steps exit 1 on the same 4 rows (tab moves that the bench's layout refuses: "the tab is already at the edge"), in both arms.Observed / Not run
prepush-gate.pypasses.ded0cb09a56(the same code as447f784c3cfwith the default on; pixel-perfect, cmux24, judge 3e.1). OFF arm (F1 is its only change): not worse. held-2: 3 terminal frames (E4) vs 2-3 in the earlier OFF films; close-column-c3: 59 and 60 vs 63 and 60; single-3k: 3 frames at the J press vs 1 earlier, inside the old spread (5 frames in the OFF films ofc4d64b5and30df24f), with the tab strip and the page late by the same amount, so the cause is the commit, not F1; whole-window late 42 vs 51. ON arm: terminal 0 in single-3k, 1 (E4) in held-2, 63 and 60 in close-column-c3.8c981e32979, switch on) the prompt row was hidden at the start of each grow (4 frames at x1, 17 at x0.25). With F2 (ded0cb09a56, switch on) and on the switch-off path, the prompt row stays in view in every frame of the grow-down spring (0 of 23 frames hidden at x1, 0 of 70 at x0.25), and no shell gap opens. pixel-perfect keeps F2 in S4 (STATUS 12689); the lead can reject it until 2026-11-05 12:00Z. When the final grid's frame arrives, the prompt row moves up once (7 px OFF, 11 px ON) because the bottom padding grows, then it rides the bottom edge.665b1874e6e(F1-F3), BLOCK at447f784c3cf(G1), SHIP atc4d6c936c92(comment 6640562380) with T5 to fix beforeready-to-land.c0c7f9b0100,162ea37166band6522020512afix T5 and the comments; the same reviewer's receipt at the final head is the one forready-to-land.c4d6c936c92the head adds and changes tests, and changes comments and the switch's help text only, so the app's behavior is that of5aab4968d19.plans/cmux-next/ghostty-integration-DESIGN.mdsection 3: the CA commit that carries a pane's new frame carries the terminal's frame at the new size, or the previous frame unchanged. The owner is told in STATUS.No capture: with the default off nothing a person sees changes. The switch-on evidence is pixel-perfect's films above (OFF and ON in one launch) with the per-frame judge.
Changelog
none