# Proof auditor pass 1 — fixture and capture contract

Scope: [#15238](https://git.cafe/pingdotgg/t3code/issues/15238), checkout `/home/ubuntu/wt/issue-15238`, branch `fix/issue-15238`. Compare base [3cc31ed27c](https://git.cafe/pingdotgg/t3code/commit/3cc31ed27c) with head [a327d35edf](https://git.cafe/pingdotgg/t3code/commit/a327d35edf298ffe9e1353a1d80edfe21e32d82c). No PR exists. This is a source-derived proof plan, not a claim that captures or runtime checks have passed.

## Lead reproduction and shared runtime

Lead reports a real Claude chat task launching a background `python3` HTTP server on port 5173, returning HTTP 200, while the default sidebar has no server indicator. Preserve that as fixture C-A; do not substitute a server started directly by the auditor or a manually injected discovery payload. At base, absence can have two causes: agent ownership is not discovered AND the default sidebar has no globe render path. Runtime discovery evidence must distinguish them.

Reuse the lead-owned app dev server: web port 5835, backend port 13875, isolated worktree `.t3`. Do not launch another app server, browser system, or subagent server. The lead owns checkout switching and any stop/start needed for server code changes. Capture base first, then head, through this same dev-server setup and browser profile. Verify the actual revision and backend readiness on each side; frontend HMR alone does not prove the new backend is running.

Provider tasks stop across app-server restarts. Relaunch the corresponding fixture commands through their owner chats after each restart; verify HTTP 200, process ancestry, and discovery again. A restored database does not restore provider roots, terminal registrations, or listening processes. Never use the developer's live database for writes. No processes or state were changed by this audit.

## What the complete diff affects

All 11 changed files were inspected. Three changed sidebar render paths need visual proof:

* Default **card**: new globe in the bottom metadata row, before the terminal indicator, PR badge and diff counts. Active, pinned and working sections share this path.
* Default **slim**: new globe inline after title/pin, before the terminal indicator, PR badge and trailing controls. Settled and snoozed sections share this path.
* **Legacy**: existing globe/handler extracted into the shared component; adds shrink prevention and pointer/key propagation guards. Existing terminal-owned behavior must remain intact; agent-owned discovery becomes available here too.

Cards/slim are section choices, not density preferences. `Sidebar.tsx` selects card for active/pinned/working and slim otherwise. Use active and settled as the representatives. No need to duplicate pinned/working/snoozed styling when adjacent controls and backgrounds are unchanged by this diff. Include a long title, long branch and nonzero diff counts in the representative data to expose pressure on the icon slots; keep PRs absent since there is no PR for this task.

The data change also affects the Browser empty-state server list: agent-owned listeners now become discoverable environment-wide. Capture its list as a secondary state in an existing recording; a fourth sidebar layout is not required.

Server changes register Claude's POSIX spawned PID and ACP runtime PIDs, walk ancestry, attach optional `threadId`, include it in snapshot equality, and probe owned HTTP listeners. Contracts keep `threadId` optional. The client chooses `threadId ?? terminal?.threadId`. These need runtime/data evidence, not more copies of the same globe screenshot.

## Shared seed contract

Use one synthetic project named `Proof servers` in the isolated environment E. Resolve E and actual persisted thread IDs from the lead's fixture manifest. The route is `http://localhost:5835/<E>/<threadId>` (the route file's `_chat` segment is pathless). Never publish pairing URLs or auth tokens. Substitute actual IDs for the aliases below; aliases are not executable routes.

Use C-0 as quiet, deterministic selected chat Q for overview frames so differing agent transcripts do not pollute image comparisons; no ninth fixture thread is needed. Use fixed titles, message bodies, branch labels, run/diff summaries, sort order, timestamps and project expansion in both revisions. Use the real lead reproduction as C-A, retaining its actual ID. Other fixture IDs below are suggested aliases, not claims that those rows already exist.

