base red: gallery health lets rows above a bottom-opened line rise (#32644's design); row grammar opens before first paint
Base red: gallery health on feat-cmux-next. This fixes three of its layout-shift states.
What was red, and why
agent-pane.agent-pair-note#held-list (0.0059 vs baseline 0) and agent-pane.composer#queued-from-agent-open (0.031 to 0.034 vs 0). In both jobs that render them, the rows above (user-1, assistant-2) move up 3 to 7 px when a disclosure opens at the bottom of the transcript.
- Culprit: #32644, "Agent transcript: opening a line at the very bottom pushes the text up". Base push runs are clean at its parent (cd8e520d8f) and red from its own merge (18d4c70be5) on. That PR added
opensBody(toggle)to the bottom stack fold, so the agent-pair "Show held messages" toggle and the pending messages disclosure now take the fold too. - This is a deliberate design. Leo, 2026-11-02: "YOU EXPAND UP WHILE YOUR SCROLL IS AT THE VERY BOTTOM AKA YOU PUSH THE TEXT UP VISUALLY INSTEAD OF EXPANDING DOWN LIKE USUAL / JUST THAT CASE". #32943 refined it on 2026-11-03 so that only the text above moves.
- The box tracker (
webviews/src/gallery/shift.ts) already lets rows after the target be pushed straight down. It still scored the designed rise of the rows above as shift.
agent-pane.transcript-row-grammar#open (load 0.3295 vs 0.06552; red in one job, 0 in the other, and 0 on one retry of the other). The variant clicks its toggles in a layout effect. The rows subscribe to the disclosure store (useSyncExternalStore) only in a passive effect, which can run after the first paint. Some runs painted the rows closed and then open a frame later. The CLS sources are the three rows under the first one, plus the send line's actions and chevron. Present since the variant landed (#28291).
Fix
shift.ts: a tracked box that comes before the step's target and moves straight up is exempt, but only while the target's transcript row carriesdata-bottom-fold. App.tsx sets that marker for exactly the length of such a fold. Anywhere else a rising row still counts, and so does a drop or a sideways move. This mirrors the existing push-down exemption. There is a unit test intest/gallery-shift.test.ts.RowGrammar.gallery.tsx: after the clicks, the layout effect remounts the rows under a new key. They read the store before the browser paints, so the first frame is the open one.
No baseline file changes.
Not fixed here (both flaky, red in one of two jobs)
- pages.home-channels#emoji-return (type "rocket", 0.083 vs 0.057): typing filters the grid one letter at a time. How many of those in-between grids get painted depends on frame timing. The variable part is one CLS entry (0.0096 in one job, 0.0351 in the other).
- agent-pane.composer-context#repo-worktrees-search (type "unread", 0.0070 vs 0.00447): the list moves 7.5 px in both jobs. In one job it also changes width by 11.8 px (and its check mark moves by 11.7 px), which matches a scrollbar appearing in an in-between filter result on the Linux runner's classic scrollbars.
I left both for a follow-up rather than gaming the play.
Risk
Low. Gallery tooling and one gallery fixture only; no shipped code. The exemption applies only during a bottom fold the app itself declares.
Agent session: agent session
🤖 Generated with Claude Code
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes the red gallery health states on feat-cmux-next. The box tracker now accepts the rise #32644 designs into rows above a line opened at the transcript's very bottom, and the row-grammar fixture paints its open state on the first frame. Gallery tooling and one fixture only; no shipped code.
Bug Fixes
- A box before the target that moves straight up is exempt only while the target's row carries
data-bottom-fold(set by App.tsx for the length of that fold); drops and sideways moves still count, covered by a new unit test. Fixesagent-pair-note#held-listandcomposer#queued-from-agent-open. RowGrammar.gallery.tsxremounts its rows under a new key from the layout effect, so the disclosure store is read before the browser paints and the closed-then-open first frame is gone.
Two other states (pages.home-channels#emoji-return and agent-pane.composer-context#repo-worktrees-search) remain flaky and are left for a follow-up rather than patched around.