# Issue 15140 real-app evidence

Before BASE: 3b95e7831b11b17627dff16843bc087bfca397ec.
After HEAD: 91197cb734f1b7cfc1ec440591eed38ff6e56dff.
Captured 2026-10-22 UTC. No PR created. Real Codex 0.163.0; GPT-6.1-Sol Low; Full access.

## Environment and comparability

Local web at http://localhost:8103. Native T3 browser, Chromium154 Linux x86_64 desktop UA, DPR2, root font16px, visualViewport.scale1. CSS viewports1440x900 and390x900. Explicit app Light/Dark themes. Whole viewport captured, chat scrolltop0, composer collapsed for narrow stills. Wide sidebar256CSS light,208CSS dark, matched per pair.

Native screenshot export cap1280px is implemented in apps/server/src/preview/ServerBrowserPage.ts (MAX_SCREENSHOT_WIDTH and CDP clip scale). Consequently1440x900CSS/DPR2 stills export1280x800PNG;390x900CSS/DPR2 exports780x1800PNG. Videos retain2880x1800 wide /780x1800 narrow. No images rescaled after capture. This is desktop browser width emulation, not native mobile.

Server, Vite and browser Date fixed2026-10-22T08:05:00.000Z; timers real. Both code versions restored identical pre-turn T3/provider snapshots, same project and thread IDs, exact prompt and local draft. Real provider latency differs; recordings are behavioral comparisons, not frame-aligned pixel diffs. Existing sidebar data copied into isolated home. No live home writes.

Route: /8a17860f-3c27-4a5f-8044-93154022e65d/3e6b8790-416b-4fe8-894c-cb7509147d32 . Title normalized through UI to Project MCP trust. Collapsed warning/answer positions are the intended visible change.

## Paired stills

Diff0 is amplified binary zero-tolerance proof: white if max absolute RGB channel difference >0; black otherwise. DiffT uses >16/255, also binary. Counts refer to exported PNG pixels. No blanket claim of zero differences outside the change.

| Row | Before BASE 3b95e7831b | After HEAD 91197cb734 | Diff0 (BASE→HEAD) | DiffT16 (BASE→HEAD) |
|---|---|---|---|---|
| N1 1440 light | ![before1440light.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/9a617f4eab680487/before1440light.png) | ![after1440light.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/4cf804afe5c45862/after1440light.png) | ![1440light-diff0.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/cf092459613f10d3/1440light-diff0.png) 4264 changed | ![1440light-diffT16.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/b0aa12e4a3c72887/1440light-diffT16.png) 3861 changed |
| N1 1440 dark | ![before1440dark.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/eab9cf8b2e373675/before1440dark.png) | ![after1440dark.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/33db7983638a30f4/after1440dark.png) | ![1440dark-diff0.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/b5582e89f6bdb42b/1440dark-diff0.png) 4272 changed | ![1440dark-diffT16.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/e9d9b1f4f46c861a/1440dark-diffT16.png) 3875 changed |
| N1 390 light | ![before390light.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/04d324aa24d06891/before390light.png) | ![after390light.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/fa1bbbc4127a18d2/after390light.png) | ![390light-diff0.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/4a7fa2e2a8b461da/390light-diff0.png) 6209 changed | ![390light-diffT16.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/42b4746aa862a12a/390light-diffT16.png) 5763 changed |
| N1 390 dark | ![before390dark.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/7bfbc6187a831aea/before390dark.png) | ![after390dark.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/c4137b712a75e468/after390dark.png) | ![390dark-diff0.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/9cdb7dc8cc1b5c47/390dark-diff0.png) 6170 changed | ![390dark-diffT16.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/e4a066625dc1acbf/390dark-diffT16.png) 5776 changed |
| N0 trusted control | ![beforeControl.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/a70e4ca87e36981d/beforeControl.png) | ![afterControl.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/836c7db842092b43/afterControl.png) | ![Control-diff0.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/d450afa727ec33a2/Control-diff0.png) 3215 changed | ![Control-diffT16.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/9f06f374371bad12/Control-diffT16.png) 2758 changed |
| N2 390 dark expanded | ![before390dark.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/7bfbc6187a831aea/before390dark.png) | ![afterExpanded.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/688eaf0a6e7f1eae/afterExpanded.png) | ![Expanded-diff0.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/f43ad94f7cc2caf6/Expanded-diff0.png) 24630 changed | ![Expanded-diffT16.png](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/820e3465fa6a761f/Expanded-diffT16.png) 22348 changed |

