# Proof audit, pass 1 — fixture matrix

PR [#33160](https://git.cafe/pingdotgg/t3code/pull/33160), fixing [#33137](https://github.com/pingdotgg/t3code/issues/33137). Reviewed branch `fix/issue-33137`, head [7697564](https://git.cafe/pingdotgg/t3code/commit/76975641d3c795ad87c32d814243e7724dab95d8), against [4c36119](https://git.cafe/pingdotgg/t3code/commit/4c361199f47f6ea32a2c14835df392640d0f7c63). Forge metadata confirms both revisions; the checkout's local `main` is different and was not used as the comparison base. Read-only audit in `/home/user/wt/pr-33160`. No capture exists or was made in this pass.

## Findings first

1. **Proof is missing, not contradicted.** The PR body names planned rows and explicitly says they were not captured. There are no linked captures. None of the visually observable routing claims is yet proven in a real client at this head. The original report is Windows desktop; a web pass proves shared web behavior, not a Windows Electron run.
2. **Capture the actual hook and both chip entry points.** The focused tests pass (2 files, 9 tests). They verify URL/store behavior and modal-return tracking. The new tests call `revealSideChatCitation` directly through a helper; they do not mount `useSideChatCitationReveal` in `ChatView`, click the real chip, reveal the embedded timeline, or show the highlight. A screenshot of the final parent URL cannot establish those steps.
3. **This is interaction proof.** Every required row below is a recording. Show the source quote, activation, visible route and panel result, plus the quote highlight/pulse and its settlement where applicable. Stills alone do not prove scroll, retained maximization, late loading, repeat activation, close-respect, or browser history. The existing pulse lasts about 3 seconds; keep enough of the recording to see it settle. Export before/after keyframes as helpful companion images, not replacements for recordings.
4. **Replay can drive side chats.** Code inspection supports using the free `Replay (dev)` provider: `useSideChat` calls the generic `forkSideChat` command, then `startTurn` with the selected model. Side-chat creation is not gated on native provider fork support. Replay is an ACP provider and can produce the replies selected and cited here. `window.t3dev.replay("showcase")` creates a new ordinary parent thread; use `/side` in that parent to create the side chat. Calling `t3dev.replay` again does not target the existing side chat. Replay returns recorded replies, not arbitrary text requested in a prompt. This audit did not execute a Replay turn.

## Source inventory and render paths

Links below are pinned to the reviewed head. Only two production React components change; the navigation module supplies the routing/effect behavior. The other two changed files are tests.

### AssistantCitationChip

[AssistantCitationChip.tsx](https://git.cafe/pingdotgg/t3code/blob/76975641d3c795ad87c32d814243e7724dab95d8/apps/web/src/components/chat/AssistantCitationChip.tsx#L38)

Every direct caller:

- [ChatMarkdown.tsx](https://git.cafe/pingdotgg/t3code/blob/76975641d3c795ad87c32d814243e7724dab95d8/apps/web/src/components/ChatMarkdown.tsx#L1084): every valid `t3-citation://v1/...` Markdown anchor becomes the default `composer=false` chip. This is not restricted to sent user messages. Invalid citation links do not create this component.
- [ComposerPromptEditorTiptap.tsx](https://git.cafe/pingdotgg/t3code/blob/76975641d3c795ad87c32d814243e7724dab95d8/apps/web/src/components/ComposerPromptEditorTiptap.tsx#L513): citation node view passes `composer=true` and a comment editor, including optional source anchor, cancellation, save, send and focus callbacks. The editor is mounted in `ChatComposer` and the Appearance prompt-font preview. The font preview's default text has no citation; manually pasting one reaches the same node view.
- `AssistantCitationChip.browser.test.tsx`: harness-only composer/comment editor path. Its router Link/navigation are mocked, so it supplies no integrated routing proof.

All production `ChatMarkdown` host files, collapsed into the same sent-chip render path (no host-specific branch is added by this PR):

- Timeline/user/assistant/work: `TimelineUserMessageRow`, `TimelineAssistantRows`, `TimelineWorkEntryRow` (expanded details and inline reasoning preview), `TimelineWorkGroupRows` (thought/status previews).
- Other chat text: `ComposerPendingUserInputPanel`, `ProposedPlanCard` (collapsed preview and full plan), `V2ItemInspector` (reasoning), `WorkflowOpeningBlock`, `UserMessageContextChips` (review comment text and fenced diff), `composerContextPresentation` (review comment text).
- Non-timeline Markdown: `pullRequest/PullRequestMarkdown`, `files/FileMarkdownPreview`, `files/NotebookPreview` (Markdown cells), `files/AttachmentFilePreview` (Markdown attachments).

These host files live under [web components](https://git.cafe/pingdotgg/t3code/tree/76975641d3c795ad87c32d814243e7724dab95d8/apps/web/src/components). All use the same `ChatMarkdown` anchor renderer, which intercepts citations before ordinary file/external links. R01/R02 cover the ordinary host; R19 covers a different host that competes for the right panel. Duplicating identical chip rendering in every host is not part of the minimum set. A host that strips links or has no citation data never renders a chip.

Data/conditional inventory:

| Input/branch | Visible consequence | Fixture coverage |
| --- | --- | --- |
| `composer=false`; popover closed/open | Button-like label; opened detail contains blockquote, optional comment and Go to source | R01/R02 |
| `composer=true` | Direct label link, not a Go to source popover; pencil exists only with `commentEditor` | R03; C02 for uncommon editor variants |
| Source shell has `lineage.sideChat=true` and parent ID | Link href and plain-click destination use parent; citation hash still identifies side chat | R01–R14, R19 |
| Shell null, ordinary lineage, or fork with sideChat false | Link initially uses source thread ID; late side-chat shell may repair it | R09/R15/R16 |
| Plain primary click vs button nonzero or Meta/Ctrl/Shift/Alt | Primary click gets fresh activation; native link behavior uses actual parent href | R02/R07 |
| Comment absent/empty/whitespace vs nonempty | Label uses trimmed comment when nonempty, otherwise quote; sent popover shows a paragraph for any truthy comment | R01 no comment; R02 comment; C01 boundaries |
| Label length <=64 vs >64 UTF-16 units | Long label gets ellipsis; surrogate pair is not split; composer also has 16em label cap | R01 short; R02 long; C01 |
| Short vs multiline/long quote; short vs long comment | Whitespace preserved; blockquote/comment wrap; detail scrolls at max height 16rem, fades depend on top/middle/bottom | C01 (existing appearance, not changed by diff) |
| LTR vs RTL text/comment | Label, blockquote and comment choose direction independently | C01 |
| Editor closed/open, voice setup false/true | Pencil popup shown only when open and voice setup closed; voice setup can replace it | C02 |
| Live source anchor, no anchor, source becomes unavailable | Editor anchored below source vs above chip; source observer may close or retain draft | C02 |
| Saved/unsaved/blank/over-limit or rejected comment draft | Close may commit, cancel, or stay open; optional Save and send/cancel/focus callbacks | C02; existing browser tests, outside routing delta |
| Empty/all-whitespace quote | Invalid schema, no valid citation chip fixture | C03 |

No new component loading spinner, skeleton, empty text, error style, count threshold, theme branch, or platform branch is introduced. Loading matters through source-shell availability and target history, enumerated separately below.

### ChatView

[ChatView.tsx](https://git.cafe/pingdotgg/t3code/blob/76975641d3c795ad87c32d814243e7724dab95d8/apps/web/src/components/ChatView.tsx#L565)

Every direct production caller:

- [ThreadRouteView.tsx](https://git.cafe/pingdotgg/t3code/blob/76975641d3c795ad87c32d814243e7724dab95d8/apps/web/src/components/ThreadRouteView.tsx#L149), three render sites: existing draft (`routeKind=draft`, keyed, force-expanded mobile composer), promoted draft redirect (`routeKind=server`, same key), ordinary server (`routeKind=server`, shared instance). `_chat` mounts it normally or inert/background under an app modal. Missing/hidden/off-shell-blocked routes can render no ChatView or redirect; those pre-existing gates are outside this diff.
- [SideChatPane.tsx](https://git.cafe/pingdotgg/t3code/blob/76975641d3c795ad87c32d814243e7724dab95d8/apps/web/src/components/chat/SideChatPane.tsx#L172): server ChatView with `embedded=parentPanel`, under BackgroundThreadContext. Parent owns its panel and shell actions. Pane has no ChatView before its shell arrives, and displays `This side chat was deleted.` when the shell is absent after the snapshot loads.

Relevant branch inventory:

| Branch | Expected observable result | Fixture coverage |
| --- | --- | --- |
| No parsed citation / malformed hash | No request or automatic side-chat opening | R17 |
| Citation environment and thread match this ChatView | It requests cited history and timeline reveal | R01–R16 |
| Parent ChatView sees a citation for its side chat | Parent itself gets no citationRequest; page hook opens side-chat tab, whose embedded ChatView matches and reveals | R01–R14/R19 |
| Embedded view | Passes null route ref to new hook, avoiding parent-routing work from the child; it still handles its own matching citation | R01/R03/R04 |
| Source shell absent, then present | Initial fallback route; later repair uses replace, preserves hash and activation | R09 |
| Parent route vs side-chat route | Open tab without navigation vs open tab and replace route | R01/R08 vs R09 |
| Citation env mismatch, unrelated route, stale citation for another side chat | Do nothing; do not steal navigation | C04 |
| Same href, fresh activation | Reveal again; state UUID prevents navigation deduplication | R02 |
| User closes/tab-switches after reveal without new activation | Respect dismissal; next explicit activation reopens/reactivates | R02/R04 |
| Panel absent/closed, existing target tab, different active surface, maximized | Open/activate/upsert tab, keep other tabs, retain maximization | R01/R02/R04/R08/R19 |
| Width >980px vs <=980px, with maximized override | Inline split vs right sheet; maximized panel uses inline full width at either width | R01/R02/R05 |
| Web width below small breakpoint | Mobile web composer/header arrangement inside sheet, same routing | R06 |
| Light/dark, optional reduced-motion | Existing tokens/highlight; reduced-motion changes pulse behavior | R01 dark/R02 light; C05 |
| History loading/found/failed; message off-page or folded | Load/expand then reveal; failure warns instead | R10–R14 |
| Source exists but quoted range no longer resolves | Reveal response and warn `The quoted text has changed` | R13 |
| Target missing, wrong message role, fetch failure or page limit | Different existing warning texts; no successful highlight | R12/R14, C06 |
| Main ChatView has no active thread | Existing NoActiveThreadState; embedded returns null | C03; not a reachable valid-quote source fixture |
| Server thread, draft, promoted draft, background modal | Normal source destination is persisted server thread; draft caller has no new special route branch; modal return drops quote hash | R20; C03 |

The remaining ChatView branches (provider banners, terminal, git diffs, approvals, media, etc.) neither consume the changed citation behavior nor change layout because of this diff; they are not multiplied into this matrix. No prop other than embedded/route identity participates in the new hook. Optional `onDiffPanelOpen`, `reserveTitleBarControlInset` and `forceExpandedMobileComposer` retain existing layout behavior.

### Navigation module and downstream reveal

[assistantCitationNavigation.ts](https://git.cafe/pingdotgg/t3code/blob/76975641d3c795ad87c32d814243e7724dab95d8/apps/web/src/lib/assistantCitationNavigation.ts#L59) has no visual component. Its production navigation/route caller is AssistantCitationChip; its reveal-hook caller is ChatView. `withoutAssistantCitationHash` remains used by sidebar main-app return tracking. Tests are the only other callers.

`revealSideChatCitation` guards null citation, different side-chat ID, different environment, and route other than parent/source. It opens the target as an active side-chat surface, then returns on parent or replaces source route with parent, preserving hash/activation. The hook waits for non-null source lineage and runs by router/env/thread/source/parent/activation dependencies, not panel-store changes. The public contract has no new fields and no server/provider adapter changed.

The embedded target runs existing `useCitedHistory` -> `MessagesTimeline` -> `AssistantCitationSource`: loading/paging, expanding a folded source, virtual-row reveal, scroll, highlight pulse/fade; failures display warning toasts. Source range can be plain prose, code or table; side scrolling is existing behavior (C01). Pointer/keyboard/wheel/touch navigation can dismiss a pending reveal, and Escape clears it (R02). These are the reason recordings are required.

## Common real-app seed S0

The capturer (lead, once) should reuse its isolated healthy dev server, or start `T3CODE_DEV_SEED=0 vp run dev` and retain its PID/session. Use only that worktree's `.t3` state, never live userdata. Pair via the Browser panel and the private startup link; do not upload auth links or tokens. No server was started by this audit.

1. Add a scratch git project rooted inside the capturer's assigned scratch directory using the app. This provides project P without developer conversations; R18's Markdown file can live in this scratch project. In the authenticated web dev client call `window.t3dev.replay("showcase")`, or select Replay (dev)/Showcase and send a parent message. Finish/resolve the scenario's requested approval if needed. The parent is T in environment E.
2. In T send `/side go` with Replay (dev) selected. `useSideChat` forks with sourcePoint latest_stable and sideChat true, then dispatches the first side-chat message with the composer's model selection. Keep the completed reply; closing an unused side chat deletes it. Name this used side chat S1.
3. Select a unique short sentence in S1's assistant reply M1 and Cite it. This yields a valid citation C carrying E/S1/M1, exact text, UTF-16 offsets, prefix and suffix. Send it to obtain a sent quote. Keep a copy of serialized citation Markdown for reuse in T or an unrelated thread. Create S2 similarly for multiple-tab checks.
4. Source route is `/<E>/<S1>`. Fixed target is `/<E>/<T>#assistant-citation=<base64url payload>`. Copy the href from the actual source link. The hash still contains S1, not T. For R17 remove the hash explicitly. Do not confuse Go to source's web href with the serialized `t3-citation:` Markdown href.
5. Choose light/dark in Appearance settings; use a default theme with matching mode. Use CSS viewport sizes listed below at normal zoom. Keep sidebar visibility, panel width and synthetic data stable for before/after. Standard panel animations stay enabled; R01/R02 should show them.

RPC alternative for a driver: use the existing scoped `threadEnvironment.forkSideChat` command `{environmentId:E,input:{sourceThreadId:T,targetThreadId:S,title:"Side chat"}}`, then scoped `threadEnvironment.startTurn` with Replay selection `{instanceId:"devReplay",model:"showcase"}`. The underlying `ORCHESTRATION_V2_WS_METHODS.dispatchCommand` command is `thread.fork`, `createdBy:"user"`, `sourcePoint:{type:"latest_stable"}`, `sideChat:true`, unique command/target IDs. Use normal app command permission/auth plumbing. Do not invent a global `window.t3dev` RPC method; its exposed helpers only start a new Replay thread/list scenarios. A driver can import existing client commands through dev modules or use the authenticated typed RPC client. Wait for persisted completion/visible readiness, not arbitrary sleeps. This pass produced no driver.

For synthetic but valid stale quotes, reuse C and alter only messageId or quoted text/selectors, then serialize with `serializeAssistantCitation` from `packages/shared/src/assistantCitations.ts`. Paste the serialized Markdown into a real composer and send via Replay, or use an actual generated web citation hash. This exercises the real app with a synthetic citation, not a mocked component. No database writes are necessary for missing-message or mismatched-text rows.

## Required capture matrix

All rows currently have **MISSING** status. `B+A` means matched recordings at the supplied base and reviewed head. `A` means reviewed-head recording; one recording may cover several rows if each is clearly labelled and visible. Default viewport D = 1440×900 CSS pixels. Every row is **recording**, including controls. Caption harness evidence explicitly; it is not integrated-client proof. Final stable screenshots may be extracted for comparison.

| ID / named fixture | Data and starting state | Diff affects | Minimum viewport × theme × revision | Seed / steps and acceptance | Type / current status |
| --- | --- | --- | --- | --- | --- |
| R01 — sent quote, maximized side chat | S0, C sent in S1, short label, no comment, target above current scroll; S1 active/maximized | Yes, reported failure | D × dark × B+A | Maximize via panel control; scroll to sent quote; open popover, Go to source. After: route stays T, S1 tab stays active/maximized, quote M1 is revealed/highlighted. Before must show S1 taking over main route. | recording / MISSING |
| R02 — split, repeat, dismiss and reopen | S0, unmaximized S1, a second quote with >64-char comment-label, source offscreen | Yes, activation/reopen | D × light × B+A | Open sent popover then source; repeat identical citation; dismiss highlight with Escape; close S1 tab and interact with parent (no spontaneous reopen); activate same C from T to reopen. Record parent + side panel and URL. Opening a quote preview may scroll a long blockquote before clicking. | recording / MISSING |
| R03 — unsent composer citation | S0, C remains unsent in S1 composer; comment editor closed | Yes, second real caller | D × dark × A | Select/Cite but do not send; click the quote label itself. T remains main, S1 stays embedded and reveals M1; draft survives navigation. Also exercise keyboard focus + Enter on the link. | recording / MISSING |
| R04 — competing tabs and another side chat | S1 and S2 used; S2 active, S1 tab inactive; C from S1 pasted/sent in S2 | Yes, target selection/upsert | D × dark × A | Go to source from embedded S2. Activate S1 exactly once without duplicating it; preserve S2 and any file tab; route T. Switch away afterward and verify same activation does not immediately steal focus back. | recording / MISSING |
| R05 — narrow web sheet | S0, S1 unmaximized, source offscreen | Yes, different panel presentation | 800×900 × light × A | Same sent-chip activation as R02. Sheet opens/reveals source; dismiss sheet and reactivate. Maximize at this width and repeat to cover maximization overriding the <=980px sheet path. | recording / MISSING |
| R06 — small mobile web layout | S0, S1 unmaximized | Yes, responsive web ChatView | 390×844 × dark × A | Touch/click-equivalent sent quote -> Go to source in sheet. Header, close action, composer and source remain usable; route T; no source is hidden behind the sheet/composer. This is mobile web, not native React Native. | recording / MISSING |
| R07 — native link navigation | S0, real sent Go to source link and composer label href available | Yes, actual href is changed | D × dark × A | Ctrl-click on Linux/Windows or Cmd-click on macOS, plus middle-click, open parent URL in new tab; select target tab and show S1/M1 reveal. Exercise Shift-click/new-window where supported; verify Alt-modified action follows browser semantics rather than invoking the plain-click handler. Native modified-click actions can share footage; one new-tab arrival cannot establish all input actions. | recording / MISSING |
| R08 — cold arrival, reload and history | S0, copy fixed parent+C URL, close tab/panel beforehand | Yes, landing hook | D × dark × A | Open copied URL in an authenticated fresh tab; record S1 opening. Reload on the same URL; navigate away then Back/Forward. Each arrival reveals S1 on T; no route to S1 as main. Use an isolated/new tab with no pre-open target tab to prove opening, not merely persistence. | recording / MISSING |
| R09 — late source shell | Valid C known, source lineage initially unavailable, then S1 shell arrives | Yes, fallback+replace | D × dark × A | Prefer a deterministic labelled harness: real router and reveal hook, source-shell atom initially null then publish S1 lineage. Start navigation with unresolved source, show S1 route -> T via replace, same hash/activation, one history entry, tab active. There is no app setting/RPC to unload just one shell; a cold boot/throttle is insufficient unless logs prove the null->known sequence. See harness note below. | recording / MISSING — labelled harness required unless real sequence proved |
| R10 — source outside loaded history | S1 has enough completed Replay turns that M1 is outside initial page; current viewport at newest sent C | Yes, target now in embedded timeline | D × dark × A | Seed turns through normal Replay app/RPC sends until real history cursor indicates more; then reload and activate C. Record loading/paging -> M1 scroll/highlight in S1 on T. Do not choose a random turn count without checking pagination. | recording / MISSING |
| R11 — source inside folded older turns | S1 loaded source M1 belongs to a folded earlier timeline group | Indirect, unchanged reveal pipeline now hosted in panel | D × dark × A | In real app fold the source group, activate saved C. Record group expanding, virtual row becoming visible, scroll and highlight within S1. If that UI cannot produce this state for chosen Replay transcript, use a labelled timeline harness instead of claiming a real-app pass. | recording / MISSING |
| R12 — missing source message | Existing S1 shell; valid synthetic C names a nonexistent message ID, original saved quote text retained | Yes, error on new destination | D × light × A | Seed via serialized synthetic citation in real T. Activate; parent stays T, S1 opens, toast `The cited response is unavailable`; quote remains saved, no false highlight or endless positioning. This is not the same as deleting the entire side chat. | recording / MISSING |
| R13 — changed quoted text | Existing S1/M1; synthetic stale C has text/range/context that cannot match M1 | Indirect fallback | D × dark × A | Paste/send synthetic citation in T; activate. Reveal the actual M1 response in S1 and show `The quoted text has changed`; saved C unchanged. Do not merely choose different offsets if resolver can still recover text from context. | recording / MISSING |
| R14 — history request failure and recovery | R10 history state, source shell known; loadEarlierHistory fails once | Indirect failure in embedded target | D × light × A | No normal setting creates this failure. Use an authenticated app driver that fails only target history request, or a labelled CitedHistory/ChatView harness. Record warning `Could not load the cited response`, usable panel, restore request and explicitly reactivate C to recover. Do not take environment fully offline and claim this isolated failure. | recording / MISSING — injection/harness |
| R15 — ordinary thread quote | Ordinary source U, no sideChat lineage, C_U sent elsewhere | Changed resolver control | D × dark × A | Create Replay ordinary U, cite its reply, paste/send in T, activate. Navigate to U itself and reveal source, without adding a side-chat tab. | recording / MISSING |
| R16 — ordinary fork quote | Fork F has parent T but sideChat false; quote from an assistant reply owned by F | Changed resolver control | D × light × A | Fork completed parent run through real UI, send Replay turn in F; cite F-owned reply and activate it from T. Route remains F, not T. Inherited parent citations are not a substitute for an F-owned source. | recording / MISSING |
| R17 — side-chat route without citation | Existing S1, direct `/<E>/<S1>` without hash; repeat with malformed citation hash | New hook negative control | D × dark × A | Navigate explicitly, not via related-thread opener which already embeds S1. No automatic parent redirect/tab reveal. Unquoted side-chat route remains as before. Invalid hash uses this same no-citation visual state. | recording / MISSING |
| R18 — citation in non-timeline Markdown | Valid C rendered in a local Markdown file in T's file preview; S1 closed | Yes, another host of changed chip | D × dark × A | Driver writes a scratch `.md` under capturer's allowed scratch directory and opens it through app Files; render C, Go to source. T remains main; file tab is preserved and S1 becomes active. No forge comment or PR-body edits are needed. | recording / MISSING |
| R19 — another environment destination | C belongs to S1/T in E2, holder is ordinary thread in E1; IDs chosen distinct | Yes, scoped shell lookup and route gate | D × dark × A | Use two isolated environments only if already available/authorized to seed; paste valid E2 citation in E1 and activate. E2/T owns revealed S1; E1 panel unchanged. If unavailable, labelled harness with two environment atoms/router refs; record exact scope. No new real remote credentials or paid provider required. | recording / MISSING — real app or labelled harness |
| R20 — modal return does not replay quote | S0, source revealed, side chat subsequently closed, app Settings/Usage opened | Control touched test caller/background view | D × dark × A | Open/close Usage/Settings and return using actual app controls; also Back to app from pull requests if available. Main return route is T without citation hash; closed side chat remains closed. Existing focused browser test covers location stripping but not real panels. | recording / MISSING |

### Characterization/guard rows, not additional required captures

These enumerate unchanged render shapes and negative paths without multiplying the full application UI into a routing-fix proof set. If a capturer uses one, label it; they cannot substitute for the required rows. All have status **NOT CAPTURED / scoped out of minimum** unless noted.

| ID | Named fixture / data | Source and seeding | Diff relationship / evidence type / status |
| --- | --- | --- | --- |
| C01 | Long quote and long comment, wrapping/scroll fades; >64 label, Unicode boundary, RTL/code/table text | Select actual long content if present in Replay; otherwise synthetic source projection in isolated stopped-server fixture or a labelled chip harness. Top/middle/bottom scroll, comment absent/present, <=64/>64 labels are distinct unchanged layouts. R01/R02 already sample absent/present and short/long label. | Existing styles only; optional recording for scroll, still for each static shape / NOT CAPTURED |
| C02 | Composer editor open/closed, anchored/unanchored/unavailable; voice setup; blank/valid/over-limit/rejected draft; no commentEditor | App Cite/Add comment/Edit comment for normal states; rejection and missing optional callbacks require labelled harness. Source disappearance and draft settlement covered by existing component tests, not modified by this diff. Real composer always supplies commentEditor. | Unchanged editor behavior; optional recording / NOT CAPTURED |
| C03 | Empty draft/no active thread; source loading/empty; malformed/empty quote | New draft route, missing route, or invalid citation (empty/all-whitespace text rejected by schema). Empty source cannot be selected/Cited. Draft promotion remains same caller. Synthetic input needed for impossible valid-source combinations. | No new visual branch; no additional capture; recording if exercised / CODE INSPECTED |
| C04 | Null citation, route/env/source mismatch, stale callback after navigating elsewhere | Labelled real-router/hook harness: use T_other, E_other, or swap C to S2 before pending S1 reveal runs. Matching route guard and stale side-chat guard have tests; environment mismatch and embedded hook exclusion inspected only. | Added guards; optional recording; not a valid successful quote arrival / PARTLY TEST-COVERED |
| C05 | Reduced motion, selection fallback without CSS Highlights, Electron shell | Browser reduced-motion/feature fixture for first two; Electron app on Windows for platform. No new theme/platform styles. Desktop shares web code but Electron titlebar/native modifier behavior is not exercised by browser pass. | Unchanged presentation; optional recording. Desktop NOT RUN; do not claim verified |
| C06 | Deleted entire side chat; wrong-role message; repeated history failure/page ceiling; scroll cannot reveal | Delete a used synthetic S1 after saving C in T through real UI; create wrong message ID using existing user ID; deterministic page-limit/scroll errors use labelled harness. If lineage is permanently absent the resolver cannot infer parent, unlike R09's temporary null. Deleted side chat and missing message have different paths; do not conflate them. | Existing failure controls; optional recording / NOT CAPTURED |

## Minimum set and capture rules

- Required: R01–R20 at exactly the viewport/theme/revision tuples specified above. Twenty row tuples; R01/R02 each need before and after, for **22 revision-specific row demonstrations**. These are coverage units, not a mandate for 22 separate video files: combine contiguous scenarios if every row is labelled and its initial state reset visibly. A row can contain several required interaction steps.
- No full theme Cartesian product: routing/CSS are theme-independent. Dark R01 and light R02 establish visible quote highlighting and chips in both modes; other rows use one mode. No new density/count threshold exists. Multi-tab count 1 vs >1 is sampled by R01/R04; zero tabs and closed panel by R08/R18; unknown/empty/deleted source by R09/R12/C06.
- R05 is the <=980px sheet branch; R06 adds the small viewport web composer branch. Maximized at both widths is exercised by R01/R05. Native mobile is outside the PR's stated web surface; shared contracts/client runtime and mobile code are unchanged. Desktop is shared web content, but the original Windows report still needs an explicit unverified statement unless separately exercised.
- R09 is deterministically a labelled harness unless a real app driver proves source-shell null then known. R14 requires narrow failure injection or labelled harness. R19 can use two real isolated environments or a labelled scoped-state harness. A labelled harness must use the real production hook/router/store and relevant pane/timeline; a direct helper call plus a fabricated screenshot is not equivalent. Do not edit production files to obtain a capture.
- Match the core before/after synthetic data, viewport, theme, sidebar/panel width and starting scroll. Put base/head labels outside the app frame in the recording/caption, keeping all credentials and private operational metadata out. Do not replace the lead's checkout or remove its worktree to get a before capture.
- Any capture driver writes each run to a fresh timestamped subdirectory of its own assigned scratch directory. This audit's scratch is `/tmp/lead-33160-r1/proof-audit-1/`; it contains the matrix, read-only PR metadata and focused-test log only. No screenshots, recordings, server, simulator or lease was created here.

## Verification and limits

Executed from the reviewed checkout: `vp test run apps/web/src/lib/assistantCitationNavigation.test.ts apps/web/src/components/sidebar/mainAppLocation.browser.test.tsx` — 2 files / 9 tests passed in 21.80 seconds. This does not replace real-client proof. No full checks or app/browser capture ran. Read all five changed files and the production callers/dependent panel/reveal paths listed above. Forge head/base and missing-evidence statement were re-read. Worktree tracked status stayed clean. Disk free remained above 100 GB.

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

FRICTION: none

VERDICT: INSUFFICIENT R01–R20 are missing captures
