Palette: keyboard selection scrolls out of view #85

Closed
opened 2026-10-07 14:57:19 +00:00 by kayg · 17 comments
Owner

Owner (2026-10-07, screenshot): in the Cmd+K palette, pressing Down repeatedly moves the selection out of view; the list does not scroll with the keyboard selection. Also each arrow press re-renders the whole list (replaceChildren in core-screen.ts renderPalette), which is wasteful.

Owner (2026-10-07, screenshot): in the Cmd+K palette, pressing Down repeatedly moves the selection out of view; the list does not scroll with the keyboard selection. Also each arrow press re-renders the whole list (replaceChildren in core-screen.ts renderPalette), which is wasteful.
Author
Owner

Starting on branch fix-palette-nav from dev at 9c62448a32.

Starting on branch fix-palette-nav from dev at 9c62448a32a75a1365c4004c982abf3336ca4883.
Author
Owner

Finding: the production Playwright regression fails at 1440x900 on ArrowDown to option 10; its row is outside #palette-results (test/palette-navigation.spec.ts). The current handler replaces all option nodes each press. Baseline handler cost: 0.3200 ms/press over 100 alternating moves, measured with performance.now around dispatched key handlers.

Finding: the production Playwright regression fails at 1440x900 on ArrowDown to option 10; its row is outside #palette-results (test/palette-navigation.spec.ts). The current handler replaces all option nodes each press. Baseline handler cost: 0.3200 ms/press over 100 alternating moves, measured with performance.now around dispatched key handlers.
Author
Owner

Finding: after the first post-fix run, port 4785 was held by a tracer server with cwd /home/kayg/Developer/infraternal/.worktrees/fix-cell-metrics/apps/tracer (verified via /proc); I left it untouched and am isolating the final rerun on port 4285.

Finding: after the first post-fix run, port 4785 was held by a tracer server with cwd /home/kayg/Developer/infraternal/.worktrees/fix-cell-metrics/apps/tracer (verified via /proc); I left it untouched and am isolating the final rerun on port 4285.
Author
Owner

Finding: controlled 100-event measurement on infraternal-dev showed filtering at 0.728 ms/input after the fix vs 0.111 ms/input on the original handler. renderPalette was also calling scrollIntoView for every typed filter; I’m replacing that with a scrollTop reset. Arrow handler measured 0.040 ms/press after vs 0.209 ms/press before.

Finding: controlled 100-event measurement on infraternal-dev showed filtering at 0.728 ms/input after the fix vs 0.111 ms/input on the original handler. renderPalette was also calling scrollIntoView for every typed filter; I’m replacing that with a scrollTop reset. Arrow handler measured 0.040 ms/press after vs 0.209 ms/press before.
Author
Owner

Finding: with stable option IDs, the integrated production test measured 2.779 ms/filter input while aria-activedescendant follows changes between different first results. I’m measuring cached option nodes with the same stable IDs to reduce input rebuild work without weakening that relationship.

Finding: with stable option IDs, the integrated production test measured 2.779 ms/filter input while aria-activedescendant follows changes between different first results. I’m measuring cached option nodes with the same stable IDs to reduce input rebuild work without weakening that relationship.
Author
Owner

Finding: the stable-ID cached-node variant measured 1.147 ms/filter input at load 22.81/11.78/10.60, with no demonstrated advantage over the detached small-list rebuild. I’m keeping the simpler rebuild for typing; arrow navigation retains nodes and measured 0.065 ms/press over 100 moves.

Finding: the stable-ID cached-node variant measured 1.147 ms/filter input at load 22.81/11.78/10.60, with no demonstrated advantage over the detached small-list rebuild. I’m keeping the simpler rebuild for typing; arrow navigation retains nodes and measured 0.065 ms/press over 100 moves.
Author
Owner

Finding: scripts/verify fast stopped in the native VT build because Zig could not fetch two GitHub dependencies (HttpConnectionClosing); no code failure was reported. I’m letting the current gate exit and will retry the fast gate.

