# Proof audit pass 1 — issue24935

Read-only source audit: /home/ubuntu/wt/issue-24935, fix/issue-24935, base e3592efe7f → head 9573ae6258 (verified HEAD). No browser, server launch, repository edits, fixture mutation, or forge writes performed. This is a capture plan, not a claim of captured proof.

## Scope and changed paths

Only three files change, 40 insertions / 13 deletions. `withoutRemovedCheckout` clears the presentation copy's branch and worktreePath only for known `isRepo:false`; null/unknown and true preserve the stored values. It does not repair persisted thread metadata or delete anything.

| Row | Affected? | Distinct caller / branch | Observable expectation at head | Proof |
|---|---|---|---|---|
| C | Yes | Current SidebarThreadRow, card; active/pinned/working sections | Removed checkout hides worktree icon AND branch text; normal project leaves empty metadata area rather than stale branch. Hover details omit branch. | Still endpoints + hover recording |
| L | Yes | Same component, slim; sections outside active/pinned/working (use settled) | Slim has no branch text/icon even before fix; only its hover details change, omitting removed branch. Do not claim its resting appearance proves this fix. | Hover recording |
| S | Yes | SidebarSearchResultRow | Result body has no changed branch rendering; hover details omit removed branch. Search remains navigable. | Search → hover recording |
| G | Yes | LegacySidebar's separate SidebarThreadRow | New leased status subscription is restricted to non-null worktreePath; removed checkout hides ThreadWorktreeIndicator. Existing icon tooltip includes worktree path + branch and disappears with icon. | Still endpoints + hover recording |
| R | Yes, shared state transition | Same cwd status false → true → false, retained status across subscription release/reacquire | Recreate same worktree restores icon/branch; remove again clears them. Temporary null data for same key retains last known result; a different environment/cwd must not inherit it. | Continuous recording with explicit refresh milestones |
| U | Control | Unknown status before first usable response | Existing stored branch/worktree must remain until known false; unknown must not be mistaken for removal. | Record only if real pending state observable; otherwise mark unproven, focused logic coverage needed |
| V | Control | Existing valid worktree, isRepo:true | Card icon/text, shared hover branch, and legacy icon remain; no warning for a valid worktree. | Use recovered phase of R; no separate fixture |
| M | Control | Local/root checkout, worktreePath:null, valid repo | Existing differing thread/current branch warning remains in current row/search hover. Helper preserves valid repo metadata; legacy adds no root status subscription. | One representative desktop hover recording if fixture already available |
| X | Conditional affected | C showsScratchMachine: !checkoutThread.branch && scratchMachineLabel!=null | Removed branch can reveal machine icon/label only for scratch projects when visible project groups span environments. Normal supplied project has label null, so cannot cover this branch. | Separate fixture only if already available; do not expand tiny fix by provisioning another environment |

No changed render caller in mobile, server, composer, chat branch toolbar, context menus, MCP, or provider adapters. Desktop shares web renderer. Pinned/working cards share C's JSX; settled/snoozed slim rows share L's JSX. No need to multiply by provider, PR state, title, terminal state, drag/drop, or every sidebar section. Branch mismatch computation also receives the sanitized copy, but an ordinary stored worktree already suppresses mismatch before the fix; do not manufacture a new visual warning case for it.

## Minimal real-app fixture and capture set

Reuse the lead's isolated app: web port 8407, server 16447, project `issue-24935-repro`, environment `a8df9b39-05ba-44eb-8b95-42ba463cc57c`, thread `1cf30be4-24a7-4a11-ab77-875769b13185`. Route: `/a8df9b39-05ba-44eb-8b95-42ba463cc57c/1cf30be4-24a7-4a11-ab77-875769b13185`. This thread came from real app New worktree flow; stored branch is `t3/reply-hello`, stored path `/home/ubuntu/wt/issue-24935/.t3/worktrees/issue-24935-repro/t3-f47dd3b0`. Root fixture repo `/home/ubuntu/issue-24935-repro`, branch main. At handoff the worktree is removed/unregistered and branch deleted.

One real persisted thread is sufficient for C/L/S/G/R/V. Move that thread between active and settled using app controls; unsettle afterward. Search its exact title via `Search threads`, clear search afterward. Toggle `Sidebar (legacy)` in Settings → General (search setting id `legacy-sidebar`); restore default sidebar afterward. Card/slim is NOT a preference: current Sidebar.tsx selects card for active/pinned/working, slim otherwise. At 390px open the navigation drawer before inspecting rows; capture the drawer and hover popup viewport behavior, not only the chat behind it. Hover must actually show popup; emulate mouse at narrow viewport if needed (390px is responsive web proof, not native mobile proof).

