# Missing-row evidence for [#32359](https://git.cafe/pingdotgg/t3code/pull/32359)

Scope: F06, F13, F15, F17 only. Server-only change; no client render path, viewports, themes, screenshots, or agent turns. Synthetic data only. No existing T3 database was read or seeded.

Before: [7389423](https://github.com/pingdotgg/t3code/commit/7389423507aff8f931fdd41fd0b752589d2d810e), the requested merge-base on main. After: [bff5e1c](https://github.com/pingdotgg/t3code/commit/bff5e1cba9b94ab1a95dde3f8a211fb941547e31), the requested PR head.

Environment: Node v26.12.1; Linux x86_64, kernel 6.8.0-117-generic; fresh-server ports before 39105 / after 37751. Each server ran its own checkout's source through `node apps/server/src/bin.ts serve --base-dir <new scratch directory> --port <port>` with `T3CODE_DEV_SEED=0`. The before checkout was created at the requested SHA and installed with `vp install`. The same runtime and harness drivers ran against both revisions. Each runtime driver restarted its own process once after project creation to make the first MCP list genuinely cold; it reused only that revision's own freshly created synthetic database.

## F06 — failed refresh and recovery (labelled service harness)

| Before | After |
| --- | --- |
| [7389423](https://github.com/pingdotgg/t3code/commit/7389423507aff8f931fdd41fd0b752589d2d810e): [before-harness.md](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/d13fced0defb42b8/before-harness.md) | [bff5e1c](https://github.com/pingdotgg/t3code/commit/bff5e1cba9b94ab1a95dde3f8a211fb941547e31): [after-harness.md](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/f2bf9dbd99a3f940/after-harness.md) |

Look for `failed-refresh-fallback`, `four-seconds-into-failure-TTL-no-retry`, `recovered-I2`, and `I2-after-next-positive-TTL`. Before returns null while expired/failed; after retains complete I1 with `repositoryIdentityResolved=false`, then returns I2 with true and remembers it through its next expiry. Failure notifications remain unresolved/null; direct reads during the failure TTL remain null on both sides. TestClock advances 61 s, then 4 s and 2 s around the 5 s failure TTL. Deferred and PubSub milestones hold the refresh and recovery; no sleeps or polling.

## F13 — capacity eviction and update order (labelled service harness)

| Before | After |
| --- | --- |
| [7389423](https://github.com/pingdotgg/t3code/commit/7389423507aff8f931fdd41fd0b752589d2d810e): [before-harness.md](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/d13fced0defb42b8/before-harness.md) | [bff5e1c](https://github.com/pingdotgg/t3code/commit/bff5e1cba9b94ab1a95dde3f8a211fb941547e31): [after-harness.md](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/f2bf9dbd99a3f940/after-harness.md) |

The permitted capacity-two substitute avoids manufacturing 513 API projects and trying to observe an uncontrolled, very short refresh interval. Warm A/B, expire and replace A (B remains remembered), insert C, then expire: B is the oldest and reads null while A-v2/C remain remembered. B stays null during a Deferred-held refresh. Later successful-null C frees its slot, so B-v2 and D both survive the next expiry. Both harness cases passed on each revision with side-specific expected outputs. This is not a claim of a 513-project real-server capture.

## F15 — WebSocket identity and resolved-root markers

| Before | After |
| --- | --- |
| [7389423](https://github.com/pingdotgg/t3code/commit/7389423507aff8f931fdd41fd0b752589d2d810e): [before-websocket-expiry.md](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/28319fbe0e401539/before-websocket-expiry.md), [before-runtime.md](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/cef79ce8bf18383f/before-runtime.md) | [bff5e1c](https://github.com/pingdotgg/t3code/commit/bff5e1cba9b94ab1a95dde3f8a211fb941547e31): [after-websocket-expiry.md](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/b5184e4973b0bb07/after-websocket-expiry.md), [after-runtime.md](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/de2afbc541db850b/after-runtime.md) |

Full timestamped WebSocket RPC frames, including all project fields, are recorded. The subscription method is `orchestration.subscribeShell` over `/ws?orchestrationProtocol=2` authenticated with a short-lived websocket ticket. Initial subscriptions expose stale metadata: absence of `resolvedRepositoryIdentityRoots` means no root is marked fresh in that authoritative frame; a following marked snapshot names the freshly resolved roots. Resume intentionally filters unmarked metadata, so resume captures alone cannot expose a stale identity. The supplement uses initial subscriptions for two separate 61-second windows. At 16:47:54.783/16:48:55.927 before sends null identities in the first unmarked frame; at 16:47:55.081/16:48:56.300 after retains widgets/peer. Each is followed by a marked fresh frame. See the exact timestamps in the full transcripts. The primary transcript also records resume/reconnect, then successful-null snapshots after origin removal and real inner-cache expiry.

## F17 — MCP list, cold/expired/refreshed and real replacement

| Before | After |
| --- | --- |
| [7389423](https://github.com/pingdotgg/t3code/commit/7389423507aff8f931fdd41fd0b752589d2d810e): [before-runtime.md](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/cef79ce8bf18383f/before-runtime.md), [before-mcp-replacement.md](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/397a535881a459ff/before-mcp-replacement.md) | [bff5e1c](https://github.com/pingdotgg/t3code/commit/bff5e1cba9b94ab1a95dde3f8a211fb941547e31): [after-runtime.md](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/de2afbc541db850b/after-runtime.md), [after-mcp-replacement.md](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/41801de61856f5af/after-mcp-replacement.md) |

The real `/mcp` endpoint was reached. Each transcript records a normal bearer-session probe rejected with HTTP 401 `invalid_mcp_credential`; the driver then registers a synthetic OAuth client, approves it with a CLI-created one-time pairing credential and PKCE, and obtains the MCP client session accepted by the server. No handler fallback was necessary. `tools/call` invokes `t3_project_list` and records the complete response at cold start, after outer-cache expiry, and after refresh. The replacement supplement waits beyond the actual 15-minute inner resolver cache after changing the synthetic peer remote from `acme/peer` to `acme/peer-v2`. At 16:57:00.113 before immediately returns null/widgets and peer-v2; at 16:57:01.133 after returns remembered widgets and peer. After the fresh milestone, 16:57:02.065 returns null/widgets and peer-v2. These are real endpoint responses, not inferred handler behavior. It records the first list and a second list after a WebSocket fresh milestone. This establishes the list values used as repository-match input; no remote agent or zero/one/multiple-peer selection is launched.

## Scripts and reproduction

- [ProjectEnrichmentService.evidence.test.ts](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/71a9a4f06cada018/ProjectEnrichmentService.evidence.test.ts)
- [harness-driver.mjs](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/4073dd268caf4572/harness-driver.mjs)
- [runtime-driver.mjs](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/73af505c3e5ab8d1/runtime-driver.mjs) — server lifecycle and complete synthetic seed
- [ws-expiry-driver.mjs](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/f0ba2b4c01413d23/ws-expiry-driver.mjs) — two initial stale/fresh WebSocket windows
- [mcp-replacement-driver.mjs](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/c3764de2cc764539/mcp-replacement-driver.mjs) — real MCP I1→I2 replacement
- [summarize-evidence.py](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/df9599bc49d1a7f4/summarize-evidence.py) — compact frame extraction
- [validate-evidence.py](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/faf6c314fb958304/validate-evidence.py) — evidence assertions and public-artifact privacy scan

For F06/F13, copy the evidence test to `apps/server/src/project/ProjectEnrichmentService.evidence.test.ts` in each checkout, run `node <scratch>/harness-driver.mjs <checkout> before|after`, then delete that literal temporary test path and check `git status --porcelain`. The runner records the exact SHA, command and output. The script is evidence only and is never committed.

For F15/F17, run `node <scratch>/runtime-driver.mjs <checkout> before|after`. It creates a new timestamped directory, fresh server home, two git repositories and their API project records, authenticates, starts/stops only its own captured server PID, and records the primary transcript. Once `SYNTHETIC_REMOTE_CHANGE` appears in that run's transcript, run both `node <scratch>/ws-expiry-driver.mjs <checkout> before|after <run-directory>` and `node <scratch>/mcp-replacement-driver.mjs <checkout> before|after <run-directory>` while the primary driver remains active. The supplements calculate their own deadlines from that line. Keep the original scratch root or adjust its constant consistently in the scripts. `summarize-evidence.py` extracts compact observations from complete frames, and `validate-evidence.py` checks the captured milestones and scans public artifacts for private model/token candidates.

## What else changed / limits

- The commit diff contains only `ProjectEnrichmentService.ts` and its tests. No TTL, resolver, contract or transport code changed. No production files were edited during capture.
- Fresh run directories, ports, server PIDs, auth sessions, creation timestamps and root paths necessarily differ across sides; the two synthetic project IDs, titles, origins and driver logic are the same. Provider turns were not started; model selections are null.
- The deterministic harness uses a synthetic resolver defect and capacity two on both sides. These are explicit fixture controls, not production settings or server observations.
- Remote removal/replacement is applied identically to both sides. The existing inner 15-minute cache initially hides it, then fresh resolution yields null/widgets and peer-v2.
- Captured **4/4 requested rows**. Unverified rows: **none**. F06 and F13 use the explicitly permitted labelled harness substitutes; F15/F17 use real servers and wire responses. The harness passed two cases on each revision, and the evidence validator passed all captured milestones.
- Final successful-null WebSocket frames at 16:57:54–16:57:55 include both roots in `resolvedRepositoryIdentityRoots`, while widgets has null identity and peer has peer-v2.
- Cleanup verified at 16:59 UTC: all four captured server PIDs (initial processes and their cold-cache restarts) exited; the before worktree was removed with the requested command; the after worktree is clean at the requested SHA. Final free space: 105 GB. Nothing was committed or posted to the PR.
- Capture and cleanup duration: about 22 minutes, within the 90-minute budget.

FRICTION: runbook | Resume subscriptions suppress unresolved metadata, so the first resume-only captures could not show the stale interval; initial subscriptions supplied it. | Specify an initial subscription after expiry for stale-frame proof.

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