Finding: scripts/verify fast stopped in the native VT build because Zig could not fetch two GitHub dependencies (HttpConnectionClosing); no code failure was reported. I’m letting the current gate exit and will retry the fast gate.
Author
Owner

Finding: the workspace test gate also hit an existing fixture path assumption: agent_review creates target/ without creating target/, while CARGO_TARGET_DIR is in the job cache. I’ll point the ignored repo target path at the same scratch target and retry; the failure was FileNotFoundError in verify-agent-attach-credit.py.

Finding: the workspace test gate also hit an existing fixture path assumption: agent_review creates target/<run-id> without creating target/, while CARGO_TARGET_DIR is in the job cache. I’ll point the ignored repo target path at the same scratch target and retry; the failure was FileNotFoundError in verify-agent-attach-credit.py.
Author
Owner

Finished at c59f9a2bb2; worktree clean.

Keyboard navigation updates the old and new option selection in place, keeps the active descendant in sync, scrolls instantly, and adds clamped Home/End plus visible-page movement. Filtering still rebuilds on input. The Playwright regression passed at 1440×900 and 393×852, walking Down to the last row and Up to the first while checking row and group-heading visibility, active descendant, and stable option nodes; Home/End/PageUp/PageDown also passed. TRACER_TEST_PORT=4385 because the requested 4785 was occupied by another worktree.

Measured on infraternal-dev, 8 cores. Baseline load 1/5/15: 21.91,12.88,11.52; final: 21.23,13.09,11.11. With performance.now around 100 dispatched key handlers at 1440×900, arrow time was 0.3250 ms/press before and 0.0390 ms/press after. Filter time was 0.1300 ms/input before and 1.2940 ms/input after.

Production screenshot captures:
apps/tracer/test-results/core-screen/tracer-core-1440x900-paper-palette-open.png
apps/tracer/test-results/core-screen/tracer-core-1440x900-ink-palette-open.png
apps/tracer/test-results/core-screen/tracer-core-393x852-paper-palette-open.png
apps/tracer/test-results/core-screen/tracer-core-393x852-ink-palette-open.png

scripts/verify fast exited 0. Verbatim gate output summary:

+---------------------------+--------------+
| Rust gate                 | Result       |
+---------------------------+--------------+
| fmt                       | PASS         |
| VT native fmt             | PASS         |
| clippy                    | PASS         |
| test                      | PASS         |
| cargo-deny                | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+

PT_INTERP: absent; size: 16161072 bytes (baseline 16142672 + allowance 262144 = max 16404816)
PT_INTERP: absent; size: 13571944 bytes (baseline 13555672 + allowance 262144 = max 13817816)
JavaScript transfer: identity=806007 gzip=245149/307200 brotli=201541/256000 bytes
stream differential: 134 streams, 11439 schedules, zero divergences

 7 pass
 0 fail
 34390 expect() calls
Ran 7 tests across 2 files. [2.84s]
Finished at c59f9a2bb2058d837646ddc9a984950d8cef8ce9; worktree clean. Keyboard navigation updates the old and new option selection in place, keeps the active descendant in sync, scrolls instantly, and adds clamped Home/End plus visible-page movement. Filtering still rebuilds on input. The Playwright regression passed at 1440×900 and 393×852, walking Down to the last row and Up to the first while checking row and group-heading visibility, active descendant, and stable option nodes; Home/End/PageUp/PageDown also passed. TRACER_TEST_PORT=4385 because the requested 4785 was occupied by another worktree. Measured on infraternal-dev, 8 cores. Baseline load 1/5/15: 21.91,12.88,11.52; final: 21.23,13.09,11.11. With performance.now around 100 dispatched key handlers at 1440×900, arrow time was 0.3250 ms/press before and 0.0390 ms/press after. Filter time was 0.1300 ms/input before and 1.2940 ms/input after. Production screenshot captures: apps/tracer/test-results/core-screen/tracer-core-1440x900-paper-palette-open.png apps/tracer/test-results/core-screen/tracer-core-1440x900-ink-palette-open.png apps/tracer/test-results/core-screen/tracer-core-393x852-paper-palette-open.png apps/tracer/test-results/core-screen/tracer-core-393x852-ink-palette-open.png scripts/verify fast exited 0. Verbatim gate output summary: ```text +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | fmt | PASS | | VT native fmt | PASS | | clippy | PASS | | test | PASS | | cargo-deny | PASS | | static builds | PASS | +---------------------------+--------------+ PT_INTERP: absent; size: 16161072 bytes (baseline 16142672 + allowance 262144 = max 16404816) PT_INTERP: absent; size: 13571944 bytes (baseline 13555672 + allowance 262144 = max 13817816) JavaScript transfer: identity=806007 gzip=245149/307200 brotli=201541/256000 bytes stream differential: 134 streams, 11439 schedules, zero divergences 7 pass 0 fail 34390 expect() calls Ran 7 tests across 2 files. [2.84s] ```
Author
Owner