Settings D: `legacySidebarEnabled=false`, `sidebarWorkingShelfEnabled=false`; fixed default typography, theme palette, grouping and sorting. Active cards: `settledOverride=active`, no pin/snooze. Settled slim rows: `settledOverride=settled`, fixed `settledAt`, no pin/snooze; expand Settled. Launch provider work before settling the slim fixtures so a new turn does not move the row. Check actual `data-testid=sidebar-row-card` / `sidebar-row-slim` before capture.

Settings L: same data/settings except `legacySidebarEnabled=true` (Settings > General > legacy features > Sidebar (legacy)). Expand the synthetic project and show only the four C fixtures; the S duplicates have no distinct legacy path and may be collapsed/filtered out identically on both revisions.

Grant `preview:operate` for normal rows. Agent-only fixtures must have no running terminal IDs. Terminal fixtures must have a real running terminal process and registered terminal owner. Set configured local server URLs to empty: explicit configuration can make an unrelated server visible and conceal the ownership bug. Serve deterministic harmless content; no directory containing credentials or personal files. Keep Browser/terminal panels closed for overview frames, desktop sidebar open at its default 256 CSS px, and pointer outside all rows.

## Smallest complete visual fixture matrix

G = globe; T = terminal-running glyph. Expected icon differences below are source-derived, to be verified. `+1` is tooltip text only; multiple servers do not produce multiple globes or a count badge. Port numbers other than 5173 are allocations to confirm free before seeding; preserve whichever allocations are chosen across revisions.

| Row | Affected path; route/settings | Seed owned server data and adjacent state | Base → head expected result | Capture group |
|---|---|---|---|---|
| C-0 | Card; Q selected, row `proof-c0`; D | No servers or terminals owned by this thread; other fixtures still run | No G/T → no G/T, unchanged spacing | D overview |
| C-A | Card; Q then click actual lead thread; D | One Claude-owned python3 HTTP 200 server, 5173; `terminal=null`; no running terminal; new head `threadId=C-A` | No G/T → G only, `Open localhost:5173` | D overview + card recording |
| C-T | Card; Q then `proof-ct`; D | One terminal-owned HTTP server, 5273; matching terminal/thread IDs; one running terminal | T → G+T; tooltip `Open localhost:5273` | D overview + card recording |
| C-M | Card; Q then `proof-cm`; D | Two same-thread servers: agent 5373 and terminal 5374; one running terminal | T → G+T; tooltip `Open localhost:5373 (+1)`; click opens 5373 | D overview + card recording |
| S-0 | Slim; Q selected, `proof-s0`; D, settled | No servers or terminals owned by this thread | No G/T → no G/T | Same D overview |
| S-A | Slim; Q then `proof-sa`; D, settled | One Claude-owned server 5473, `terminal=null`, no running terminal | No G/T → G only; tooltip 5473 | Same D overview + slim recording |
| S-T | Slim; Q then `proof-st`; D, settled | One terminal-owned server 5573; one running terminal | T → G+T; tooltip 5573 | Same D overview + slim recording |
| S-M | Slim; Q then `proof-sm`; D, settled | Agent 5673 plus terminal 5674, both same thread; one running terminal | T → G+T; tooltip `Open localhost:5673 (+1)`; opens 5673 | Same D overview + slim recording |
| L-0 | Legacy; Q selected, C-0; L | Reuse C-0 | No G/T → no G/T | L overview |
| L-A | Legacy; Q then C-A; L | Reuse real lead 5173 task | No G/T → G only; tooltip 5173 | L overview + legacy recording |
| L-T | Legacy; Q then C-T; L | Reuse terminal 5273 | G+T → G+T; geometry and click behavior preserved | L overview + legacy recording |
| L-M | Legacy; Q then C-M; L | Reuse agent 5373 + terminal 5374 | G+T → G+T; base tooltip/open **5374**, head tooltip/open **5373 (+1)** | L overview + legacy recording |

L-M deliberately changes first port: at base the agent port is absent from discovery. Do not mark that expected change as a regression. C/S terminal-only rows prove that the new default indicator does not depend on agent attribution. Mixed ownership proves one globe, count suffix, deterministic first item, and coexistence with a terminal glyph; pure two-agent or two-terminal lists add no distinct render branch and can be collapsed.

