# Evidence capture for [#24682](https://git.cafe/pingdotgg/t3code/pulls/24682)

Runtime evidence is complete for S1 and S2. Ready for independent evidence audit; this capture is not itself an independent audit or merge recommendation.

Baseline: [68d5b7ac](https://git.cafe/pingdotgg/t3code/commit/68d5b7ac735a5e95a945084430c6b5925873cc82). Integrated head: [accb7e75](https://git.cafe/pingdotgg/t3code/commit/accb7e75199340d332c6086502023cb468bf2c47). Original worktree stayed clean at the integrated head. Git-archive snapshots under /tmp were checked against 28,334 tracked blobs each; only GitHubRest.ts differs, identically instrumented in both snapshots. Exact patches and blob hashes are included. Instrumentation records physical sends inside the retry callback, after policy credential routing; only allowlisted noncredential fields are emitted. Server workspace dependencies resolve to each snapshot; installed third-party dependencies are reused.

## S1: actual mobile transport path

Fixture: public GitHub [#24544](https://github.com/pingdotgg/t3code/pull/24544), modified file `apps/mobile/app.config.ts`, selected from the returned first-page patch. No explicit commit or cursor. Fixture base [eea98953](https://github.com/pingdotgg/t3code/commit/eea989536849e5b5049987ee24318b5a77326542), head [9abc9d15](https://github.com/pingdotgg/t3code/commit/9abc9d15be4a0745dd1e6e12dd2252cf49d91c19).

Fresh independent T3 homes, `vp run dev --no-seed --home-dir /tmp/ev-24682/<sha> --auto-bootstrap-project-from-cwd`. No live userdata accessed or copied. An isolated test credential was generated in memory and saved mode 600; existing GitHub credentials were used through their normal path. A scratch GitHub-origin project was registered through projects.mutate in each isolated server. Browser-session exchange and one-use RPC websocket ticket stayed private. No browser or native UI was launched.

Initial patch: POST `/api/pull-requests/diff`. Expansion: real `pullRequests.diffFileContents` over `/ws?orchestrationProtocol=2`, using installed contracts, Effect RpcClient, JSON serialization and Socket. No invented mobile HTTP endpoint. Expansion completed within 60 seconds of initial read in both runs.

| Physical upstream HTTP requests | Before | After |
| --- | ---: | ---: |
| Whole-PR diff, Accept application/vnd.github.diff | 2 | 1 |
| Additional whole-PR diff during expansion | 1 | 0 |
| PR metadata, Accept application/vnd.github+json | 1 | 1 |
| Authoritative source, Accept application/vnd.github.raw+json | 2 | 2 |
| Total | 5 | 4 |

All nine physical sends completed HTTP 200; no observed retries, rate limits or fallback. `nextCursor=null`, `truncated=false`. Identical patch length 4,282,796 characters, estimated UTF-16 size 8,565,592 bytes (threshold 524,288), SHA-256 `8a78b370a375ce973b5661bb5c905a51762ffc8c01f4761c22f1457538a68ec6`.

Both WS responses returned old source 19,216 characters and new source 19,171 characters. Their hashes match each other and independent raw source reads at the exact base/head commits, recorded separately outside the measured server sequence. Source contents are omitted from uploaded evidence. Unrelated `.env` expansion failed with not-found in both runs and added zero upstream requests, including zero source reads.

Single sequence per revision. Initial HTTP elapsed: 1438→1425 ms; expansion: 1699→864 ms. These are individual network observations, not a general latency guarantee or a claim that GitHub/network caches were cold.

## S2: deterministic loader cancellation

Actual mobile loader source from each snapshot was run under Node's TypeScript support; only its trace import was replaced by an in-memory diagnostic sink. The harness uses promise gates, no sleeps or polling. Hold active.ts; queue cancelled.ts; abort subscriber; release active.ts; await source-dropped; request cancelled.ts afresh; hold replacement; join it before completion.

Before: aborted subscriber null, fresh caller null, fetch order `[active.ts]`. After: aborted subscriber null, replacement started, fresh and joined subscribers receive the identical authoritative SOURCE object, fetch order `[active.ts,cancelled.ts]`. The transcript includes source-dropped, source-queued, source-start and source-shared, proving that the previous finalizer does not erase replacement sharing. This is JS loader cancellation; it does not claim an already-sent WS RPC is cancelled.

## Isolation and limits

Existing focused suite: 150/150 passing across PullRequestService.test.ts, nativeReviewDiffSource.test.ts, nativeReviewDiffHighlighter.test.ts and nativeReviewDiffSurface.test.tsx. Test source hashes and SHA provenance are in manifest.json. No tests added or changed. A named isolation-only evidence run passed five existing cases: oversized rename/deleted cursor slices plus unrelated/half-rename denial; explicit-commit scope; credential fingerprint with same account ID; 61-second in-flight expiry; in-flight refresh/epoch. The verbose output's 89 skipped entries are filtered-out tests, not new quarantines. This second run exists to expose individual isolation gate names absent from the default 150-test summary.

S3 credential gate, S4 in-flight epoch gate, S5 existing cursor/rename/deleted behavior, S6 existing commit-direction gate, and S7 existing in-flight expiry are supported by those focused tests. Per maintainer direction, no added S5 page-order, S6 reverse-direction or S7 exact-TTL-boundary tests. S8 scope permutations and >128-slice eviction are not separately runtime-captured here; do not treat this report as new proof of every matrix subcase.

M1–M3 remain UNVERIFIED on both platforms. fs-15 has no adb/emulator/Android SDK/Java/Gradle; xcrun/iOS unavailable. No tooling installed, no Android build/JVM checks, no browser surrogate, no screenshots or videos manufactured. Unit tests do not establish native UI behavior.

## Cleanup and retained scratch

All capture servers stopped by individually captured spawn-tree PIDs; no pattern kills. Baseline tree: 1481398,1481404,1481432,1481476,1481511,1481512,1481524. After tree at cleanup: 1485931,1485937,1485965,1485977,1486012,1486013,1487297. The after sequence was served by PID 1486025, which exited on a watch restart after the sequence completed; its replacement 1487297 was captured and stopped. Failed first-launch PID 1480641 exited before a server started. All listed PIDs were confirmed absent or stopped; no surviving captured processes. Exact commands/cwds and cleanup method are in processes.json. Test and headless-client commands have exited.

Parent may remove the entire `/tmp/ev-24682` after audit. It contains snapshots, isolated per-SHA T3 homes, fixture-repo, public artifacts/harnesses, and private files: `private-credential`, `*-startup.private.log`, `*-patch.private.diff`. Private files and homes were excluded from uploads; never upload the directory wholesale. No PR artifacts committed; no production changes, push, comments, watchers, auth/credential/permission/runtime/MCP edits, or forbidden VITE origin variables.

## Friction

Category | What happened | What would have avoided it
--- | --- | ---
Tooling | fs-15 lacks adb/emulator/Android SDK/Java/Gradle; iOS unavailable | Prearranged authorized native device host; do not install here
Snapshot setup | First launch lacked scripts/node_modules and exited | Link all installed dependency scopes into disposable snapshots before launch
Protocol | Matrix suggested projects.list, which is absent from current WsRpcGroup | Inspect installed RPC catalog first; projects.mutate can register isolated scratch project directly
Auth client | Session exchange also expires a normal cookie; naively replaying its empty cookie caused 401 | Keep only nonempty session cookies from Set-Cookie
Socket adapter | Node WebSocket class was initially supplied where Effect expects a constructor function | Use Socket.layerWebSocketConstructorGlobal
Evidence provenance | Original matrix names an older head | Explicitly pin supplied integrated SHA and verify every snapshot blob
Watch process | After server restarted after completed capture, during later evidence checks | Stop capture servers immediately after client completion, before running further test tooling

Parent can merge latest main and rerun scoped tests. Recapture only if production changes affect the evidence. Independent capture audit remains the next step.
