Repository navigation
perf(ui): gate the frame path (slice 1.12d) - #119
Merged
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 withMAYFLY_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: thegalleryandfocusscenarios of the PTY bench against coarse ceilings, scaled byBENCH_PTY_SLACK(3 in CI). It runs with the smoke.packages/mayflyAGENTS.mdstate 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.Ceilings and where the build sits
/settings,/model,/theme,/help)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:retained4,816 tests with no stale row; 60 shots current; 174 golden walks match;smoke:happypasses;bench:pty:assertpasses both scenarios.node --test script/tests/*.test.mjs: the planner selectstest:retainedfor a compiler change and keeps CI in step with the local gate.🤖 Generated with Claude Code