Continuation run 2 starting on branch fix-palette-nav at c59f9a2bb2; base dev: 9c62448a32. I’m profiling filter phases and will preserve the navigation regression coverage while bringing the 100-input filter measurement back to the original 0.13 ms/input.

Continuation run 2 starting on branch fix-palette-nav at c59f9a2bb2058d837646ddc9a984950d8cef8ce9; base dev: 9c62448a32a75a1365c4004c982abf3336ca4883. I’m profiling filter phases and will preserve the navigation regression coverage while bringing the 100-input filter measurement back to the original 0.13 ms/input.
Author
Owner

Finding: the production Playwright harness reproduced the filter regression under systemd-run CPUWeight=10000: current c59f9a2 measured 0.7220 ms/input (load 1.82,8.76,14.02); original dev handler measured 0.1120 ms/input (load 17.08,11.63,14.30). The original run had higher load, so the regression is attributable to added handler work; I’m profiling its phases next. Arrow was 0.0370 ms/press on current and 0.2420 ms/press on original.

Finding: the production Playwright harness reproduced the filter regression under systemd-run CPUWeight=10000: current c59f9a2 measured 0.7220 ms/input (load 1.82,8.76,14.02); original dev handler measured 0.1120 ms/input (load 17.08,11.63,14.30). The original run had higher load, so the regression is attributable to added handler work; I’m profiling its phases next. Arrow was 0.0370 ms/press on current and 0.2420 ms/press on original.
Author
Owner

Finding: phase instrumentation on one filter input at 1440x900 reported filter 0.000 ms, DOM build 0.200 ms, attribute/id assignment 0.000 ms, replaceChildren 0.200 ms, active-descendant 0.000 ms, scroll reset 0.200 ms, handler 0.700 ms, then a forced layout read 0.400 ms (timer resolution ~0.1 ms). No geometry read occurs in renderPalette; the filter rebuild and scroll reset are the handler work to remove. Added the 0.13 ms/input regression assertion first; it fails on current code at 1.213 ms/input (load 16.32,14.58,14.64).

Finding: phase instrumentation on one filter input at 1440x900 reported filter 0.000 ms, DOM build 0.200 ms, attribute/id assignment 0.000 ms, replaceChildren 0.200 ms, active-descendant 0.000 ms, scroll reset 0.200 ms, handler 0.700 ms, then a forced layout read 0.400 ms (timer resolution ~0.1 ms). No geometry read occurs in renderPalette; the filter rebuild and scroll reset are the handler work to remove. Added the 0.13 ms/input regression assertion first; it fails on current code at 1.213 ms/input (load 16.32,14.58,14.64).
Author
Owner

Finding: the first node-reuse attempt that toggled hidden rows still measured 0.5470 ms/input (load 1.03,5.60,10.57), so it does not meet the 0.13 ms target. A one-input phase sample measured 0.500 ms total: 0.300 ms in the scroll reset, 0.100 ms filtering/row work, and 0.100 ms selection/ARIA (timer granularity ~0.1 ms); a post-handler layout read took 0.300 ms. I’m switching to cached nodes assembled into the filtered fragment and will measure again.