What else changed: N1 wide light/dark and narrow dark show the actual warning and displaced No answer/action timestamp. Narrow light additionally has a tiny sidebar-toggle icon raster difference. N2 adds expanded detail and larger answer displacement. Trusted control has Yes in both and no warning; its auto-generated title timing differs (BASE raw prompt title, HEAD Check MCP Tool Availability), plus small submit-button readiness/focus styling. The control is not pixel-identical. Sidebar ages are frozen; no additional age change was identified in the core pairs. Diff masks show all actual differences, without masking or normalizing pixels.

## Recordings and timestamp narration

No audio track was added. The following written narration accompanies each recording. Times are elapsed media seconds; ranges below are bounded using sampled frames, rather than claiming precise provider event latency.

| Row | Before BASE 3b95e7831b | After HEAD 91197cb734 | Narration / observed endpoint |
|---|---|---|---|
| T1 arrival | [beforeT1.mp4](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/b55880f6f5ece33e/beforeT1.mp4) 25.4032s; start08:18:48.061Z | [afterT1.mp4](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/b9b415c7c3704e61/afterT1.mp4) 23.283367s; start08:26:02.439Z | 0s prepared exact prompt; by5s sent. Decoded held-frame PTS: BASE14.989133s still Thinking,16.398167s No (completed by20s), held to end. HEAD4.908533s Thinking without warning;9.517100s warning visible (by10s);14.442333s No, one warning held to end. Provider timing differs. These bounds supersede the earlier incorrect BASE-by15s and HEAD-warning-by5s narration. |
| T2/T5 detail/reload | [beforeT25.mp4](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/2bc357724f9e205e/beforeT25.mp4) 5.969367s; start08:22:16.978Z | [afterT25-final.mp4](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/245389cf86775e64/afterT25-final.mp4) 64.956467s; start08:39:26.003Z | BASE has no warning to expand; reload preserves absence/No. HEAD exact verified timeline below. |
| T3 trust next turns | [beforeT3.mp4](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/d17b69adac84281f/beforeT3.mp4) 40.478933s; start08:22:58.287Z | [afterT3.mp4](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/a873ae98f30b9fbc/afterT3.mp4) 35.3048s; start08:29:04.111Z | Both begin from untrusted first-turn history. Trusted helper transition occurs outside public browser view at start; next same-thread catalog answer remains No (native session catalog caching), with no NEW warning. BASE trusted answer complete by10s; reverse untrusted transition at~29s, next turn Thinking at35–40.48s endpoint. HEAD trusted answer complete by15s; reverse at~26s; new warning visible by30s, final answer still Working/streaming at35.30s endpoint. Neither clip proves final reverse-turn completion. Fresh trusted N0 proves Yes availability. |

T2/T5 final HEAD media narration uses decoded frame PTS (sparse variable-frame-rate video, ~1fps average). Seeking with ffmpeg -ss can return the NEXT frame, so requested seek time is not a measured frame timestamp. Auditor decoded every frame to establish these bounds:

| Media time / bound | Narrated observation |
|---|---|
| 0s | Starts expanded from preceding inspection. |
| By11s, still collapsed at19s | Visible row header clicked to establish collapsed state; tool independently verified aria-expanded=false. |
| By22s | Expanded full reason, readable project and provider-home trust instruction. |
| By32.974700s | Collapsed again; verified aria-expanded=false. |
| 32.974700–40.275133s | Reload transition is bounded by the last collapsed frame and first blank-loading frame. |
| 40.275133s /40.359s | Blank loading, then splash. |
| 45.684267s /48.725667s | Splash, then partial page. |
| 49.947667s | Restored page, persisted notice collapsed. |
| ~50.62s; settled51.02s | Explicit postreload expansion; aria-expanded=true, height150.48CSS verified. |
| 51.02–64.92s | Complete recovery reason held visible through final decoded frame. Video duration64.956467s. |