## Guards and nonvisual obligations — no extra theme/viewport Cartesian product

| Obligation | Seed / check | Minimum retained evidence |
|---|---|---|
| Empty discovery vs empty per thread | First verify no owned listeners, then overview C-0/S-0 while other threads have listeners | Discovery snapshot + scoped DOM absence; empty per-thread visual already in overview |
| Permission guard | Existing scoped session without `preview:operate`, owned listener still present in discovery, for default and legacy | DOM absence of `Open localhost:*`; visual is equivalent to row 0. If no such session is available, report unverified, do not fabricate it |
| Old server compatibility | Payload omits `threadId`, contains terminal.threadId; explicit head threadId takes priority if both are present | Contract/client filter evidence. No new server or browser needed; record whether tested or source-only |
| Ownership isolation | C-A port must not show on C-0/S-0; terminal port keeps its original terminal association | Redacted subscription snapshot with environment/thread/terminal attribution and row mapping |
| Lifecycle / reverse state | Stop the fixture command or end its owning session using the lead's retained handles; wait for actual discovery removal | Include G disappearance and no stale tooltip in one existing recording; removal across restart alone is insufficient |
| ACP registration path | A real ACP-backed provider launches an owned server; thread ID reaches the scanner; root unregisters on close | One focused runtime transcript/snapshot. Reuses same visual representative; do not multiply screenshots by provider |
| Shared error/interrupt handling | Extracted legacy handler still ignores interrupted commands and reports real failures as `Unable to open preview` | Source-equivalence check retained. If a real opening failure is encountered, retain recording/toast; no invented error evidence |

No screenshot can by itself prove actual provider ancestry or cleanup. The new focused PortScanner test simulates the lsof/ps ancestry path and unregister; it does not prove Claude/ACP launch registration in the running app. This audit did not run tests. The complete diff adds no native mobile indicator. Desktop uses these web rows; Electron-specific preview opening and remote/tunnel URL resolution are unchanged downstream code, and are not claimed verified by a local web capture. Windows skips parent walking, and Claude's Windows spawn path does not add the POSIX registration: do not claim Windows descendant attribution from Linux evidence. No new Settings/palette/keybinding/MCP entry point is introduced. Record those scope limits in the final evidence manifest.

## Minimum capture set

Use outer browser **1440×1000** and **390×1000** CSS px, same DPR, zoom, locale and profile before/after. At 390 open the responsive sidebar sheet explicitly; it starts closed. This is narrow web coverage, not native-mobile proof. Verify the sheet's measured width; do not force its CSS to 256 px. Use the same viewport and scroll position for each paired frame.

For each revision, capture D (all eight C/S rows together) and L (four C rows) at both widths, in light and dark: **8 settled compositions per revision, 16 compositions total**. That is eight base/head comparison pairs. Use light/dark appearance overrides only after putting the product in follow-system appearance, and confirm computed theme/root classes; an OS override does not override an explicit product theme. Every row uses theme-dependent success color; terminal fixtures also use distinct light/dark teal colors, so both schemes apply to all overview groups.

Take two shots 500 ms apart per composition after fonts and state settle: **32 raw stills minimum**. If eight default rows do not all fit at these dimensions, retain a deterministic second scroll tile for each affected width/theme/revision. Never claim an offscreen/virtualized row was captured. A one-pixel scrollbar/overflow change is not grounds to silently crop it away.

Record each of card, slim and legacy at 1440 and 390 in dark on both revisions: **6 recordings per revision, 12 total**. Light interaction recordings are equivalent because handlers do not branch on theme; light settled frames cover appearance. Sequence each recording to cover one-server hover, multiple-server hover, pointer click from an inactive row, destination thread activation and Browser opening, close/back to sidebar, and keyboard activation without row drag/rename/double action. At 390, include opening the sidebar sheet and its dismissal or retained state after activation. Narrow mouse hover remains mouse evidence, not a claim about native touch.

