Skip to content

perf(ui): gate the frame path (slice 1.12d) - #119

Merged
dossettg merged 5 commits into
feat/ui-foundationfrom
feat/ui-foundation-1-12d
Oct 9, 2026
Merged

dossettg merged 5 commits into
feat/ui-foundationfrom
feat/ui-foundation-1-12d

Conversation

@dossettg

@dossettg dossettg commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator

Part d of slice 1.12, the last, stacked on #116, #117, and #118 (review the fourth commit). No runtime change.

Why

The work budgets were green while a side pane held a core, because nothing measured the path a frame takes on screen. Parts a to c added that measurement and fixed what it found; this part makes the two checks that would have caught it part of the gate.

What

  • pnpm run test:retained (script/test-retained.mjs): every suite but the work budgets with MAYFLY_UI_VERIFY_MEMO=1, so each retained-row hit paints again and throws when the rows differ. It runs in the full gate and in CI after coverage, and in a changed-files gate over the core, design, and e2e suites when a file a compiled surface paints from changed.
  • pnpm run bench:pty:assert: the gallery and focus scenarios of the PTY bench against coarse ceilings, scaled by BENCH_PTY_SLACK (3 in CI). It runs with the smoke.
  • Instructions: the root and packages/mayfly AGENTS.md state the retained-row rules (what moves the epoch, what is volatile, how a moving cell arms the clock, one viewport per layout pass) and where frame cost is measured.
  • Roadmap: slice 1.12d and the final frame budgets.

Ceilings and where the build sits

Scenario Measure Ceiling This branch
Side pane loaded idle CPU 10% 0.2%
Side pane loaded key to paint, median 20 ms 5.9 ms
No pane key to paint, median 10 ms 1.6 ms
No pane CPU per typed key 5 ms 1.5 ms
No pane CPU per cursor key (/settings, /model, /theme, /help) 12 ms 2.4 to 5.1 ms

With the frame path broken (slice 1.11) the first two rows read 113% and 164 ms.

Verification

  • pnpm run verify:full -- --base origin/feat/ui-foundation: green. Coverage run 4,842 tests at 100% per file; test:retained 4,816 tests with no stale row; 60 shots current; 174 golden walks match; smoke:happy passes; bench:pty:assert passes both scenarios.
  • node --test script/tests/*.test.mjs: the planner selects test:retained for a compiler change and keeps CI in step with the local gate.

🤖 Generated with Claude Code

dossettg and others added 5 commits October 9, 2026 01:58
The work budgets counted a compiled component's render and missed the
lane measure, pi-tui's native layout, and the animation clock, which is
where a frame's time goes. Add frame workloads W13-W17 that paint through
the alternate screen and the surface renderer, six counters for them, and
a PTY bench that reports idle CPU and key latency against a mock LLM.
Budgets start at today's counts; the following parts lower them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A side lane was rendered only to be measured, and the measure was thrown
away; a frame with a scroll view rendered the tree before laying it out;
every stack child re-walked the control tree when the layout viewport
changed; painters crossed service proxies on every row; the editor shell
snapshotted the keymap on every keystroke.

- side lanes answer their measure with no rows and the native layout
  entry prepares itself (list fitting, focus-move report)
- a frame with a scroll view runs the constrained layout alone
- the control walk is remembered per viewport and a layout pass
  reconciles once per set of controls
- static leaves read the glyph mode once, a framed row is measured once,
  charts are pure leaves, hint translations are remembered per revision
- the animation clock paces its ticks by the cost of the last frame
- matches() resolves keys once per keymap change; the shell snapshots
  the keymap only for an extension that declares an action
- an arrow at the edge of the only group skips the geometry layout

W14 (a tick on a side pane): 949 leaf renders and 328 control walks down
to 385 and 0. Gallery pane: frame 206 ms to 63 ms, key 164 ms to 40 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pi-tui lays a pane out natively on every frame and renders a component
once per ancestor stack that measures it; nothing below the frame memo
was remembered, so a key in the editor repainted a whole side pane and a
loader below the fold repainted it ten times a second.

- a layout pass has one viewport: a measuring render is not a new frame
- one epoch per surface, moved by everything that can change a row but
  the clock; a key that found nothing to do leaves it where it was
- every component handed to pi-tui answers a repeat render from a memo
  valid for one epoch; a live prompt, the host editor, and a failed
  paint are never remembered
- a render that reads the clock holds for that frame only, a tick asks
  for a frame without invalidating, and the clock is armed only by a
  moving cell that can be on screen
- column and row stacks that paint pi-tui's rows, place their children,
  skip the measure nobody reads, and remember each composited row
- verifyRetained / MAYFLY_UI_VERIFY_MEMO=1 paints again on every hit and
  throws on a difference

W13 (a key beside an unchanged pane): 447 leaf renders to 0. W14 (a
loader below the fold): no tick. Gallery pane: idle CPU 113% to 0.2%,
key to paint 164 ms to 6 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The work budgets were green while a side pane held a core, because
nothing measured the path a frame takes on screen. Two checks now do.

- test:retained runs every suite but the work budgets with
  MAYFLY_UI_VERIFY_MEMO=1: each retained-row hit paints again and throws
  when the rows differ. It runs in the full gate and in CI after
  coverage, and in a changed-files gate when a file a compiled surface
  paints from changed.
- bench:pty:assert runs the gallery and focus scenarios of the PTY bench
  against coarse ceilings (idle CPU, key to paint, CPU per key), scaled
  by BENCH_PTY_SLACK; it runs with the smoke.
- the root and packages/mayfly instructions state the retained-row rules
  and where frame cost is measured; the roadmap records the slice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dossettg
dossettg merged commit 68130f3 into feat/ui-foundation Oct 9, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant