# Proof audit, pass 1

PR [#32507](https://git.cafe/pingdotgg/t3code/pull/32507), issue [#32492](https://git.cafe/pingdotgg/t3code/issues/32492). Reviewed head [bf3f9f0](https://git.cafe/pingdotgg/t3code/commit/bf3f9f0acfcbe39715d4d6b5d62a5da34b378530), source branch `fix/issue-32492`, against main [d313b2a](https://git.cafe/pingdotgg/t3code/commit/d313b2aff31991ab94c8ac80cdc38a54dd575604). Local HEAD, PR metadata, remote branch tips and merge base agreed. Scope: source-derived proof plan for web; Electron boundary identified separately. No browser, server, test, seed or capture was run in this pass. No repository changes or forge comments were made. All fixture content below is synthetic.

## Findings

1. **Proof is insufficient.** No captures are attached. The new browser test supplies one synthetic older-settled row, opens its URL, clicks the title action button and asserts that Rename thread appears. This does not prove search-result ownership handoff, header right-click, the toast or retention across route/environment/shell transitions. The PR body's manual verification and test results are author-reported, not executions of this audit.
2. **There are two changed behaviors, not a styling change.** The route mounts a family only after authoritative bootstrap when a server route has neither shell nor local draft. Its mount sticks after the family supplies the row and releases on route-key change/unmount. A menu miss now emits an error toast. Every required row therefore needs a recording; endpoint stills may supplement it but cannot replace it.
3. **The mount is broader than the motivating settled root.** It applies to any eligible off-shell server thread, including omitted idle subagents, and runs before hidden/deleted/missing route gating. The matrix includes those guards and a reconnect. This is a proof obligation, not a confirmed regression.
4. **The existing fake cannot prove the reporter's real cutoff/search reproduction.** `olderSettled: true` omits a row regardless of count or age and enables family support. It has no older-settled summary/cutoff or real search implementation; all fake timestamps are the same. Use the real app for the four reporter sequences. Synthetic fake captures must be labelled harness. Its new family handler groups a row by `sideChatOf ?? id`; do not use it to claim arbitrary nested subagent-lineage coverage.
5. No independent code defect is asserted in this proof-only pass. CI/reviews were read for context, not used as visual evidence. The current review list was empty; existing bot comments include analysis of an earlier revision. No merge-readiness verdict is implied.

## Render and interaction inventory

### Changed route component and hook

- The only production `ThreadRouteView` caller is [ChatRouteLayout](https://git.cafe/pingdotgg/t3code/blob/bf3f9f0acfcbe39715d4d6b5d62a5da34b378530/apps/web/src/routes/_chat.tsx#L18). It supplies `target` and `background=modalOpen`. Server routes are `/$environmentId/$threadId`; draft routes are `/draft/$draftId`. Settings (`/settings` and descendants) and `/usage` retain the preceding target as an inert background. With no target, the layout renders an outlet or empty inset instead; the changed component is absent.
- [ThreadRouteView](https://git.cafe/pingdotgg/t3code/blob/bf3f9f0acfcbe39715d4d6b5d62a5da34b378530/apps/web/src/components/ThreadRouteView.tsx#L34) has these mount inputs: null/server ref; shell status live versus every non-authoritative state; shell present/absent; local draft present/absent; same mounted key versus different key. The key includes environment and thread. The effect's cleanup releases its mount. A present row at first render takes the no-mount path; a row arriving after a miss must keep its mount. A family query may be pending, failed, empty or populated; reconnect changes its shell-generation query. Servers without `shellThreadFamilies: true` return an empty family rather than making that RPC.
- Route render branches are: draft session hidden -> home; draft session visible -> keyed draft ChatView with expanded mobile composer; promoted draft hidden -> home; promoted draft off-shell while any tag hiding is active -> redirect to its server route without mounting draft ChatView; promoted visible draft -> server ChatView retaining draft key plus server redirect; absent draft/session/redirect -> home. Server branches: hidden-key -> home; off-shell and hiding with no loaded visible parent -> blank until detail exists, then home after bootstrap; otherwise ready -> ChatView; loading with a shell -> ChatView; loading without a shell -> empty inset. A detail failure with no projection stays on the loading/empty path; cached detail with data can still decide ready/missing. Family failure has no route-specific error UI and is surfaced by the new toast only after a menu click. Missing is decided by deleted shell, deleted detail, or archived/deleted projection; with other shell/draft refs it navigates home, without them it stays empty. Background suppresses redirects and missing navigation and makes the inset inert. These branches are represented by R09-R18 or explicitly collapsed control C01.
- The handoff key has no independent visual layout: draft promotion retains the same ChatView instance; same server route retains it, leaving resets it. R11/R18 cover the lifecycle. Inset height uses `h-svh` below `md`, `h-dvh` at `md`; the new code adds no theme, width, operating-system, provider, item-count or text-length branch.

### Changed menu hook and its callers

- The only production caller of [useThreadActionMenu](https://git.cafe/pingdotgg/t3code/blob/bf3f9f0acfcbe39715d4d6b5d62a5da34b378530/apps/web/src/hooks/useThreadActionMenu.ts#L61) is [ChatHeader](https://git.cafe/pingdotgg/t3code/blob/bf3f9f0acfcbe39715d4d6b5d62a5da34b378530/apps/web/src/components/chat/ChatHeader.tsx#L199). ChatView is the only production ChatHeader renderer and omits it when embedded. It passes a server thread ref or null for a draft, project workspace path or null, rename callback, optional Find callback (`serverConfig.threadFind`), and guest-share grant availability. Tests also call the hook directly through `createMenu`; ChatHeader browser tests mock this hook, so they do not exercise the changed row lookup.
- Open interactions: title/button/chevron click anchors below its rectangle; keyboard button activation follows that handler; header context menu uses pointer coordinates; trailing double-click is ignored. Unmodified title double-click closes the web menu and starts rename, except modifier keys or the chevron target. A draft title has no thread action trigger; draft header context menu offers project settings only when supplied. During rename header context menu does nothing. Rename accepts nonempty changed text on Enter/blur, rejects empty text, Escape cancels; composition ignores Enter. These existing branches are controls C01/C06, not separate new styling obligations.
- Menu opening branches: null ref -> return; no LocalApi (SSR/non-window) -> return; shell missing -> new error toast with title `Thread actions unavailable`, description `This thread has not loaded yet.` and no menu; shell found -> existing items and menu. Choice failure/null -> return. Operate permission is checked again after the menu resolves; a revoked grant produces a different existing `Thread action unavailable` toast. Web uses DOM fallback; desktop uses `desktopBridge.showContextMenu`. `closeMenu` dismisses only the DOM fallback. New toast is independent of platform and has no loading spinner or Retry action; a later click is required after the row arrives.
- Header layout inputs: project absent versus project breadcrumb; scratch project -> `No project`; project and thread title lengths; subagent parent absent versus title/ellipsis (measured width, title/font/rename changes); `rightPanelOpen` changes right padding; link origin adds `From ...` badge outside rename; guest sharing can add indicator. Server title shows text-direction-aware text and hover/focus chevron, draft shows text, rename shows input. R14/R20/R21 and controls cover representatives, with no Cartesian multiplication for unchanged header style.

### Menu data shapes and all existing action branches

The menu builder is unchanged; restoration exposes the same builder. [Menu logic](https://git.cafe/pingdotgg/t3code/blob/bf3f9f0acfcbe39715d4d6b5d62a5da34b378530/apps/web/src/components/threadActionMenu.logic.ts#L116) varies as follows:

| Input dimension | Visually distinct result | Fixture mapping |
| --- | --- | --- |
| `onFind` present/absent | Find row included/omitted | R02, R22 |
| branch null/nonempty/long | New thread on branch row and Copy/Branch absent/present/truncated | R02, R21, R22 |
| config loaded / not loaded | Pin, Settle, Snooze, Regenerate rows present/omitted | R02, R22 harness |
| auto-settle support and enabled flag | Submenu absent or Follow settings/Never checkmark | R02, R21, C02 |
| pinned/unpinned | Unpin/Pin | R02, C02; pinned roots cannot satisfy real older-settled omission |
| explicit settled/active | Un-settle/Settle | R02, C02 |
| snoozed future/unexpired and not raised hand / unsnoozed, expired or early wake | Wake / Snooze submenu | R02, C02; future-snoozed roots are retained in real shell |
| can snooze / pending approval, incompatible runtime request, pending subagent request, queued start or guest message | Snooze enabled/disabled | C02; live/actionable rows are controls rather than honest old-settled fixtures |
| snooze presets 0 / 1 / multiple, duplicate wake minute, time format and current time | Custom-only without separator / timed entries plus separator; labels/count change | R02, R21; duplicates collapse at minute, clock fixed for capture |
| regenerating title null/non-null | Regenerate title enabled / Regenerating… disabled | C02 |
| runtime attached/running versus archivable | Archive disabled/enabled; Turn into project disabled/enabled if present | R02, C02 |
| operate grant yes/no | Mutable rows and submenu children enabled/disabled; local actions remain | R02, R19 |
| guest share grant yes/no | Share with guest row and possible indicator | R02, R20 |
| scratch project with own folder and capability / otherwise | Turn into project included/omitted | R02, R20 |
| `projectFilter` | Always null at this hook caller, so Filter/Show all projects is unreachable here | C03, no fixture needed |
| path available / absent | Menu structure same; Copy Path succeeds / Path unavailable toast | R02, C04 |

Every menu interaction is accounted for, without requiring a new capture for every unchanged handler: Find opens in-thread find; project settings navigates; new thread on branch creates draft with same branch/worktree; settle/unsettle, pin/unpin, snooze/presets/custom/cancel/expired, unsnooze, auto-settle, regenerate-title and mark-unread update their respective states; Rename starts header input; Share opens guest dialog; Turn into project promotes and toasts; Copy Path/Branch/Thread/ID use clipboard (missing path toasts); Archive/Delete may confirm/cancel, then mutate/navigate. Command failures toast, interruptions do not, archive distinguishes navigation failure after success, delete avoids a duplicate missing-row failure. R02 must demonstrate Pin/Unpin and Rename to prove usable retained row data; the remaining old handlers stay C04 rather than expanding this small patch into a full action audit.

### Test fixture changes

[Fake environment](https://git.cafe/pingdotgg/t3code/blob/bf3f9f0acfcbe39715d4d6b5d62a5da34b378530/apps/web/test/fakeEnvironment.ts#L194) is non-product code. Its `FakeThread` permits `sideChatOf`, new `olderSettled`, id/title, arrays of runs/items. Zero history creates idle shell, nonzero completed history; titles/counts affect ordinary ChatView content, not the new mount branch. `olderSettled` sets explicit settled fields in shell and projection, omits the row from shell, and dynamically advertises family support; no older row leaves previous config behavior. Family request finds requested id -> same synthetic root group, missing id -> empty. Initial config and both config subscriptions now call the same factory. Callers are ChatView browser regression tests, model-picker test, Sidebar shortcuts test and whole-app render scenarios; these are not extra shipped surfaces. New [browser test](https://git.cafe/pingdotgg/t3code/blob/bf3f9f0acfcbe39715d4d6b5d62a5da34b378530/apps/web/src/components/ChatView.browser.test.tsx#L70) has one older row, one history turn and a direct route. It does not mount search/shelf or exercise failure timing.

## Seed recipes for the capture operator

These are instructions, not scripts run in this pass. All driver files, seeds, logs and captures must be written under `/tmp/lead-32507-r1/proof-audit-1/<new-timestamp>/`; never overwrite/delete an earlier run. Use only synthetic content and an isolated home; never seed the developer's real database. The primary capture operator reuses its task's dev server and follows the test-t3-app skill.

**S30 (real app/RPC):** Create a synthetic project `Proof project` rooted at a synthetic writable folder. Use authenticated `orchestration.dispatchCommand` with `thread.create` for ids `proof-00` through `proof-29`, titles `Seed 00` through `Seed 29`, distinct command ids and the app's valid model/runtime defaults. No paid turn is needed. Dispatch `thread.settle` with explicit `settledAt` values from capture-now minus 30 days through minus 1 day. Settle all before the cold authoritative connection/snapshot. The server keeps the newest 25 settled top-level timestamps globally, including ties; do not create equal settle times or unrelated real threads. Keep target unpinned, unsnoozed, not live and without a retained child. Verify actual shell excludes proof-00..04 and includes proof-29, family read includes the target. Sidebar search supports older-settled title matches, so search `Seed 00`; message history is optional for this title search. For a transcript-visible representative, a free Replay turn may be created before settling. Do not claim backend mutation success using projection-only fixtures.

**Open:** route `/<environmentId>/proof-00` from a fresh page for URL cases; for search, open another thread, search `Seed 00`, open the result, clear/close search completely and ensure the Settled shelf is collapsed. Do not accidentally keep search/shelf row mounts alive. For shelf handoff, page Settled with Show 25 more, open proof-00, collapse/unmount the shelf, then act. Avoid the capture operator inspecting the family through a persistent client mount that itself masks the bug.

**Delay/failure (real client with controlled transport):** Gate only `orchestration.getThreadFamily` response, allow shell/config and thread-detail HTTP/socket to succeed. A general network slowdown is not deterministic because it can prevent the header from rendering. Browser-level RPC interception or an external fixture RPC server can release/reject a recorded request; this is a labelled harness-transport capture if the ordinary Browser panel cannot control individual WS responses. Do not claim generic HTTP interception delays a WebSocket RPC. Use explicit response milestones, not sleeps. For a failed family, reconnect or leave/revisit to cause a new query; clicking alone does not refresh it. Empty family with live detail needs an intentional inconsistent fixture server and is always labelled harness.

**Tags/missing/drafts:** Use Settings to enable hidden thread tags, and normal app metadata commands to give target/parent that tag; open direct URL with/without a loaded visible parent. Archive/delete through app commands and then navigate the stale URL, both with another listed thread and in an otherwise empty environment. Draft route uses New thread; promotion uses a free Replay send. Hidden mid-promotion draft and missing config/family-support version cases require a labelled full-AppRoot harness if not controllable through the real app. Projection fixture writes may be used only in a stopped isolated server, with copied valid payloads and matching lineage; they prove rendering, not event-sourced mutations.

**Family:** A normal app fork with `sideChat: true` supplies a side chat; omitted idle subagent needs a synthetic completed subagent lineage with parent/root IDs and real family endpoint. Avoid paid provider turns; if no free app/RPC path creates this lineage, label a projection-seeded client capture or full-AppRoot fake-server capture as harness. The present `sideChatOf` fake has side-chat lineage, not subagent lineage, so it cannot prove the subagent parent breadcrumb.

**Settings:** theme in Appearance, timestamps in client settings, environment snooze presets in Settings/Threads. Keep the capture clock/timezone explicit (for example 2026-11-10 12:00 UTC) for snooze labels. Reset theme, permissions and mounts between runs. Read-only is a second authenticated session with orchestration read but no operate scope. Guest/share and scratch promotion rows need the actual destination environment's supported capabilities/grants, otherwise labelled harness.

## Fixture matrix and minimum capture set

D = web 1440×900, dark. L = web 390×844, light. W = web 1024×768, light. These are CSS viewport sizes at 100% zoom and default UI font. A capture means a **recording**, including setup-to-action transition and observable result; still endpoints are optional. A single continuous recording may cover several rows if each is visibly identified and its preconditions are logged. No full viewport/theme Cartesian product: no changed code branches on theme; success and the new toast are each sampled in both themes/widths. Native mobile is a separate UI and outside this web diff.

For every required affected row, capture the same synthetic sequence on main and head at the listed tuple and label Before/After; an unchanged endpoint is still useful guard evidence. R01 and R18 are head-only controls. C rows explicitly require no new capture. All required captures are currently missing.

| Row | Named fixture and required data | Diff effect / expected proof | Seed and interaction | Minimum tuple; type | Status |
| --- | --- | --- | --- | --- | --- |
| R01 | Listed recent settled control: Seed 29, one project, live shell, full grants | No mount needed; same usable menu; no family RPC attributable to route | S30; click title, Escape, header right-click; record request trace alongside screen | D; recording; head control | MISSING control capture |
| R02 | Old omitted root, cold URL, settled/unpinned/unsnoozed, branch main | Family supplies row; title chevron opens menu; Pin then Unpin and Rename work; dismiss/reopen remains usable | S30/Open; show actual chevron activation, Pin/Unpin, rename with Enter; keep shelf/search closed | D; recording; Before/After | MISSING |
| R03 | Same old omitted root, cold URL, narrow web | Header right-click opens at pointer and fits viewport | S30/Open in fresh page; right-click header, dismiss with Escape | L; recording; Before/After | MISSING |
| R04 | Old omitted root opened from title search; search rows have released | Route retains row after search closes; chevron works | S30; search Seed 00, select, clear/close search and shelf, click chevron; reopen after dismiss | D; recording; Before/After | MISSING |
| R05 | Same search handoff, narrow web, keyboard button access | Header right-click works after handoff; keyboard focus and Enter also open title menu | S30; search/open/close; right-click; dismiss; Tab to title and Enter | L; recording; Before/After | MISSING |
| R06 | Old root already mounted by paged Settled shelf, then shelf leaves | Existing row at route entry may avoid initial mount; after shelf unmount route must acquire it | S30; Show 25 more, select old row, collapse shelf, title click | D; recording; Before/After | MISSING |
| R07 | Detail ready but family request pending; live shell has no row | First click shows exact new toast with no menu; releasing family then a new click opens menu | S30 + targeted response gate; click both entry points while pending, release and retry | D; recording; Before/After; transport harness if needed | MISSING |
| R08 | Family failed, detail still valid; persistent missing row | Same readable toast; repeated click must not silently fail; leaving/revisiting or reconnect may recover | Targeted family error, then successful remount/reconnect. Empty-family variant has same visuals and is harness-only; log it without requiring duplicate pixels | L; recording; Before/After; labelled transport/harness | MISSING |
| R09 | Cold server route before authoritative shell; no cached shell/detail | Empty inset until data; no premature redirect, family read or missing-row toast; eventually ready menu | S30 with shell bootstrap gate; release shell then detail/family milestones | W; recording; Before/After; gate harness if needed | MISSING |
| R10 | Cached open old thread through reconnect/new authoritative generation | Existing chat can stay shown while loading; family becomes absent/pending and repopulates; final menu uses current generation | S30; open, disconnect/reconnect destination, gate/release new family; record no stale row after new snapshot | D; recording; Before/After | MISSING |
| R11 | Route lifetime, two omitted targets and two environments with same synthetic thread id | Same route retains mount; different thread/environment replaces ownership; leaving to home/draft releases it; returning works | S30 in two isolated environments; A/proof-00 -> A/proof-01 -> B/proof-00 -> listed thread -> home/draft -> back. Pair video with mount/RPC diagnostic trace; pixels cannot prove cleanup alone | D; recording; Before/After | MISSING |
| R12 | Omitted old thread under Settings modal | Background inert; family retained, redirects suppressed; close returns to same usable header | S30; open old root -> /settings/threads -> close -> menu | D; recording; Before/After | MISSING |
| R13 | Omitted old thread under Usage modal | Same retained background mechanism under different modal surface | S30; open -> /usage -> close -> menu; can share R12 video | D; recording; Before/After | MISSING |
| R14 | Omitted idle subagent; parent + long titles, long project name, right panel open; parent initially absent then family present | Entire family is restored; parent breadcrumb changes from fallback to real title, collapses on constrained width; child header actions work | Family recipe, valid subagent lineage; 100-character parent/project/title; open child URL, open/close panel, then parent link/menu. Linked-origin badge may be added through synthetic valid metadata | D; recording; Before/After; labelled harness if seeded | MISSING |
| R15 | Hidden off-shell root or child of hidden/unavailable parent | New mount must preserve gating: blank pending, then home; hidden content must not flash | S30 + Tags; direct root URL; separately child with blocked parent; stage family/detail to show ordering. Directly hidden shell-key variant has same home endpoint | D; recording; Before/After | MISSING |
| R16 | Tag hiding active, off-shell child of loaded visible parent, no hidden target tag | Allowed-child exception remains usable, then mounted family can supply row | Family + Tags; visible parent shell present, child absent; open child URL then menu | W; recording; Before/After; labelled harness if needed | MISSING |
| R17 | Deleted/archived/unknown server route: with another thread versus empty environment | Family does not resurrect missing thread: home when others exist, empty inset when none; background variant defers navigation until overlay closes | App archive/delete commands, stale URL; repeat with no other refs; overlay variant via synthetic runtime event if needed. Missing detail 404, deleted shell and archived projection collapse to these two endpoints | D; recording; Before/After; two labelled phases | MISSING |
| R18 | Draft/no server row, then promotion with same reserved id | New hook must not fetch a draft family prematurely; composer survives promotion; server menu available afterward | New thread -> /draft/<id> -> free Replay send; pair with family request trace; include empty-history draft | L; recording; head control | MISSING control capture |
| R19 | Old omitted root through read-only remote session | Restored menu has disabled mutations and usable Find/Copy; toast path remains possible without operate scope | S30 in read-only destination connection; menu, Copy/Thread ID; no grants added for capture | D; recording; Before/After | MISSING |
| R20 | Old omitted scratch-project root with own folder, guest share and scratch-promotion grants/capabilities | Breadcrumb labelled `No project`, Turn into project and Share with guest rows/indicator visible after mount | Synthetic scratch project + thread worktreePath, valid capabilities/grants; open menu and guest dialog, cancel; do not need to execute promotion for this patch | D; recording; Before/After; labelled harness if capability unavailable | MISSING |
| R21 | Old omitted root with long branch, auto-settle disabled, empty snooze presets, 24-hour labels | Restored menu submenus, checkmark and long branch truncation remain usable; Custom-only snooze has no leading separator | S30 + metadata/settings; show Copy and Auto-settle submenus, Snooze -> Custom -> cancel. Contrast R02 default nonempty presets; explicit capture clock | L; recording; Before/After | MISSING |
| R22 | Old omitted root, branch null, Find absent; config-not-loaded/legacy-family variant | Minimal menu omits branch/Find; missing config omits supported groups; old server family-empty path shows toast instead of silent miss | Real app for branch null and Find-disabled capability; full-AppRoot fixture for config unavailable or no family support while valid detail remains. Keep labelled phases since minimal menu and toast differ | W; recording; Before/After; capability phases labelled harness | MISSING |

### Collapsed controls and unreachable combinations

| Row | Branches inventoried | Why not another required capture | Status |
| --- | --- | --- | --- |
| C01 | Draft session/redirect missing or hidden; promoted off-shell draft redirect; server local draft until finalization | Unchanged redirect/layout code; R15 tests hidden guard, R17 missing endpoints, R18 draft-to-server. If a capture claims mid-promotion specifically, use labelled harness, not a settled-root substitute | COVERED BY PLAN; no separate capture |
| C02 | Already-listed pinned+snoozed/active/running/pending/regenerating thread, support flags, both auto-settle checkmarks | Builder unchanged. Real shell expressly retains pinned, future-snoozed, live and ancestor rows; combining these with a supposedly real omitted settled root contradicts the seed. R01 controls listed behavior; R02/R21 show relevant menu branches | EXCLUDED unchanged combinations |
| C03 | projectFilter active/inactive, hook called without LocalApi, null ref, embedded ChatView | projectFilter is always null here; no-window has no capturable app; draft null-ref R18; embedded ChatView has no header and does not call this menu | NOT APPLICABLE / unreachable |
| C04 | All remaining menu action success/failure/confirmation/cancel/permission-revocation branches | No handler code changed. Pin/Unpin and Rename in R02 prove retained row usable. Existing unit coverage is not visual evidence but does not require a full action-product audit here | EXCLUDED unchanged handlers |
| C05 | 0/1/25/tied-25/26/30 settled roots; no/one/many history items; multiple projects | Count matters only for whether actual shell includes row; R01 vs S30 omitted rows collapse that binary. No count/layout code changed. Use 30 distinct settle timestamps for honest cutoff proof. Empty composer R18, ordinary transcript R02 | COVERED BY PLAN |
| C06 | Long/short/RTL titles, custom fonts, hover/focus/double-click/rename composition, right panel padding, unmodified/modified double-click | Header unchanged. R14 constrains long text and panel; R05 keyboard focus; R02 rename; theme/font Cartesian product and all input-method variants add no changed branch | EXCLUDED unchanged header permutations |
| C07 | Electron native context menu; macOS/Linux/Windows shell/titlebar | Desktop shares mount and hook but switches renderer via desktopBridge. Web matrix does not prove native menu rendering. A desktop recording of R02/R03 is needed only for a desktop-proof claim; no native mobile code changed | OUT OF REQUESTED WEB MINIMUM; explicitly unverified |
| C08 | Local, remote/relay and tunnel routes | URLs transport differently but hook uses destination ref, not origin; R19 remote permissions and R11 multi-environment cover relevant data/grant scope. No protocol/auth/server code changed; no separate tunnel UI capture required | COVERED BY PLAN / no extra capture |

## Capture acceptance

- Keep raw synthetic request/event traces with the recordings: authoritative shell omits target, family response contains it, route mount lifetime and actual menu action result. Do not infer a missing shell from merely not seeing a sidebar row.
- Before footage must show the click/right-click and absent menu/toast with a stable ready transcript; After must show the same setup and the actual menu/toast/action. Add labelled still endpoints if desired.
- For R07/R08 make the response gate/error visible in a non-sensitive trace or recording annotation; do not call an arbitrary delay a measured slow-connection reproduction.
- Every affected row R02-R17/R19-R22 and controls R01/R18 remains uncaptured. Multiple rows may share a video, but no row can be marked covered merely by a source assertion, a test pass, or another fixture's menu screenshot.
- This pass started no persistent process; no teardown required. Workspace stayed clean; disk had 231 GB free at start. No deletion was performed.

FRICTION: none

VERDICT: INSUFFICIENT R01-R22 recordings missing; R02-R17 and R19-R22 require labelled Before/After evidence, R01/R18 require head control evidence