Base card/slim contain no globe: record the same rows and hover regions and retain DOM absence, then end the unsupported click segment. Do not manufacture a before button or substitute a different entry point and call it the same interaction. Base legacy exercises terminal one/multiple; head also exercises agent one. Record the Browser empty-state discovered-server list and one ownership-removal transition within these existing recordings, resetting panels/state before the next paired overview. A changed Browser list is an affected downstream surface, not a fourth sidebar variant.

At head, the opened state must show the correct thread and requested port (the lowest owned discovered port), a loaded deterministic HTTP page, and no terminal drawer opening instead. Retain final open-state frames from recordings for human inspection; if using them for pixel comparisons, they must separately satisfy the still-settling contract. Compare matching action segments; base absence and head success are intentionally different endpoints.

## Evidence integrity and actual tool friction

1. Record a capture manifest outside the repository with full revision, route, alias→actual ID mapping, viewport/DPR, theme/palette, fonts, settings, seed snapshot fingerprint, frozen time, discovery payload, and file hashes. Keep public content synthetic and redact auth material, real chat text and sensitive process arguments. Persist exact fixture definitions so pass 2 can rebuild one row without reverse-engineering a screenshot.
2. Freeze the **page's displayed clock** at the same stated ISO instant on both sides using the supported `preview_evaluate` page context, if needed. Disclose the temporary Date/Date.now override and verify both reads; reapply after navigation/reload. There is no exposed browser clock-install API. Do not claim server time, performance.now or timers were frozen. Use fixed persisted timestamps and a quiet selected chat to prevent changing duration labels/transcripts. If the supported page override cannot stabilize a visible clock, the frozen-clock requirement remains blocked; do not hide it by unreported cropping.
3. Await `document.fonts.ready` and verify `document.fonts.status=loaded`; wait for actual discovery/DOM readiness and completed finite transitions, not just a generic delay. Move the pointer out of rows for overview stills, clear hover tooltips and text carets, and verify all target rows are visible.
4. **Actual limitation:** `preview_set_appearance` accepts only `colorScheme` (light/dark/system). It exposes no reduced-motion option. The lead reports `matchMedia('(prefers-reduced-motion: reduce)').matches=false`. This audit confirmed the exposed schema but did not open a browser to remeasure it. Do not pass a fictional reducedMotion field, monkeypatch matchMedia, or label CSS overrides as genuine reduced-motion coverage. The test-t3-app workflow requires the built-in Browser panel and forbids switching automation systems when unavailable.
5. Supported approach: retain unmodified live recordings for hover/click/open and pulse behavior. For settled stills only, use disclosed `preview_evaluate` access to pause the document's continuous Web Animations at a fixed currentTime after finite transitions settle. The terminal glyph uses `motion-safe:animate-status-pulse` (2 s), so clock freezing alone will not stabilize it. Log every paused animation target/name/time and use the same rule at base/head. Restore/reload before live recordings. This is animation stabilization, **not reduced-motion emulation**. If an embedded Browser surface or other process keeps repainting, do not claim stability until measured; closed-panel overview frames avoid that issue.
6. Require **Diff0**: decoded pixels of each same-revision pair taken 500 ms apart must be identical. Record changed-pixel count and dimensions, not merely PNG file-byte equality. Do not accept a nonzero thresholded comparison as Diff0. A failing stability pair must be diagnosed and recaptured, not silently masked.
7. For each of the eight base/head pairs, retain original images, an exact absolute pixel diff, and a thresholded diff. State the threshold in the manifest (proposed maximum-channel delta >16/255), changed-pixel count/bounds, and any explicitly approved masks. Use no masks by default. Expected change areas: added globe/necessary adjacent spacing; legacy M ownership target changes appear in hover/open evidence. Thresholding is supplementary; inspect raw diffs for color/spacing regressions below threshold. Row-0 and legacy terminal-only controls should be unchanged when state and stabilization match.
8. Inspect **every** raw image, diff image and retained recording endpoint, at readable scale. Check clipping, tooltip text/count, theme contrast, icon coexistence, correct active row, scroll/sheet geometry, and unintended panel/timeline changes. Hash equality does not replace the requested inspection. Upload evidence through ghdrop; never commit PR-only captures or scratch artifacts.

