# Timeline minimap evidence reproduction

Synthetic data only. No live userdata or copied database was used. Both checkouts created their own database with their own migrations. No repository files were edited or committed.

Before: [7171fdd](https://git.cafe/pingdotgg/t3code/commit/7171fdd7716b05dc1b4be40c26ac78e8bb35f80c). After: [790f0ac](https://git.cafe/pingdotgg/t3code/commit/790f0ac13612fd5b2453d75c76e1a070048b5a3e).

## Canonical run

1. Put these scripts in `/tmp/lead-33036-r2/evidence-capture-1/`. Install dependencies in both checkouts with `vp install`. The scripts import the installed Playwright 1.60.0 package from the after checkout. Chromium is headless 148.0.7778.96 on Linux x64.
2. In each checkout, start a fresh isolated server with `T3CODE_DEV_SEED=0 NODE_OPTIONS='--import=/tmp/lead-33036-r2/evidence-capture-1/freeze-clock.mjs' vp run dev`. Capture the launch PID and redirect output to `server-before-fixed.log` / `server-after-fixed.log` in scratch. Never use live userdata and never set VITE_HTTP_URL or VITE_WS_URL. Read the real `[dev-runner]` ports; this run used web 8524/server16564 before and web7856/server15896 after. If they differ, update the two port choices in the drivers.
3. Pair each fresh browser using `node pair-bootstrap.mjs before` and `node pair-bootstrap.mjs after`. Pairing URLs, startup logs and the `auth-*-private.json` states are credentials; keep them private. This helper is the streamlined fresh-run replacement for the exploratory bootstrap.mjs.
4. Run `node seed.mjs before` and `node seed.mjs after`. The synthetic ACP agent replies through the genuine provider adapter and orchestration RPC. No paid agent or private runtime model is used. The fixed Date also runs in the browser; timers and performance.now remain real. Seed scripts write timestamped output folders. Use fresh databases for repeat runs because thread IDs are deterministic.
5. Run `node capture.mjs before R01,R02,R03,R04,R07,R13,R10,R05a,R05b,R08,R12`, then the same for after. Drivers write a new timestamped folder, preserve old runs, record the entire viewport with CDP and encode MP4 with ffmpeg. Frame receipt timestamps use the same Node performance.now clock as action observations; older compositor frames are discarded. Concat durations preserve those intervals before30fps resampling, and recording-clock.json records the exact capture duration. Interaction JSON logs the actual time of each observation, row coordinates, in-view flags, disabled state and active preview. A mark occurs after the action's wait, so its time is a settled observation, not a mouse-down timestamp.
6. `verify-seed.py` normalizes both stored projections to role/text/createdAt and confirms equality. Update its two timestamped seed directory names for a new run. The uploaded fixture-data.json is the verified synthetic content from this run.
7. Install Pillow into an isolated scratch venv if needed; run `assemble.py` there. It chooses each side's latest complete capture, makes amplified pixel masks with ImageMagick compare at fuzz0% and fuzz2%, and combines full viewport originals and masks into sheets. `review-videos.py` decodes each MP4 to one-frame-per-second review sheets; it does not replace the recordings. `publish.mjs` uploads the selected artifacts and scripts with ghdrop upload -m. `write-evidence.py` uses the uploader's exact Markdown output to make the ready-to-paste section.

## Fixture content

Pair: turn-0/reply, turn-1/reply, turn-2/reply, pair-a, pair-b/reply, turn-3/reply, turn-4/reply. Each reply is `Reply.\n\n` repeated120 times. Triple adds pair-c with the reply after pair-c. Terminal removes turn-4's reply. Short contains first,last with no replies. User/assistant content and creation times were verified identical across revisions.

## Labelled harnesses

R10: real app route and minimap, with timeline inline padding26px and unlimited content lane, yielding measured gutter32px and hit strip20px. Rail hidden at rest, visible under hover/focus. The app's usual minimum desktop gutter prevented this exact presentation through width settings alone.

R12: real app route, Full chat width, controlled inline padding0px yielding measured gutter6px and hit strip0px. Tab reaches the rail from Previous; arrows/Home/End/Enter/Space exercise keyboard selection. Programmatic focus establishes the starting Previous button; this is not proof of the entire page's Tab order.

R05a/R05b: real app route and minimap with fractional top padding and DPR2. A retained real list ref is found through the React development tree. Only pair-a's `getState().sizeAtIndex` result is replaced with that row element's measured fractional DOM height (50.75px); cached row size was51px. Other list state, real scrolling, minimap logic, data and inputs stay in the actual app. This explicitly labelled state/geometry harness demonstrates strict size/range arithmetic; it is not proof that every natural layout reports fractional sizes. The badge reports the actual DOM bottom relative to the scroll viewport at setup. Coordinates24,24.5,25,25.5 are asserted. The state-harness replacement is applied identically to both revisions. The ordinary R01/R02 app captures have no style or list-state overrides.

## Scripts retained for audit

bootstrap.mjs and inspect.mjs are the initial, unsuccessful native-session import exploration. Importing session history did not materialize the intended message list; final evidence uses seed.mjs genuine message.dispatch turns. inspect-current.mjs, probe.mjs and inspect-ref.mjs inspected only the isolated synthetic app and list geometry. Their private/raw default-provider screenshots and browser-auth files are deliberately not published. These exploratory scripts are included so every seed/driver written is available; they are not canonical setup steps.

## Capture conventions and limitations

No device emulation, fine mouse pointer, DPR1 except the DPR2 boundary harness,100% font scaling, root16px, en-US/UTC, fixed wall clock2026-11-10T09:00:00Z. Viewports/themes are recorded per row. R03 records normal smooth scrolling; every settled PNG temporarily uses reduced motion. Each still waits for network idle and fonts, then requires byte-identical full viewport screenshots500ms apart. A magenta pointer ring is an identical input-only overlay. Navigation-focus previews in R13/R12 and boundary-focus previews in R05b are intentional, not stale popovers. Content naturally clips at scroll viewport boundaries; the old user tail at the top is the bug geometry itself.

R07 reveals a shared behavior: Next to the terminal short user stops180px before the physical max scroll, leaving it behind the composer. A manual wheel reaches physical bottom and reveals it on both revisions. The settled pair is identical at zero threshold. This row is evidence of the behavior, not a claim it was fixed.

390px phone viewport is not captured: it has no minimap pointer hit area. Fine-pointer keyboard access is sampled separately by R12; native mobile uses a separate component.

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