Capture configurations: **1440 light, 1440 dark, 390 light, 390 dark**, fixed documented height (900 recommended). Use actual app appearance controls; no CSS injection. All affected rows C/L/S/G must appear in this four-configuration set. Keep baseline/head framing, title, sidebar width, scroll, theme, viewport, and lifecycle phase comparable.

Minimum deliverable per revision (base and head):
- **8 stills**: removed-state C and G × four configurations. Baseline shows stale icon/branch, head hides them. C framing can include the open details popup if unobstructed.
- **4 recordings**, one per configuration, consolidating C hover → settle/L hover → search/S hover → clear/unsettle → legacy/G hover. These supply interaction proof; stills alone do not satisfy L/S. Label each phase visibly or in accompanying timestamps.
- At **1440 dark**, extend that existing recording with R: known removed → recreate → refresh → visible restored metadata → remove → refresh → hidden metadata. This recovered segment also supplies V. Run the same sequence at base to show the stale removed behavior; no need for four separate recovery recordings because dimensions/theme do not alter the state logic.
- U and M are control observations, not another 2×2 Cartesian set. Include in the 1440-dark recording if naturally available; report absent evidence honestly. X is explicitly conditional/not covered by supplied fixture.

Thus core set: **16 stills + 8 recordings total across base/head**, using one thread and one reversible worktree lifecycle. Recordings may include the still endpoints, but retain exported stills for rapid before/after comparison. Capture identifiers should include revision/config/row/state, e.g. `head-390-dark-C-removed.png`. Do not call source inspection or an unchanged slim resting row a visual proof.

## Seeding and refresh instructions for lead (not executed here)

Keep the existing isolated `.t3` database. No SQL edits, backend object mutation, mocked VCS payload, or live userdata writes are needed. For a fresh sandbox, seed via normal dev database snapshot tooling then create an actual thread with New worktree in the app; it is the real persisted branch/path paired with later filesystem deletion that reproduces this defect.

For recovery, use git in the dedicated ROOT FIXTURE repo to create branch `t3/reply-hello` from main and add a worktree at the exact stored path; confirm its git status/registration. Before deleting, confirm it is clean and contains no agent work; remove via git worktree remove, then delete that fixture branch. Do not mutate the implementation checkout or recreate at a different path. Never commit fixture scripts; if a script is useful, keep outside repo and upload it.

Status needs an explicit refresh after each external git transition. Source-backed command: `vcs.refreshStatus`, environment above, input `{cwd: <exact stored path>, localOnly: true}`. In client code the wrapper is `vcsEnvironment.refreshStatus`; branch-selector opening calls it with the branch cwd. Use the authenticated command path already available to the lead, or a completed agent turn as the established refresh trigger. Do not assume a specific browser-global RPC object exists. Refresh the stored worktree cwd rather than only the root fixture. Wait for the actual status/render milestone before capture, rather than elapsed delay. Existing subscription is viewport/active leased with 160px overscan; keep inspected row visible. Releasing/reacquiring its lease while preserving same cwd should not flash stale branch after a known false result.

Settings are persisted via client settings APIs; no URL query parameter for forcing card/slim or isRepo was discovered. Use app Settings and sidebar section controls. A reload is not required for each setting/state and may take 30+ seconds; if used for pending-state U, start recording before reload and wait for readiness, without treating unknown as a guaranteed reproducible fixture.

## FRICTION

FRICTION fixture|Removed worktree and deleted branch are the starting state, so no valid indicator exists to hover initially|Recreate exact path+branch for recovered control, then remove again; keep metadata untouched.
FRICTION subscription|External git mutations can leave cached UI unchanged until refreshed|Explicitly refresh exact worktree cwd, keep row visible, await observed status.
FRICTION layout|Slim is section-driven; hovering resting slim/search is the only changed presentation there|Settle/unsettle through UI and record popup opening; don't hunt a nonexistent density preference.
FRICTION responsive|390px navigation is a drawer and hover can be clipped or unavailable on touch|Open drawer, use desktop pointer at narrow viewport, capture actual popup; distinguish from native mobile.
FRICTION reload|Full reload can take 30+ seconds and confounds unknown-state observations|Prefer in-app settings and explicit refresh; record readiness and report U unproven if missed.
FRICTION conditional|Scratch machine fallback requires scratch project plus cross-environment grouping|Mark X conditional; don't seed fake data or provision unrelated environments for this tiny diff.
FRICTION hygiene|Fixture scripts/screenshots can accidentally enter the implementation diff|Keep in /tmp or external artifact directory, upload with ghdrop, never commit.
