From e6f4772e82fa8b75c52baf14aa026032a844982b Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 30 Jul 2026 02:41:28 -0700 Subject: [PATCH] docs(perf): record rejected recent-tab split --- ...witcher-lazy-visual-boundary-2026-07-30.md | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 tools/benchmarks/results/phase1-recent-tab-switcher-lazy-visual-boundary-2026-07-30.md diff --git a/tools/benchmarks/results/phase1-recent-tab-switcher-lazy-visual-boundary-2026-07-30.md b/tools/benchmarks/results/phase1-recent-tab-switcher-lazy-visual-boundary-2026-07-30.md new file mode 100644 index 00000000000..9c061c0dc1a --- /dev/null +++ b/tools/benchmarks/results/phase1-recent-tab-switcher-lazy-visual-boundary-2026-07-30.md @@ -0,0 +1,162 @@ +# Phase 1 recent-tab switcher lazy visual boundary — 2026-07-30 + +**Result:** rejected. The measured controller/visual split reduced the main-window static +closure by 455 raw bytes, but automatic shared-chunk allocation added one JavaScript file +and raw bytes to both dashboard-popout and web. Only this negative report is retained; all +candidate source/tests were removed, the accepted checkpoint was restored exactly, and no +budget changed. + +## Production ownership and behavior audit + +The accepted `RecentTabSwitcher.tsx` has two deliberately eager input paths: + +1. Native `window.api.ui.onCtrlTabKeyDown` advances the switcher, and + `onCtrlTabKeyUp` commits the selected identity. +2. Capture-phase DOM `keydown`/`keyup` listeners provide the CDP/test fallback. Matching + events call `preventDefault` and `stopPropagation` before terminal input can observe them. + +Both paths use `matchesRecentTabSwitcherChord`, `getShortcutPlatform`, and the current +keybinding overrides. The shared shortcut policy requires Ctrl+Tab, excludes Meta/Alt, gates +the held interaction on `tab.previousRecent`, treats Shift as direction rather than a +separate binding, and recognizes both Control release and the Electron Tab-release fallback. +`Terminal.tsx` intentionally yields matching Ctrl+Tab events to this capture/native owner +before its ordinary previous-recent handler, preserving terminal-first keybinding policy. +Main-window and browser-guest input owners use the same shared policy. + +`openOrAdvance` reads the current store only when invoked. It rejects non-terminal views and +missing worktree identities, then builds the visible model for the active group using the +configured MRU or sequential order. Repeated advances preserve the selected tab key across a +fresh model before applying direction. Commit clears the controller state before activating +the selected editor, browser, or terminal identity. Escape and blur cancel without +activation; unmount removes both native callbacks and all DOM listeners. + +The model is based on the active worktree/group and unified visible tab identities, so it +does not assume a git worktree or local runtime. Folder workspaces, local/WSL/SSH/relay/ +remote-runtime terminals, browsers, editor tabs, providers, and Git policy were not changed. + +The rejected candidate kept `RecentTabSwitcherHost` at the exact existing +`RecoverableRenderErrorBoundary` placement in `App.tsx`. That eager host retained all native +and capture-phase callbacks, model construction, selected identity, commit/cancel, and +cleanup. It used `lazyWithRetry` with reload key `recent-tab-switcher` to load only the +portal/listbox/icon surface after the first successful open. A permanent latch kept the +loaded surface component mounted while closed. Controller tests proved no load for startup +or a rejected open, correct event ordering, repeated advancement, Escape/blur cancellation, +native commit while the chunk was unresolved, cleanup, retry identity, and mounted state +continuity. The unchanged surface preserved its portal, listbox/option roles, accessible +label, exact copy, selection styling, dirty marker, icons, tokens, and floating shadow. + +## Why the candidate was rejected + +B emitted the intended dynamic visual surface: + +- `src/components/tab-bar/RecentTabSwitcher.tsx` → + `assets/RecentTabSwitcher-DbtTOQcn.js` +- 3,359 raw / 1,265 gzip bytes +- SHA-256 `eedd8dc397eb8f0313cfa00dfd7c21b566d241c00de30f4543fb299c55ef1631` +- dynamic importer `_App-DnzSeDqT.js` + +The visual surface was absent from all three B static closures. Its Lucide icon dependencies, +however, caused Rolldown to extract +`assets/createLucideIcon-D99aGs7B.js` as a new shared static chunk: 3,344 raw / 1,100 gzip +bytes, SHA-256 +`adc1731cb067df724d0014e9678010e1affab5e90efafba7d116e6b9579efc23`. +That allocation added one eager JavaScript file to every renderer entry through these exact +B manifest paths: + +- `index.html` → `_App-DnzSeDqT.js` → `_createLucideIcon-D99aGs7B.js` +- `popout.html` → `_workspace-status-iHjOceCR.js` → `_createLucideIcon-D99aGs7B.js` +- `web-index.html` → `_cable-DBe33Ms2.js` → `_createLucideIcon-D99aGs7B.js` + +Moving the icons back into the controller would defeat the visual-only boundary. Replacing +the established Lucide icons, changing the visual copy/tokens, or widening this into a +manual shared-chunk policy was outside the requested small candidate and was not attempted. + +## Fresh A/B production evidence + +The paths below were ephemeral local build directories used during measurement, not durable +artifacts. The hashes, byte counts, manifests, and conclusions recorded here are the portable +evidence. + +- A artifact: `/tmp/orca-recent-tab-switcher-a.QuecOq` +- Rejected B artifact: `/tmp/orca-recent-tab-switcher-b.rroQ8F` +- A transformed 2,003 main, 17 preload, and 9,186 renderer modules. +- B transformed 2,003 main, 17 preload, and 9,187 renderer modules. +- Both builds emitted only the two existing CSS `::highlight(...)` parser warnings. + +| Static closure | A raw | B raw | Raw change | A gzip | B gzip | Gzip change | A JS | B JS | A CSS | B CSS | +| ---------------- | --------: | --------: | ---------: | --------: | --------: | ----------: | ---: | ---: | ----: | ----: | +| Main window | 8,416,540 | 8,416,085 | -455 | 1,877,823 | 1,877,276 | -547 | 292 | 293 | 2 | 2 | +| Dashboard popout | 4,507,253 | 4,508,049 | +796 | 984,615 | 984,849 | +234 | 77 | 78 | 2 | 2 | +| Web renderer | 4,360,652 | 4,361,042 | +390 | 928,355 | 928,648 | +293 | 33 | 34 | 1 | 1 | + +Electron entries were file-for-file identical: + +| Entry | A/B raw | A/B gzip | A/B SHA-256 | +| ------- | ------: | -------: | ------------------------------------------------------------------ | +| Main | 776,873 | 174,092 | `6e75b9d4862f1219c23f16d6c920004167d7dae1995498caaacc5354a9d8f8fd` | +| Preload | 130,798 | 20,642 | `c388a39cdca9609760e286d95b87ad1e53793720450e507f830ff1f6c5bd259f` | + +The complete A and B main trees each contain 184 files and have identical sorted SHA-256 +manifests: +`3576805e0f10c1c6c3ca473257901f824326f4e2b1a0a224bbb50e72eb28a5f2`. +The preload trees each contain one file and have identical sorted SHA-256 manifests: +`3bb30bdb361c7c99cc423e4a4939399f8cb29042d653bdbfe5ef582034d9ed00`. + +The renderer manifests contain 778 A entries and 781 B entries. Their file SHA-256 values are +`92ad617f8c8aee6d4c4a23622f524266cbe9c93b3fe65544d1acb833dc3f0d67` and +`3fd9fb6e490332a02f58538cee915a4399ca6bc81ee6da6fa0514037b8d546e7`, +respectively. Complete output trees contain 972 A files and 975 B files; their sorted SHA-256 +manifests are +`09ff908615c9ec1f7f45bfe4f8929e56c2afa25c4a2b32242665d184f4b15df0` and +`2286af9868206a2fb7626c2ec6c6a6468db096a530ac9b3ce40c9d9a7c3ab759`. + +Sorted static-closure manifests include path, raw, gzip, and file SHA-256: + +| Entry | A rows / manifest SHA-256 | B rows / manifest SHA-256 | +| ------ | ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | +| Main | 294 / `f863c014e40dd4cd49b0864e4fa61c5df98f185712f76b9227fbb5d2cc443488` | 295 / `a130a8d46fe1687b522efd966731a4a640b73bb3728687c7a1ad8d955ee28cdd` | +| Popout | 79 / `b83774dc8005e78455d394f6342824c9c21784c07813e43a3770d6c19ab61d45` | 80 / `c94acad7289c5df20c9c0bf7311f352ac37a12f8c43e81d2ce586a732e5be84d` | +| Web | 34 / `46925643199b3b8f53da06bed01b5bed8ed070d79fb8b6214d70cf796672e571` | 35 / `8ba3246bba99e03360b7d5b3bc58ef06cd738395e1c1081b3ea90ef17aca4526` | + +The accepted renderer-index raw budget remains 8,466,347, preserving its 49,807-byte +headroom over A. Electron main remains within its 825,109 raw budget with exactly 48,236 +bytes of headroom. No budget was changed. + +## Restoration and validation + +After rejecting B, only candidate source/test edits were removed and the accepted source was +rebuilt. The restored build transformed 2,003 main, 17 preload, and 9,186 renderer modules +and is file-for-file identical to A across all 972 output files. Its sorted output manifest +SHA-256 is +`09ff908615c9ec1f7f45bfe4f8929e56c2afa25c4a2b32242665d184f4b15df0`. + +Complete renderer manifest validation checked every import key, dynamic-import key, emitted +JavaScript/CSS file, and declared asset for existence beneath its artifact root: + +| Arm | Manifest entries | Static edges | Dynamic edges | Emitted references | Missing/escaping failures | +| --- | ---------------: | -----------: | ------------: | -----------------: | ------------------------: | +| A | 778 | 6,247 | 213 | 860 | 0 | +| B | 781 | 6,294 | 214 | 863 | 0 | + +- Candidate focused suite: 4 files and 42 tests passed before the rejected boundary tests + were removed. +- Restored switcher/model/shared-policy/main-window/browser-guest suite: 5 files and 154 tests + passed. +- `pnpm run typecheck:web`: passed. +- Targeted `oxlint --deny-warnings`: passed over App, switcher/model, Terminal, shared shortcut + policy, and focused test source. +- Targeted `oxfmt --check`: passed over the same source plus this report. +- `pnpm run check:max-lines-ratchet`: passed with 354 grandfathered suppressions and no new + bypasses. +- `pnpm run check:electron-bundle-budgets`: passed against the restored A output. +- `git diff --check`: passed. + +## Residual risk + +The accepted switcher visual remains eager. A later attempt would need a bundler allocation +strategy that keeps the visual dynamic without extracting Lucide's factory into every entry; +that is a shared renderer chunk concern rather than a safe change to the input controller. + +No packaged launch smoke was run on macOS, Linux, or Windows. Production builds and complete +manifest validation ran on this macOS worktree, so packaged cross-platform launch behavior +remains unresolved.