Finding: the first node-reuse attempt that toggled hidden rows still measured 0.5470 ms/input (load 1.03,5.60,10.57), so it does not meet the 0.13 ms target. A one-input phase sample measured 0.500 ms total: 0.300 ms in the scroll reset, 0.100 ms filtering/row work, and 0.100 ms selection/ARIA (timer granularity ~0.1 ms); a post-handler layout read took 0.300 ms. I’m switching to cached nodes assembled into the filtered fragment and will measure again.
Author
Owner

Finding: the first fast-gate run also failed static builds because .cargo/musl-cc.sh expects target/auth-tools/zig/zig while auth-toolchain staged Zig in the scratch CARGO_TARGET_DIR; the test fixture likewise expects target/. I’m using the already ignored worktree target path as a symlink to the scratch target for the retry. Rust fmt/clippy passed; the failing test was the known fixture path assumption.

Finding: the first fast-gate run also failed static builds because .cargo/musl-cc.sh expects target/auth-tools/zig/zig while auth-toolchain staged Zig in the scratch CARGO_TARGET_DIR; the test fixture likewise expects target/<run-id>. I’m using the already ignored worktree target path as a symlink to the scratch target for the retry. Rust fmt/clippy passed; the failing test was the known fixture path assumption.
Author
Owner

Finished at 9c3781cca7 on fix-palette-nav.

Filtering now precomputes row/group IDs and uses the direct rebuild path without an extra scrollTop read/write. The regression test enforces the 0.13 ms/input ceiling. The production browser test passed at 1440x900 and 393x852, including Down to the final row, Up to the first, group heading visibility, active descendant, and Home/End/Page navigation.

Performance on infraternal-dev (8 cores; systemd-run --user --scope -p CPUWeight=10000): start c59f9a2 measured 0.7220 ms/filter input and 0.0370 ms/arrow press at load 1.82,8.76,14.02; final measured 0.1050 ms/filter input and 0.0350 ms/arrow press at load 0.92,3.20,8.32. The original dev handler measured 0.1120 ms/filter input and 0.2420 ms/arrow press at load 17.08,11.63,14.30. The c59 phase profile identified 0.2000 ms for DOM build, 0.2000 ms for replaceChildren, 0.2000 ms for scroll reset, and 0.4000 ms for a forced post-handler layout read; performance.now resolution was about 0.1 ms. Hidden-row reuse and cached-node fragment attempts measured 0.5470 and 0.6900 ms/input, so the faster rebuild path remains.

Production screenshots:
apps/tracer/test-results/core-screen/tracer-core-1440x900-paper-palette-open.png
apps/tracer/test-results/core-screen/tracer-core-393x852-paper-palette-open.png

scripts/verify fast passed on this commit. Verbatim gate summary:

METRIC name=network_rtt_selftest configured_rtt_ms=300 jitter_ms=30 loss=unsupported transport=tcp-stream n=3 p50_ms=295.09 p99_ms=314.46 result=PASS
+---------------------------+--------------+
| Rust gate                 | Result       |
+---------------------------+--------------+
| fmt                       | PASS         |
| VT native fmt             | PASS         |
| clippy                    | PASS         |
| test                      | PASS         |
| cargo-deny                | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+
PT_INTERP: absent; size: 16161072 bytes (baseline 16142672 + allowance 262144 = max 16404816)
PT_INTERP: absent; size: 13571944 bytes (baseline 13555672 + allowance 262144 = max 13817816)
JavaScript transfer: identity=806025 gzip=245153/307200 brotli=201559/256000 bytes
stream differential: 134 streams, 11439 schedules, zero divergences

 7 pass
 0 fail
 34390 expect() calls