## FRICTION / handoff blockers

* Reduced-motion emulation is unavailable in the exposed tool. The supported alternative is real-motion recordings plus explicitly stabilized stills; genuine reduced-motion coverage remains unverified.
* Revision restart destroys provider fixtures. The lead must recreate and verify owner-root ancestry for both base and head through the same dev server, not assume a DB restore keeps them alive. Relaunched turns also change timestamps and chat history. `threadTimeTimestamp` reads `latestUserMessageAt ?? updatedAt`; a frozen page clock does not freeze those inputs. Demonstrate matching visible metadata before approving a comparison; if it cannot be reproduced through the allowed fixture workflow, mark the matching-data requirement blocked. Do not conceal mismatch by editing screenshot pixels or unreported DOM changes.
* The lead's actual thread/environment IDs, current settings, fixture snapshot and task handles were not supplied to this auditor. The matrix's aliases and spare ports are a seed specification, not an already-seeded environment. Bind and retain them before capture.
* Baseline default rows have no server button. Absence is the before evidence; an equivalent successful before click cannot be captured through this feature.
* Permission-denied, old-payload fallback, ACP runtime registration, remote/tunnel, Electron opening and Windows ancestry are not certified by source inspection or a Claude/Linux screenshot. Keep their measured/source-only/unverified statuses explicit rather than expanding the image matrix indiscriminately.

## Source anchors

* [Card/slim insertion and shared row slots](https://git.cafe/pingdotgg/t3code/blob/a327d35edf298ffe9e1353a1d80edfe21e32d82c/apps/web/src/components/sidebar/SidebarThreadRow.tsx)
* [Shared globe guards, tooltip, input handling and open action](https://git.cafe/pingdotgg/t3code/blob/a327d35edf298ffe9e1353a1d80edfe21e32d82c/apps/web/src/components/ThreadDiscoveredPortButton.tsx)
* [Legacy extraction](https://git.cafe/pingdotgg/t3code/blob/a327d35edf298ffe9e1353a1d80edfe21e32d82c/apps/web/src/components/LegacySidebar.tsx)
* [Thread ownership filter and old-terminal fallback](https://git.cafe/pingdotgg/t3code/blob/a327d35edf298ffe9e1353a1d80edfe21e32d82c/apps/web/src/portDiscoveryState.ts)
* [Ownership ancestry, probe policy, equality and registration](https://git.cafe/pingdotgg/t3code/blob/a327d35edf298ffe9e1353a1d80edfe21e32d82c/apps/server/src/preview/PortScanner.ts)
* [Focused ancestry/unregister test](https://git.cafe/pingdotgg/t3code/blob/a327d35edf298ffe9e1353a1d80edfe21e32d82c/apps/server/src/preview/PortScanner.test.ts)
* [Claude PID registration](https://git.cafe/pingdotgg/t3code/blob/a327d35edf298ffe9e1353a1d80edfe21e32d82c/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts), [ACP registration](https://git.cafe/pingdotgg/t3code/blob/a327d35edf298ffe9e1353a1d80edfe21e32d82c/packages/provider-acp/src/server/AcpSessionRuntime.ts), [service wiring](https://git.cafe/pingdotgg/t3code/blob/a327d35edf298ffe9e1353a1d80edfe21e32d82c/apps/server/src/server.ts)
* [Optional wire ownership field](https://git.cafe/pingdotgg/t3code/blob/a327d35edf298ffe9e1353a1d80edfe21e32d82c/packages/contracts/src/preview.ts), [AgentProcesses reference](https://git.cafe/pingdotgg/t3code/blob/a327d35edf298ffe9e1353a1d80edfe21e32d82c/packages/shared/src/AgentScope.ts)

Pass 1 stayed read-only with respect to source and app state: no captures, server/browser launch, source edits, commits, push, comments or merge. Only this external proof artifact was written and uploaded. Pass 2 should audit the actual evidence set against this matrix and independently rebuild one fixture row.
