# Revised fresh proof audit — final listener attachment diff

PR: [#22788](https://git.cafe/pingdotgg/t3code/pull/22788), [GitHub mirror #22788](https://github.com/pingdotgg/t3code/pull/22788).

Reviewed head: [ca449ec80e](https://github.com/pingdotgg/t3code/commit/ca449ec80ee41858d61e7ad1d48e83308f20872c), full ca449ec80ee41858d61e7ad1d48e83308f20872c. Base: [74541bab17](https://github.com/pingdotgg/t3code/commit/74541bab17270c35f4418d4e543d85628632ed8f), full 74541bab17270c35f4418d4e543d85628632ed8f. Checkout /home/ubuntu/wt/issue-20366; branch fix/issue-20366. This replaces /tmp/pr-22788-proof-pass1.md in full.

## Findings

No concrete code bug identified in the complete four-file final diff. Proof blockers remain; this is not merge approval. The production overflow fallback and its test are absent. No predicate change, composer-layout change, style change, schema change, provider change, or native mobile change belongs in this matrix.

The replacement moves listener lifetime from a finite requestAnimationFrame retry window to the actual mounted LegendList node: ChatView → MessagesTimeline → ConversationTimeline.setTimelineList → list.getScrollableNode → setTimelineScrollNode → effect install/cleanup. ChatView is the sole production caller of both the hook and MessagesTimeline. The ref callback also still sets listRef and registers the canvas timeline. Its stable identity avoids null/node churn on every streaming render. The state update occurs at node identity changes; input/stream events do not add per-event state updates beyond existing follow transitions.

The list returns a loading/empty placeholder without LegendList for empty inactive history. When rows/work/header/footer require a list, its ref delivers the node. Replacement/unmount reports null and effect cleanup removes scroll, wheel, touchmove, pointerdown and document keydown handlers. Thread changes can reuse the same node (dataVersion/list identity changes, no thread key on LegendList); these must keep their listeners and read current refs. A true list→placeholder→list cycle exercises new-node cleanup/attachment. Test both phases in one lifecycle recording.

Listener bodies retain their prior behavior. Wheel-up uses measured real-content overflow; ctrl-wheel and independently scrolling nested content are ignored. Touch requires leaving the end band. Pointer on scrollbar uses overflow; other pointer targets require away-from-end. Document navigation keys accept timeline/body/root targets and reject editable/floating-layer/modifier/composition/defaultPrevented cases. Scroll-only upward detection handles chained iframe scrolling and excludes programmatic anchor motion. Composer PageUp/PageDown has a separate existing controller. Tool start can release a new-turn anchor only while anchoring; manual cancellation changes mode and disables LegendList maintainScrollAtEnd. Pill click restores composer, sets following-end, removes anchor and scrolls on the next frame. Those unchanged paths require runtime acceptance, not independent visual fixture families.

The added browser regression waits beyond the old frame budget while the placeholder is present, then mounts an overflowing list and asserts synthetic wheel-up disables follow. It does not send a native scrolling gesture or assert native PageUp, stable paragraph position across tool start/finish, pill visibility/click, replacement cleanup, or short fitting content. The user's fail-old/pass-new result is reported, not independently rerun here; browser launches were prohibited. Focused existing timelineScrollTarget unit tests: 7/7 passed. Full diff whitespace check passed. No repo-wide checks.

Prior failed-fix runtime log /tmp/issue-20366-runtime-failed-fix.txt reports native PageUp moved scrollTop 15046.5→14392 while follow stayed true, then tool boundary snapped to 19297, with measured tall content and the expected scroll node. This refutes the old overflow-only explanation; it does not independently prove missing listeners. Establish final-head cause by observing delayed mount plus actual event→follow=false and successful hold, preferably with listener lifecycle timestamps. Do not claim listener absence from the previous failure alone.

## Minimum fixture matrix

W = 1440×900 and 390×844 CSS pixels. T = explicit Light and Dark. B = pinned base; A = pinned final head. Run each of the two visual fixtures in W×T on B/A: eight matched pairs / sixteen fixture runs total. Continuous clips can include all phases. No diff-pane axis. Keep normal pane/sidebar geometry fixed within each pair.

| ID | Visually distinct fixture | Lifecycle / interaction phases in the same fixture | Required evidence | Current status |
|---|---|---|---|---|
| L | Overflowing prose → tool start → tool finish → more prose | Enter with hydration held beyond 20 frames; release into actual list. Begin following and show live growth. Native wheel-up on wide web; native touch-away on narrow touch-capable web. Keep a named paragraph at a measured viewport offset through tool start, finish and subsequent prose. Show pill, click it, show pill clears and further chunks follow. Switch to another seeded thread and back while streaming; then force actual list→placeholder→new-list and repeat detach/hold. | B/A actual-app streaming recordings, indexed phases, matched PNGs at entry, detached, tool-running, tool-finished/prose, resumed, replacement. Native PageUp from prose/body must be a supplementary trial in this fixture because that was the failed-fix trigger. | REQUIRED / no final-head artifacts supplied to this audit |
| S | Real content fits above composer throughout | Same delayed mount and replacement mechanism; ineffective upward wheel/PageUp on wide, touch on narrow; one short prose chunk, compact tool start/finish, another short chunk. Never exceed usable content height. | B/A actual-app streaming recordings showing follow stays enabled, no false pill, later prose remains visible. PNG pair at matched stable phase. Log measured real-content fit, not merely scrollHeight including reserved space. | REQUIRED / unverified |
| I (supplement to L/S) | No additional visual fixture | At wide Light use Home/ArrowUp, scrollbar drag, downward/end rearm, composer PageUp/release; nested scrollable tool output consumes wheel, ctrl-wheel does not detach. At narrow use real touchmove. Verify old detached node no longer responds after replacement, and current node responds exactly once. Include scroll-only chained navigation if supported. | Short indexed trials reusing L/S. Lifecycle/event trace may supplement footage. Do not multiply all themes/widths for these unchanged branches. | UNVERIFIED; iframe/native scrollbar branches HARNESS-ONLY if unavailable |
| H | Nonvisual lifecycle harness | Empty placeholder >20 frames→list; list→null→different node; same-node thread data replacement; unmount removes document handler. Assert cancellation changes state and detached old node does not affect new thread. | New test covers first transition only. Additional harness results must be labelled and cannot replace L/S recordings. | First transition present in source; execution not independently verified here |

Scope collapse: message/markdown/thinking/tool/group/attachment/plan/app rows use the same list node. Representative prose and tool rows cover height growth; nested scroller covers event-target filtering. No per-row-kind or per-provider cross product. New-turn anchor, free-scroll hold, end-follow, hydration placeholder, same-node thread switch, new-node remount, and explicit resume all have assigned phases. Find/citation/minimap manual navigation bypasses this listener attachment and needs no separate pixel fixture. Fullscreen app/disclosure/restoration gates of maintainScrollAtEnd are unchanged; do not force them into this audit. Reduced-motion affects existing smooth-follow only; one L supplemental trial can check immediate mode without a new matrix axis.

Web and Electron use this renderer; web captures do not establish OS/Electron scrollbar behavior. Narrow captures are web, not React Native. Native mobile has no change here. Settings/command palette have no separate attachment entry point. No service, MCP action, scheduled-task, contract or transport change; remote/relay/tunnel deliver the same rows and need no extra visual fixtures. Record the provider actually used; do not claim all providers reproduced the hydration race.

## Deterministic real-app seeding handoff

The capture agent MUST commit a synthetic fixture and runnable seeding/replay script, with no copied private userdata. That is work for the primary/capture agent; this audit is read-only and makes no commit. A prompt asking a model for 100 paragraphs is not a reproducible seed.

Concrete implementation route: extend the existing isolated ACP mock-provider approach in apps/server/scripts/acp-mock-agent.ts with a dedicated synthetic scenario, using agent.client.sessionUpdate and finishPrompt. Its existing agent_message_chunk/tool_call_update protocol provides actual server→projection→WebSocket→ChatView rendering. Use the provider configuration in the isolated environment only. Do not insert static V1 projection rows: scripts/mobile-showcase-environment.ts explicitly seeds V1 thread tables which V2 clients do not read. The existing acp-replay-agent.ts emits inbound batches without a delay/barrier field; an unmodified replay transcript is not sufficient to prove held live streaming.

The committed seeder should generate fixed paragraph strings from indexes (no model generation), fixed message/tool IDs and timestamps, then stream this phase sequence through the configured mock provider:

    L: 100 fixed paragraphs in individually paced chunks
       → barrier before tool-start
       → tool_call pending/in_progress (fixed command/title)
       → barrier before tool-finish
       → tool_call_update completed
       → 20 fixed paragraphs in individually paced chunks
       → finishPrompt(end_turn)
    S: short fixed sentence → compact fixed tool start/finish → short sentence

Expose release barriers to the capture driver so a native gesture and hold checkpoint happen before continuing each stage. Use the same fixture hash and event ordering on B/A. One companion thread with the same long seed enables same-node switching; an empty inactive thread plus delayed thread-detail response enables placeholder→new-list. Hold the real hydration response until a logged >20 animation frames elapse (use an async release barrier, not a guessed sleep). If the authorized driver cannot intercept hydration safely, label real-app delayed mount UNSEEDABLE/UNVERIFIED and retain the browser regression as harness proof only. Never fake loading with CSS or present a component harness as the real app.

Seed through supported isolated project/thread creation and provider turn dispatch; capture script must record actual created IDs and route. Record its exact runnable command and configuration when implemented. No deterministic seeding command for this final scenario exists in the reviewed diff, and this audit has not executed such a script. This is an implementation recipe, not a claim of an available working fixture. Do not restart or reconfigure the primary's existing ports 7647/15687 from this audit. Primary owns how base/head are served and must record both server and Vite revision; current ports alone do not establish revision provenance.

## Matched pixels and recordings

For each pair use identical synthetic fixture hash, phase/event index, initial named row and pixel offset, viewport, DPR (recommend 1), browser version, zoom 100%, explicit theme, font load completion, sidebar/pane geometry and composer state. Freeze Date/time displays to a shared epoch before render; preserve RAF, input and stream delivery, driving stream barriers with an independent deterministic phase schedule. Record timezone, locale, reduced motion and font settings. Freeze-clock configuration must not stop the race being tested.

Produce TWO PIXEL DIFF IMAGES for every matched PNG checkpoint, alongside B and A originals:

- Diff0: abs RGB channel differences with zero threshold; amplify each channel by 8 and clamp at 255. Every nonzero difference remains included. Report changed-pixel count using max channel difference >0.
- DiffT: same absolute differences, retain pixels only when max absolute RGB channel difference >8 on the 0–255 scale; render retained pixels in a conspicuous color. State threshold 8/255 in the artifact metadata and caption; report count. Alpha must be normalized identically before comparison.

No registration, resizing, content alignment, moving masks, or hidden dynamic regions. If unequal initial scroll/layout prevents matching, fix seed/capture and recapture. At a failure checkpoint identical actions may intentionally produce different B/A scroll positions: retain that difference and annotate it as behavior under test. Do not forcibly scroll A to the broken B position. Entry and S checkpoints should show unchanged styling; nonzero pixels need explanation. Pixel diffs alone cannot establish streaming hold/resume: videos must include tool boundaries and additional prose after the explicit resume click.

Artifact manifest per cell: audit head/base full SHA; separately running web/server SHA and any instrumentation/seed commit SHA; fixture file hash; isolated T3 home (sanitized); machine/OS/browser/provider/model; environment/project/thread IDs (sanitized if necessary); ports and origin without auth token; viewport/DPR/theme; clock epoch/phase; initial row+offset; input focus/method; content/viewport measurements; clip timestamp ranges; B/A originals; Diff0 and DiffT paths/URLs+threshold/gain. Do not upload auth cookies, tokens, startup pairing URLs or private transcripts. A seed commit after audited head must identify that only fixture tooling changed, or rerun the code audit if production changes.

## Evidence inventory and acceptance

[Original before clip](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/1a8286217180b71a/repro-main.mp4) and [failed first-fix clip](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/1ed9efb377434305/failed-first-fix.mp4) are historical evidence, not a matched base/final-head pair. No final-head clip, committed deterministic seeder, matched pixel originals, Diff0, DiffT, or metadata bundle was supplied to this audit. Primary's live validation is ongoing separately; its outcomes are not inferred here.

Evidence blockers: actual runtime cause on final head; paragraph hold across tool start/finish and prose; visible pill plus explicit click resume during further streaming; fitting-content non-regression; delayed/replacement lifecycle; matched W×T B/A provenance and two correctly defined pixel diff outputs. Label unavailable combos HARNESS-ONLY or UNVERIFIED with reason; never substitute them silently.

## FRICTION

| Category | Happened | Prevention |
|---|---|---|
| Scope drift | Old matrix followed a removed overflow fallback and composer caller | Re-derive from final four-file diff; no predicate/composer fixture axis |
| Terminology | Old matrix treated Diff0/DiffT as diff-pane states | Pixel outputs explicitly defined with gain 8, thresholds 0 and 8/255; no pane axis |
| Runtime causality | Prior fix still snapped despite measured tall content | Validate event delivery and cancellation after delayed mount before claiming root cause |
| Seeding | Exact final streaming fixture/committed seeder not supplied | Commit synthetic paced provider scenario and real-app seed driver; retain fixture hashes |
| Coverage | New regression proves synthetic wheel cancellation only | Lifecycle/hold/pill/short control remain explicit actual-app acceptance gates |
| Tooling | vp absent from shell PATH, including suggested global paths | Used existing apps/web/node_modules/.bin/vp; 7/7 focused unit tests passed |
| Thread registration | Singular GitCafe URL rejected by app linker | Structured host/repository/number registration succeeded; no forge reads/writes |

Read-only audit preserved tracked checkout; no edits, commits, pushes, PR comments, forge reads, server starts or browser launches. Only this temporary report and its public ghdrop upload were created. Disk checked first: 3.7 GB available, above 2 GB floor. Final disk/status check recorded separately by audit runner.

VERDICT: INSUFFICIENT

This verdict concerns supplied runtime/visual evidence. The revised matrix is the final-diff capture specification; no confirmed code defect was found.