Ran 7 tests across 2 files. [2.29s]
Finished at 9c3781cca7d2d4cce82627c718dec5c05013fc92 on fix-palette-nav. Filtering now precomputes row/group IDs and uses the direct rebuild path without an extra scrollTop read/write. The regression test enforces the 0.13 ms/input ceiling. The production browser test passed at 1440x900 and 393x852, including Down to the final row, Up to the first, group heading visibility, active descendant, and Home/End/Page navigation. Performance on infraternal-dev (8 cores; `systemd-run --user --scope -p CPUWeight=10000`): start c59f9a2 measured 0.7220 ms/filter input and 0.0370 ms/arrow press at load 1.82,8.76,14.02; final measured 0.1050 ms/filter input and 0.0350 ms/arrow press at load 0.92,3.20,8.32. The original `dev` handler measured 0.1120 ms/filter input and 0.2420 ms/arrow press at load 17.08,11.63,14.30. The c59 phase profile identified 0.2000 ms for DOM build, 0.2000 ms for replaceChildren, 0.2000 ms for scroll reset, and 0.4000 ms for a forced post-handler layout read; performance.now resolution was about 0.1 ms. Hidden-row reuse and cached-node fragment attempts measured 0.5470 and 0.6900 ms/input, so the faster rebuild path remains. Production screenshots: apps/tracer/test-results/core-screen/tracer-core-1440x900-paper-palette-open.png apps/tracer/test-results/core-screen/tracer-core-393x852-paper-palette-open.png `scripts/verify fast` passed on this commit. Verbatim gate summary: ```text METRIC name=network_rtt_selftest configured_rtt_ms=300 jitter_ms=30 loss=unsupported transport=tcp-stream n=3 p50_ms=295.09 p99_ms=314.46 result=PASS +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | fmt | PASS | | VT native fmt | PASS | | clippy | PASS | | test | PASS | | cargo-deny | PASS | | static builds | PASS | +---------------------------+--------------+ PT_INTERP: absent; size: 16161072 bytes (baseline 16142672 + allowance 262144 = max 16404816) PT_INTERP: absent; size: 13571944 bytes (baseline 13555672 + allowance 262144 = max 13817816) JavaScript transfer: identity=806025 gzip=245153/307200 brotli=201559/256000 bytes stream differential: 134 streams, 11439 schedules, zero divergences 7 pass 0 fail 34390 expect() calls Ran 7 tests across 2 files. [2.29s] ```
Author
Owner

Deployed dev 5ed4f29 to monitor01: reviewed recovery backend with the recovery UI held (live sign-in verified: no recovery entry, text "Welcome back / Your passkey is all you need / Continue with passkey"), palette keyboard selection stays in view (#85), deterministic recovery expiry tests (#65). Gate: scripts/verify fast exit 0. Rollback: web.prev / infraternal.prev.

Deployed dev 5ed4f29 to monitor01: reviewed recovery backend with the recovery UI held (live sign-in verified: no recovery entry, text "Welcome back / Your passkey is all you need / Continue with passkey"), palette keyboard selection stays in view (#85), deterministic recovery expiry tests (#65). Gate: scripts/verify fast exit 0. Rollback: web.prev / infraternal.prev.
Author
Owner

Closed by the orchestrator during the 2026-10-28 handover (#254), on the owner's request to close finished issues. Evidence: Palette keyboard selection fix merged (e3e00eaea in dev) and deployed to prod in 5ed4f29 (ancestor of release-prod-live), owner-visible deploy comment, no open findings. Reopen if anything is still missing.

Closed by the orchestrator during the 2026-10-28 handover (#254), on the owner's request to close finished issues. Evidence: Palette keyboard selection fix merged (e3e00eaea in dev) and deployed to prod in 5ed4f29 (ancestor of release-prod-live), owner-visible deploy comment, no open findings. Reopen if anything is still missing.
kayg closed this issue 2026-10-28 17:52:34 +00:00
Sign in to join this conversation.
No labels
launch-qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/infraternal#85
No description provided.