Separate wall-clock action log (UTC, not inferred media PTS): recording start08:39:26.003; header collapse08:39:35.975; expand08:39:46.478; collapse08:39:58.515; reload08:40:05.858; postreload expand08:40:16.613; stop08:40:30.912. Tool call times and sparse media frames have different precision. Neither is relabelled as the other.

The earlier afterT25.mp4 and afterT25-corrected.mp4 are superseded; their collapse/final expansion sequence was insufficient. Clicking the inspector center can hit detail instead of the header; final recording uses the actual visible header and explicit state checks.

Safe trust transitions changed only disposable config.toml. No credentials or editor/terminal config content appears in recordings. Trust was restored UNTRUSTED; root then restored first-turn checkpoint for independent audit. No provider turns were issued after that restore.

## Coverage and limitations

Captured required: N1 all4 width/theme pairs; N0 trusted pair; N2 expanded pair; T1 arrival pair; T2/T5 detail/reload pair; T3 trust-transition pair with explicit endpoint limitation.
Missing required conditional row: **N3 stacked notices UNCOVERED**. Real fixture exposes one disabled project layer; no real nested layer or labelled protocol harness capture was produced. Source review is not substituted for this row.
Additional unverified: N4 whitespace harness, deterministic T4 failures/races, T6 goal continuation, remote/relay/tunnel runtime, native Windows desktop, native iOS/Android expansion/copy. Narrow web is not native proof.
T3 final untrusted turn completion is not captured. It is not claimed by the stills or descriptions.
No duplicate provider tests/checks run by capture worker. Parent separately reported79 adapter tests, targeted lint and server typecheck passing.

## Reproduction and audit

Committed credential-free fixture:
scripts/fixtures/seed-codex-project-mcp.py

Run from HEAD checkout:
`python3 scripts/fixtures/seed-codex-project-mcp.py /tmp/issue15140-capture-fixture`
(destination must not already exist).

It creates project/.codex/config.toml, local Python stdio MCP server, trusted/untrusted provider homes and freeze-clock.mjs. It starts nothing and contains no credentials. Operator privately authenticates disposable homes using their own authorized provider setup; never publish homes/config/auth. Select generated project/ in T3, set Codex homePath to generated untrusted home, Full access, GPT-6.1-Sol Low. Create a fresh thread BEFORE send and snapshot isolated T3/provider homes while stopped. Send verbatim:

> Do you have the issue15140_probe MCP tool available in your tool catalog? Answer yes or no from the catalog only. Do not inspect files or run shell commands.

For both versions restore exact pre-turn state, use the same origin and clock, then send the same prompt. Set explicit Appearance Light/Dark, width/height, scrolltop0 and collapsed notice/composer as above. Use trusted home with fresh native thread for N0; existing-thread tool catalogs can remain cached.

Audit target: N1 390dark, collapsed, same route above; first-turn restored snapshot contains exactly prompt + warning + No. Reference local PNG /tmp/issue15140-evidence-assets/after390dark.png (780x1800), source native artifact browser-screenshot-localhost-mvja38rl-7fb8d546.png. Auditor independently rebuilt the narrow row in a separate tab: initial mismatch preserved (notification dismissal and hover-state difference); settled matched-hover capture reported zero differing pixels at both thresholds. This is the auditor's result, not a capture-worker claim of independent execution. Capture uses real persisted provider reason, not seeded notice or direct DB mutation. No raw DB, auth, configs, protocol bodies or pairing URLs uploaded.

FRICTION browser|expanded inspector center click does not toggle header|use visible row header and verify aria-expanded after each action.
FRICTION export|native screenshots cap width1280|label CSS viewport, DPR and actual export dimensions separately.
FRICTION infrastructure|BASE startup/dependency and owned-service memory throttling delayed capture|root repaired isolated dependency layout and owned service limits; worker started no servers.
Active commands: none (parent-owned HEAD server remains running).
