Environment for every recording: Linux web development build, Chromium preview browser, dark appearance, 1280 × 800 CSS viewport. The preview recorder emits 2560 × 1600 pixel MP4 files. Both independent servers use their own fresh development databases, with automatic seeding disabled. The same 180 synthetic GitLab repositories are added through projects.mutate. Real glab calls an HTTP fixture, not the public GitLab service. No application source is patched.

The fixture has 120 merge requests in project-001 and two in every other project. Timestamps and rows are fixed. It emulates a host reporting GitLab 18.5.0. Author/reviewer queries for demo-viewer are empty. Every listing request has 150 ms fixture latency. Other response/CLI overhead is real. These measurements do not predict public GitLab latency.

Primary comparison: on each side open /pull-requests?involvement=all&state=open&host=gitlab.com; wait for the initial 457 rows and enabled Load more. Start recording from this same settled page. Click Refresh pull requests; wait for enabled Load more; scroll that button into view; click it; wait for Synthetic change 120 for project-001; scroll that row into view; return to the top to show the final count. Author and label pickers remain closed. Each side reaches 478 rows. Refresh recording duration includes operator/automation gaps and is not a precise list-call benchmark.

Cold-opening request counts are separately captured in requests-evidence.jsonl: stage 'before' is base, 'head-warmup' is head. Filter initial listing requests by variables.first == 99 and no non-null variables.after. The page lists All first and then its Authored and Reviewing partitions, hence three groups of reads. Base: 540 single-project listing reads plus 180 version probes. Head: 54 batch listing reads (18 groups of ten for each partition) plus one version probe. Pagination uses project-001 after=99. Other request kinds, including row statistics and glab telemetry, are logged separately and are not included in these list-read counts.

Supplemental head failure: enable fail=true and failSingle=true in scenario.json, then refresh the same page. The batch containing project-011 gets HTTP 503. Fallback individual reads succeed for its nine sibling projects; project-011 fails both GraphQL and REST. Other batches retain their rows: 455 initially and 476 after Load more. Open Filters > Project, scroll Project 011 into view, and observe Unavailable beside it while successful rows remain visible. Close both menus, then exercise Load more. The request log includes the failing batch, individual fallbacks, and the REST failure. This is an injected HTTP 503, not an HTTP 429/backoff demonstration.

browser-driver.js contains the RPC seeding and independent runtime inspection functions used in the browser. Invoke evidenceSeed with the fixture root after seed.py creates its repositories. evidenceInspect reads both pages from the actual server and records row identities and cursors; it does not mock the server. The runtime JSON files are these responses reduced to synthetic row fields and project errors, with no private paths or credentials. The before/after normal runtime files each contain 457 first-page rows, 21 continuation rows, and zero overlap. Failure runtime contains 455 plus 21 rows, one project error, and zero overlap.

Run fixture.py from the artifact directory. Configure glab with an isolated config directory, synthetic credentials, GITLAB_API_HOST=127.0.0.1:18965, and GLAB_API_PROTOCOL=http for both app servers. Start each worktree with T3CODE_DEV_SEED=0 vp run dev. Authenticate each browser with its own app pairing flow; do not publish the pairing URLs. Use the actual dev-runner ports. Keep the inactive side off Pull requests to prevent background refreshes crossing capture stages.

AI-assisted; verified with the tests and tools listed above.

The final primary recordings use capture-driver.js without failure injection. The supplemental failure recording uses the same refresh/menu/pagination flow but ends at row 120; the final return-to-top hold was added for the paired primary recordings. The failure still was captured twice more than 500 ms apart and both screenshot files had SHA-256 8bc9234ea1321707d783b0ecce3b3670a11f2e76b8edc3bb29772543d50610ff. No list rows are blurred. All captures were opened and visually reviewed.
