ghostty-next: a scroll region scroll breaks the soft wrap across its edges (VT fuzz finding E2)

LA
5d ago
+66-6

Summary

  • Fixes VT fuzz finding E2 (resize P0). After DECSTBM 3;10 and a soft wrap at the region's bottom, an attach replay drew the row under the region 43 or 163 columns to the right.
  • Root cause, in ghostty-next: a full-width scroll region scroll moves the rows in the region and keeps the rows outside it. The row above the region kept wrap, and the row below kept wrap_continuation, but their neighbour in the region was replaced. Reflow and the unwrapping formatter then joined rows that are not one line. This applied to cursorScrollRegionUp (fast and slow paths), cursorScrollAbove with a bottom margin, insertLines and deleteLines.
  • Fix: Screen.breakWrapAbove clears the flag pair (and a spacer head) at each region edge after the scroll. At a bottom margin, the row that scrolls up first drops its soft wrap into the row below the region (cursorResetWrap in index and scrollUp). printWrap keeps its own wrap, because that wrap goes into the new row. As defense, the formatter carries blank cells only across a joined wrap.
  • Pin: GhosttyNextKit xcframework-8444cc1eb8436b090fdccbdb2369e9c3816f9f26-apple-v6 from fork branch release/cmux-9ce9b7d0-e2. That branch is the current pin 9ce9b7d08 plus only fork PR manaflow-ai/ghostty-next#66 and the release workflow change of manaflow-ai/ghostty-next#67. This PR does not bring in the v1.4.0 / Zig 0.17 merge.
  • The ghostty-next gitlink, package-notices.json and vt-link-graph.json move with the pin, as in the 9ce9b7d bump (b5f678c1d49d). docs/ghostty-fork.md has the fork note and conflict notes.

Testing

Red first. The focused command is cmux-ci run --class light --script scripts/ci/cmux-tui-rust-check.sh --arg test --arg vt_replay_keeps_a_stale_wrap_continuation_row_in_its_column --ref SHA.

  • Base (red commit ca1c7b0487e, old pin): fails, fleet step 480ed523. The replayed viewport has the row under the region shifted right.
  • Head ebef3a54978: passes, fleet step 8830981cc72f78a96045df62 (Actions run 41237606815: the focused test and the other 5 vt_replay tests pass).
  • Fork Zig tests (fork CI "Patch unit tests"):
    • Red: run 41231126621 (5 of 5 new "breaks the wrap" tests fail on 771194607), run 41235852185 (the 3 moved-row tests fail on 93b615013, before the review fix), and run 41231128585 (the formatter test fails on 19ab1cbcb).
    • Green: PR #66 run 41235841145; a broad run 41235854294 (2289 of 2289 libghostty-vt tests with the Terminal, Screen, PageList, Page, formatter, Terminal vt and replay filters); the release run 41235900646, which built and published this kit.
  • Review: the first review blocked a8e90999146 (the row that scrolled up at a bottom margin kept its wrap, and two tests checked the wrong row). That is fixed in ebef3a54978.

Observed / Not run

  • Observed: the red and green results above. The kit's SHA256SUMS lists eee858c7830376f0e99e7e612de60b9c731d11a681cb1c27eb1f9174d4c8516f, which is the checksum in Package.swift.
  • Observed on the fleet with the journey r3-e2-reattach (kit c370e63cead): a line soft-wraps from the region bottom into the row under it, the region scrolls, then quit and reopen.
    • Base f2f3083b: red in 3 of 3 valid rounds at the replay compare (s1 cmux18 20:25Z, s3 cmux22 20:28Z, s5 cmux22 20:31Z; app 219 characters, daemon 218).
    • This head (build 07ff972e1dab6961f7bd2302, tag e2-wrap-edge-v1): green in 3 of 3 valid rounds (cmux22 20:38Z, 20:40Z, 20:42Z).
    • Set-up not-runs (before the journey's first check) are not counted: 2 on the base, 1 on the head.
  • Not run: an iOS build with the new kit. The macOS build above uses it.
  • Not changed here: the iOS link sets and Settings acknowledgements (ghosttykit_repin.py). They name the old pin until one repin lands for this pin, as with the 9ce9b7d bump.

Changelog

  • Fixed: a terminal row under a scroll region no longer moves to the right after cmux reattaches to its session.