# Proof audit, pass 1: return navigation after Go to source

PR [#29013](https://git.cafe/pingdotgg/t3code/pull/29013), issue [#29002](https://github.com/pingdotgg/t3code/issues/29002). Reviewed the complete supplied diff from [dd6c05ec31](https://github.com/pingdotgg/t3code/commit/dd6c05ec313c6c870c7a7688e87143da5084031d) to [77041abe04](https://github.com/pingdotgg/t3code/commit/77041abe0468bbb5b91c485bafb5a6df1b26385f). Read-only inspection in `/home/ubuntu/wt/pr-29013`; no application, browser, or tests launched. The local branch is named `pr-29013` and has the requested head. Local `main` has moved, so use the supplied base, not local `main`, for before evidence.

## Findings

1. **Missing proof, not a confirmed implementation defect:** the author's scrollTop table describes the right scenario but supplies no recordings. The new browser test mounts two substitute buttons and checks return hrefs; it never mounts a real timeline or measures scrolling. Pass 1 cannot certify the user-visible fix.
2. There is no changed production JSX component. The production diff adds a string helper and applies it when the shared tracker records either return href. The relevant distinction is modal close versus sidebar Back, with a mobile sheet entry variant. No new card/content/theme matrix is warranted.
3. A real reproduction must first activate a citation, then scroll away while its hash remains. A short thread, leaving the source visible, manually removing the hash before departure, or scrolling the window instead of the timeline cannot prove this bug.
4. The author's baseline table names a different commit. New before recordings should identify the supplied base above and the after recordings the reviewed head. Exact absolute scrollTop values need not match the author's table or match across builds; the timeline maximum can change during virtualized remeasurement.

## Code-derived paths and branches

Source links below are pinned to the reviewed head.

| Changed code / consumer | Complete relevant path inventory | Fixture collapse |
| --- | --- | --- |
| [startMainAppLocationTracking](https://github.com/pingdotgg/t3code/blob/77041abe0468bbb5b91c485bafb5a6df1b26385f/apps/web/src/components/sidebar/mainAppLocation.ts#L24) | Sole production installer: [appInit](https://github.com/pingdotgg/t3code/blob/77041abe0468bbb5b91c485bafb5a6df1b26385f/apps/web/src/appInit.tsx#L207). Records immediately and on `onResolved`. `/settings`, every `/settings/...`, `/usage`, `/pair`, `/connect`, `/connect-agent`, `/guest` skip recording. Every other path records `appModalReturnHref`; utility paths (`/pull-requests`, `/projects/...`) preserve the older `mainAppHref`; other paths update both. | R1–R3 exercise both saved locations; R2 nested Settings exercises skip-on-modal navigation; R3 modal-over-utility exercises distinct saved locations. B1 covers excluded/fallback branches. |
| [withoutAssistantCitationHash](https://github.com/pingdotgg/t3code/blob/77041abe0468bbb5b91c485bafb5a6df1b26385f/apps/web/src/lib/assistantCitationNavigation.ts#L43) | Sole production caller: tracker above. No `#`: unchanged. First fragment starts exactly, case-sensitively, with `assistant-citation=`: remove the entire fragment, retain all preceding path/search, even if payload is empty or malformed. Other fragment: byte-for-byte unchanged, including an occurrence of that text later in the fragment. | R1–R4 matching prefix; R5 absent hash; R6 other hash/search. B2 covers artificial payload/prefix boundaries without extra visual fixtures. |
| [useCloseAppModal / AppRouteModal](https://github.com/pingdotgg/t3code/blob/77041abe0468bbb5b91c485bafb5a6df1b26385f/apps/web/src/components/AppRouteModal.tsx#L15) | Sole production hook consumer: `AppRouteModal`. Production callers are Settings route and UsagePage. Escape, close X, and a genuine backdrop press all close the same Dialog, then call the hook on close completion. Text selection ending on backdrop does not dismiss. Return precedence is modal return, main return, then `/`. Settings/Usage retain the background ChatView via [_chat](https://github.com/pingdotgg/t3code/blob/77041abe0468bbb5b91c485bafb5a6df1b26385f/apps/web/src/routes/_chat.tsx#L18). | R1 Escape; R2 X and backdrop segments; R3 modal opened over a utility page; R4 narrow full-screen modal. No need to multiply every Settings section or Usage data state. |
| [useNavigateToMainApp / SidebarUtilityMenu](https://github.com/pingdotgg/t3code/blob/77041abe0468bbb5b91c485bafb5a6df1b26385f/apps/web/src/components/sidebar/SidebarChrome.tsx#L221) | Sole production hook consumer: utility-menu Back. Utility route shows Back instead of Settings/Usage/PR icons. Normal menu shows PR only when an environment has server config. Mobile handlers close the sheet before navigating. Back returns saved main href or `/`. | R3 desktop, R4 sheet. PR list loading, empty, populated, and error content do not select different tracker/Back code; choose an empty or populated real page without requiring forge mutations. |
| Other entries and exits | Sidebar buttons, palette Open Settings/Open usage/Open pull requests, `usage.open` shortcut, desktop `open-settings` menu, direct Settings-section links all navigate into the same paths. [Project settings](https://github.com/pingdotgg/t3code/blob/77041abe0468bbb5b91c485bafb5a6df1b26385f/apps/web/src/routes/projects.%24projectKey.tsx#L3) from sidebar/thread actions redirects `/projects/$projectKey` to `/settings/projects`; it has no independent rendered utility page. PR Escape and `navigation.back` call browser history, not these saved-return hooks. | R2 includes palette/nested/project-settings variants; R3 includes palette Usage while PR page is open. Browser-history exits are explicitly out of this fix's claim, not substitutes for sidebar Back. |
| Citation entry | [AssistantCitationChip](https://github.com/pingdotgg/t3code/blob/77041abe0468bbb5b91c485bafb5a6df1b26385f/apps/web/src/components/chat/AssistantCitationChip.tsx#L36) callers: ChatMarkdown (sent quote, popover → Go to source) and ComposerPromptEditorTiptap (composer quote link, optional comment editor). Ordinary click activates the same navigation helper; modified click uses the href. ChatView parses the location, checks environment/thread identity, and passes citation requests through history loading and timeline reveal. | Use a sent quote for R1–R4. Composer/comments, long/short quote labels, loading/missing/changed source, pagination, and streaming are unchanged antecedent features; choose one short valid quote in loaded, settled history. R7 checks reactivation/direct load. |
| New test component | `ReturnControls` has no props; one test render calls both hooks. Static Close modal and Back to app buttons cover Usage and PR respectively. | Labelled router harness evidence only, not a real-app capture. It proves neither the real modal/sheet interaction nor timeline retention. |

No changed prop, count, name length, empty/error/loading visual, theme style, or animation is introduced. Keep a nonempty, long, settled thread because scroll distance matters; keep overlays/panels and viewport fixed within each run. The desktop sidebar's collapsed/expanded states do not change the tracker; use expanded. The narrow sidebar branch is real and merits R4.

## Real-app seed recipe (capturer executes; not executed in this audit)

1. The lead/capturer owns dev-server startup. Reuse a healthy isolated server or start `T3CODE_DEV_SEED=0 vp run dev` from the checkout, with fresh worktree-local `.t3` state. The variable prevents copying live data; it does not erase an existing dev DB. Never use `~/.t3/userdata`, set Vite origin variables, or reset an existing DB just for this audit. Keep raw output, drivers, and captures under a new timestamped directory inside `/tmp/lead-29013-r1/`.
2. Pair through the startup URL privately, finish onboarding, and add the checkout as a project in this isolated app. Do not put pairing URLs/tokens in evidence. The [Replay dev fixture instructions](https://github.com/pingdotgg/t3code/blob/77041abe0468bbb5b91c485bafb5a6df1b26385f/.agents/skills/test-t3-app/SKILL.md#L25) and [DevReplayHost](https://github.com/pingdotgg/t3code/blob/77041abe0468bbb5b91c485bafb5a6df1b26385f/apps/web/src/components/DevReplayHost.tsx#L20) supply a free provider.
3. In the app, use the Replay picker (`Ctrl+Alt+Shift+R`) or `await window.t3dev.replay("showcase")`. This creates **one new thread**; do not call it repeatedly to grow that thread. Resolve the recorded approval if shown. Each reply is a replay, not a paid provider call.
4. In that same thread, select a short, distinctive piece of its first assistant reply. Use **Cite** (`Cite selection in composer`), add a short comment if needed, and send. This persists a genuine quote chip in a user message. Continue sending ordinary follow-up prompts with Replay (dev) selected until there are about 20 completed turns and the source is at least a viewport above the end. Resolve Replay approvals as needed; finish all turns before recording. The precise count is not an assertion.
5. Optional free CLI growth, against the same dev state only: `node apps/server/src/bin.ts thread send --base-dir .t3 --wait <thread-id> "fixture continuation"`. Starting a replay thread instead is supported by `node apps/server/src/bin.ts thread start --base-dir .t3 --provider devReplay --model showcase <project-path-or-id> "go"`. Use the UI Cite/send step to create the quote, not a fake rendered chip. Await completion/approval rather than relying on sleeps.
6. Route is `/<environmentId>/<threadId>`. At the end, click the **sent** quote chip, then **Go to source**. Verify source reveal and the `#assistant-citation=` fragment. Scroll the timeline to its end using normal scroll or its jump-to-end control. Verify the citation hash is **still present** before opening any utility/modal. Repeat this activation and scroll-away setup for each affected cycle; a preceding successful return has removed the hash.

All required rows are seedable through this real app. No SQL fixture, customer data, paid model, actual PR, or PR write operation is required. A connected environment provides the PR-menu capability; empty/no-auth/error PR results can still exercise sidebar Back. If that menu is absent, fix pairing/capability setup or use the real `/pull-requests` route and disclose direct-route entry; do not present the test's substitute buttons as app evidence.

## Fixture and capture matrix

**Minimum:** R1–R3 each before **and** after at **1280×800, dark**; R4 after at **390×844, dark**; R5–R6 after at **1280×800, dark**. This is nine recording segments and may be packaged into fewer clearly chaptered videos. R7 reactivation is appended to R1, with no extra video needed. One theme is sufficient: no theme-sensitive code changed. Choosing light throughout instead is equivalent; keep the choice identical for before/after. Do not require a light × dark cross-product.

| ID / named fixture | Data and real-app seed / interaction | Diff effect and required assertions | Capture / status |
| --- | --- | --- | --- |
| R1 — desktop Usage, cited source then end | Long Replay thread above. Sent quote → Go to source → end → sidebar Usage → Escape. At head, append another Go to source after returning. | **Affected:** before returns with citation hash and jumps toward source; head returns to same thread/search without citation hash and remains at end. The appended explicit activation must still reveal source and restore the hash. | **recording; REQUIRED, MISSING**. Base + head, 1280×800, dark. Includes R7 repeat activation. |
| R2 — desktop Settings, nested sections and dismissal | Same activation/end setup → sidebar Settings (`/settings` redirects to general) → another Settings section → close X. Head-only short additional cycles: palette Open Settings → backdrop dismiss; project sidebar/thread action Project settings → close. | **Affected:** every modal section preserves original thread return; head has no citation hash and remains at end. Show the real close completion. Project entry lands at `/settings/projects`, not a fictitious utility screen. | **recording; REQUIRED, MISSING**. Base + head primary X cycle, 1280×800, dark. Additional entry/dismissal segments can share head video. |
| R3 — desktop Pull requests, sidebar Back | Same activation/end setup → sidebar Pull requests → **sidebar Back**. Head-only second cycle: while on PR, palette Open usage → close → verify return to PR → sidebar Back to thread. | **Affected:** utility updates modal-return but must retain main-thread return. Head thread return has no citation hash and is at end; closing the nested Usage returns to PR with its filters/search, not straight to thread. Before direct PR Back replays citation. PR fetch success is not required. | **recording; REQUIRED, MISSING**. Base + head primary cycle, 1280×800, dark. Additional modal-over-utility segment in head video. |
| R4 — 390-wide sidebar sheet | Resize same settled real thread to 390×844 **before** setup; recompute max. Cycle A: quote → source → end → open sidebar sheet → Usage → X close. Cycle B: reactivate quote/end → sheet → Pull requests → reopen sidebar sheet → Back. | **Affected:** same normalized hrefs, different actual controls. Both cycles close the sheet and return without citation hash, at the recalculated end. Settings shares the full-screen modal/close path already covered by R2. | **recording; REQUIRED, MISSING**. Head only, 390×844, dark. No desktop/mobile visual equivalence assumption. |
| R5 — no-hash control at end | Same long thread at end, ordinary thread URL with no hash. Usage → close; then PR → sidebar Back. No source activation in these cycles. | **Unaffected branch:** retains original path/search, no new hash, stays at end. Guards against a general return/scroll regression; no need for a separate empty-thread fixture. | **recording; REQUIRED, MISSING**. Head only, 1280×800, dark. |
| R6 — unrelated fragment and search control | Load real thread URL `/<env>/<thread>?proof=return#proof-anchor`; allow load, scroll to end. Settings → close; then PR → sidebar Back. Record the starting href exactly. | **Unaffected branch:** return preserves thread path, `proof=return` search, and `#proof-anchor` after both hooks, and stays at end. An unknown non-citation fragment is intentionally not removed. Do not use `history.replaceState` alone without notifying/reloading the router. | **recording; REQUIRED, MISSING**. Head only, 1280×800, dark. |
| R7 — intentional citation activation / shared link / reload | Repeat activation is R1's final segment. Optional additional segment: copy the real citation href, open it directly in paired app, then reload. | **Unchanged navigation:** helper only sanitizes recorded returns, not the current citation URL. Explicit activation still reveals/highlights source; direct load/reload should still resolve citation. | **recording; repeat activation MISSING via R1**. Direct-link/reload segment supplemental, not an additional minimum capture. |
| B1 — skip/fallback and root routes | Code branches: modal routes and shell-less paths do not replace saved locations; regular root/draft routes record normally; null saved values fall back to `/`. Existing new test uses a synthetic route and two buttons. | No new UI shape. R2 covers modal skip; R3 covers utility precedence. Shell-less auth/guest and null initialization are outside this reproduction and unchanged. | **CODE-MAPPED; no additional capture required**. If exercised in isolation, label it **router harness**, not app proof. |
| B2 — artificial hash boundary inputs | Helper inputs: absent hash; empty `#`; ordinary hash; exact leading prefix with valid, empty, or malformed payload; case mismatch; prefix only later (`#proof-anchor&assistant-citation=x`). Include a search before `#` in each relevant case. | Only exact leading lowercase prefix strips. Real valid, absent, and other hash covered by R1–R6; artificial strings have no additional layout. | **CODE-MAPPED; no visual capture required**. A supplemental direct helper/router check is labelled **harness**; existing test does not claim these cases. |
| P1 — Electron desktop | Same replay seed, same R1/R3 interactions, but real desktop shell, whose [appInit](https://github.com/pingdotgg/t3code/blob/77041abe0468bbb5b91c485bafb5a6df1b26385f/apps/web/src/appInit.tsx#L172) selects hash history. Include internal route/hash in diagnostic readout. | Shared tracker; distinct history adapter. No Electron execution was performed. Web recordings alone must not be labelled desktop verification. | **recording; supplemental, NOT VERIFIED**. Add if claiming desktop runtime coverage; unavailable desktop is a disclosed limit, not a reason to stage fake app proof. |
| P2 — native mobile, providers, transports | Native mobile does not import this web tracker. Provider choice supplies messages; transport does not own the browser return-href logic. | No mobile-native, provider-adapter, RPC/contract, or server change. 390-wide web is **not** React Native evidence. | **NOT APPLICABLE** to this diff; no native-device, paid-provider, local/relay/tunnel cross-product required. |

## What each recording must show or narrate

- Burn in **Before / After**, reviewed short commit, row ID, viewport, and theme. Show the real UI actions continuously from quote activation through final return; stills cannot prove the absence of a jump.
- Overlay or narrate at each milestone: sanitized route pathname/search; `citationHash=true/false` (or harmless fragment text); actual timeline **scrollTop**, **max = scrollHeight − clientHeight**, and **distanceToEnd = max − scrollTop**. Keep the same thread identity visible or in the readout. Never expose pairing credentials, personal conversations, or other URL secrets.
- Measure the **timeline scroller**, not `document.scrollingElement`, modal/PR content, or sidebar. `.messages-timeline-scroll` identifies the LegendList area; confirm the measured element is the actual scrollable node (including a descendant if necessary) and its scrollTop changes when the timeline is scrolled. If unavailable on a utility page, label metrics `timeline unmounted` rather than reusing a stale last value. Settings/Usage keep the thread mounted underneath.
- Required checkpoints: (1) source revealed, hash present, source visibly off the end; (2) scrolled away/end, hash **still present**, `distanceToEnd` approximately zero; (3) utility/modal visibly opened; (4) return complete with path/search correct, no citation hash at head, and end retained. For R5/R6 substitute their control fragment expectations.
- At end use an operational pixel tolerance such as 2 px once geometry settles; if the list changes max during measurement, narrate the contemporaneous values and verify the final end marker stays visible. Do not demand the same absolute scrollTop across before/after. Source reveal need not be scrollTop exactly zero: require the target source visible and clearly separated from the end.
- Keep recording through modal close completion and any timeline restoration/highlight so a delayed jump is observable. Source highlight has a brief animation; R7 is also a recording. Do not replace a failed assertion with a later corrective scroll.
- For an interior-position variant, compare a visible row/text anchor and offset, not only raw scrollTop. It uses unchanged retention machinery and is supplemental; end-position rows are the minimum reproduction the PR claims.

The author's reported numbers are a useful reproduction guide only. No recordings or runtime results have been independently checked in this pass. No tests were run because this assignment is fixture planning before capture, not verification execution.

FRICTION: none

VERDICT: INSUFFICIENT R1–R6 recordings, including R7 repeat-activation segment within R1
