# Proof re-audit (pass 3): #22999 / GitCafe #23002

Scope: the [new captures-index.md](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/4430c482f048008f/captures-index.md), checked row by row against the [approved matrix](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/d062c0e98b079ed9/pr-22999-matrix-final.md) and the [prior audit](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/b516fcde00d38904/pr-22999-proof-audit-pass2.md). Before is [680152e27b](https://github.com/pingdotgg/t3code/commit/680152e27b2a961686389ca69a1557cbb2c55f08) and after is [db94329f89](https://github.com/pingdotgg/t3code/commit/db94329f8965c9893796d82bb78a92c93764ef5f).

This pass was read-only:
- No forge commands were run.
- No code or settings were edited.
- No servers were launched or stopped.
- No browser tabs were opened.

Evidence completeness is judged separately from whether the behavior passes. Failing expectations are listed as product findings below.

## Baseline checks

- **Worktrees.** Both are clean. HEADs are db94329f89 (after) and 680152e27b (before), and before is an ancestor of after. The diff is the same four files (+444/−71).
- **Upload integrity.** I downloaded all 22 artifacts linked from the index: 16 MP4s, 4 PNGs, the metadata JSON and `static-diff.json`. Each is SHA-256 identical to its local copy in `/tmp/pr-22999-evidence/v2`, and the index itself is identical too. The uploaded set is the set I audited.
- **Budget.** There are exactly 16 clips and 1 static pair, with no grouped samples.
- **Viewport, DPR and theme.**
  - All 16 clips have environment metadata. For R3–R6 it sits in `result.value.env`. Every row's viewport, DPR 2 and theme match the matrix.
  - Rasters are 2880×1800 for the 1440 rows and 780×1688 for the 390 rows.
  - R1's previously missing environment record is now present.
- **Capture state.** `capture: true` with pointerId 1 is recorded for R3–R6. R3 now also logs capture after blur: true in before, false in after.
- **Post-release geometry.** Every R3–R6 recording now has `postNativeRelease`, and it equals the logged final rest in all eight.

## Decoding method

I decoded every frame of all 16 clips with cv2, reading `CAP_PROP_POS_MSEC` per frame.
- My PTS lists match the capturer's internal `pts.json` to within 0.03 ms.
- I read the burned label on every frame from full-resolution crops.
- I measured the fixture header's bounding box on every frame.
- I made visual key-frame sheets for R1 and R5.

**Narration timing now matches.** Every narrated time in the index is the PTS of the first decoded frame that shows that phase label, in all 16 clips. Every clip opens with its own row's Rest label, so the stale-overlay issue is gone. The burned script-clock values are now labelled as script clock, not seek time. The prior seek offset of 0.5–0.75s is fixed.

## Row by row

| Row | Decoded frames | Matrix observable | Completeness |
| --- | --- | --- | --- |
| **R1** NW normal, 1440/light | The cursor sits on the NW corner at 1.808 (before) and 2.097 (after). The label flips to "Native NW grow" at 1.896 / 2.187. The resized frame appears at 2.377 / 3.071, with its NW corner under the cursor. "Released" shows at 2.515 / 3.279, then nothing changes through the end of the clip (3.637 / 4.186). | Final (880,400) 340×213 in both revisions. That is exactly start + (−20,−12) along the frozen 1.6 ratio, which gives 212.5 (bottom 612.5). The 613 bottom is the same rounding in both revisions. The right edge holds at 1220, the ratio is 1.596, and the frame stays above the minimum. Passive motion is inert. | SUFFICIENT |
| **R2** E normal, 1440/dark | The label flips at 1.615 / 1.700 and the edge grows over the next frames. "Released" at 2.149 / 2.237, then static. | 340×213 in both. The left edge holds at x=900, the ratio is stable, and passive motion is inert. | SUFFICIENT |
| **R3** NW blur, 1440/dark | Before: the held move grows the frame to 355×222 by 2.806. Blur shows at 3.221. The held `buttons=1` moves after blur (3.990→4.512) grow it to 460×288 with capture still true. After: the frame reaches 355×222 by 2.904. Blur shows at 3.259. With capture=false the frame holds 355×222 through 6.615. | **The confound is resolved.** The post-blur moves now hold the button down (`buttons:1`), so the after code's buttons=0 branch cannot be what stops the gesture. In source, blur calls `finish()`, which sets `gesture = null`, so later moves are dropped by the `!active` guard. Blur therefore finalizes the gesture. Capture release is observed as false in after. The frame stays reachable (bottom 612) and later motion is inert. | SUFFICIENT |
| **R4** E missing release, 390/light | Before grows during the held move (to about 280) and keeps growing through the `buttons=0` frames 2.377→2.888 to 318×199. After reaches 280×175 by 1.789 and holds through 4.865. | The missing-button move terminates, the frame fits, and later motion is inert. | SUFFICIENT |
| **R5** NW source transition, 1440/light | The source change label shows at 0.963 / 1.109. The source change alone pushes the bottom edge down about 40px in both revisions before any move. The NW growth frames follow, then pointerup at 2.914 / 3.068. Visually, before's footer returns to its original line and after's footer ends lower. | Logs match the frames: before ends (886,361) 334×251 with bottom 612, after ends (865,390) 355×266 with bottom 656. Termination leaves a reachable frame. **The "rendered ratio stable" expectation fails in both revisions** (1.33 at rest). **After loses the opposite-corner anchor by 44px; before does not.** The index now states both plainly as adverse findings. | SUFFICIENT (the evidence shows a failure) |
| **R6** E source transition, 390/dark | The source change shows at 0.559 / 0.700. Growth frames follow, then pointerup at 2.275 / 2.380, then static. The frames are identical across revisions. | Both end (60,332) 280×210. The chosen width 280 (start + 20) is kept and no gesture stays active. **The "frozen gesture ratio" expectation fails in both revisions** (rendered ratio 1.595→1.333). The index discloses this. | SUFFICIENT (the evidence shows a failure) |
| **R7** NW normal, 390/dark | The label flips at 1.225 / 1.230. The frame is resized by 1.588 / 1.593, with the corner under the cursor. Release, then static. | 280×175 at (40,320) in both. The opposite corner holds at 320/495 and the frame stays above the 240×150 minimum. | SUFFICIENT |
| **R8** E normal, 390/light | The label flips at 1.236 / 1.249. The frame is resized by 1.635 / 1.653. Release, then static. | 280×175 in both. The left edge holds at x=60 and the frame stays above the minimum. | SUFFICIENT |
| **S1** static, 1440/light | n/a | Files are unchanged from the prior pass (same SHA-256). Recomputed: **10,760 / 1,024,000 nonzero, 962 above 16, max 71, mean 0.042699**, which matches `static-diff.json` exactly. Pixels above 16 fall in PNG bbox 824,372–1060,437, inside the stream. **0 pixels differ outside the player.** | SUFFICIENT |

### Is a single native step enough for R1, R2, R7 and R8?

Yes, for this matrix.

The prior objection was that clamping to the minimum size hid the start state. With a 20px outward step from a 320×200 (or 260×163) start, the end is 340×213 (or 280×175). Both are above the 240×150 minimum, so the final geometry is unclamped and equals start + delta exactly, with the opposite edge fixed. A baseline jump at first movement would show up as an arithmetic mismatch in the final geometry, and there is none in either revision.

The decoded frames also show three states for each row:
1. The cursor on the rendered corner before the step.
2. The cursor at the target before the frame catches up.
3. The resized frame with its corner under the cursor.

That is enough to verify the matrix observable.

The single step leaves two things open:
- **Intermediate motion.** There is no multi-step trajectory, so these clips cannot catch a jump that later corrects itself mid-drag.
- **Before/after discrimination.** The fixture's geometry is integral, so these rows do not discriminate the PR's fractional-geometry read from the old path. Both revisions are expected to pass, and both do.

The matrix asks for neither, so these are sampling limits, not evidence gaps.

## Behavioral findings (retain; do not frame as passing)

1. **R5, after-only regression: the opposite edge drifts.** After a source sizing change during an NW resize, after ends with bottom 656 against 612 at the start (+44px). Before re-anchors to 612.
   - Mechanism: `finish()` in `previewMiniPlayerGesture.ts` resolves the end position with `source: active.frame`, which is the frozen pre-change height. Meanwhile `store.resize` persists the width only, and the shell re-derives height from the live source ratio.
   - Not tested here: whether a frame near the bottom obstacle could overflow. That is a plausible follow-on risk.
2. **R5 and R6: the rendered ratio is not frozen when the source changes. This affects both revisions, so it is not a regression.**
   - The PR comment "Freeze the rendered aspect ratio for the entire gesture, even if the source changes" only holds for the movement math. The rendered frame follows the live source: 1.6→1.335 in R5 and 1.595→1.333 in R6.
   - The matrix observables "captured rendered aspect ratio remains stable" (R5) and "frozen gesture ratio" (R6) are **not met**.
3. **Fixed by the PR (R3 and R4):**
   - Blur now ends the gesture and releases capture. Before kept resizing on held moves after blur.
   - A missing release (`buttons=0`) now terminates the gesture. Before kept growing.

## Remaining minor issues (non-blocking)

- R3's row title still reads "NW blur + missing release". The pointerup is omitted, so the title is technically true, but the row now isolates blur.
- R5 and R6 show no separate "Native pointerdown; capture=true" frame. The capture state is folded into the source-change label, and the index narration matches what is shown.
- The index's "What else changed" section does not repeat the `finish()` stale-height clamp mechanism. The R5 caption does explain the cause in prose.

## Static rebuild

I did not re-run a live rebuild in this pass, for three reasons:
- The S1 PNGs are byte-identical to the previous pass.
- The before server's new tab previously streamed blank frames.
- Rebuilding would mean opening new Browser tabs and minting pairing tokens, beyond a read-only audit.

Instead I recomputed against the prior rebuilds in `/tmp/audit2`:

| Comparison | Nonzero | Above 16 | Max | Outside the player |
| --- | --- | --- | --- | --- |
| Rebuilt after vs evidence after | 13,000 | 1,568 | 78 | 588 px, max 8 (sidebar-toggle hover residue) |
| Rebuilt before vs evidence before | 32,357 | 28,280 | 227 | 588 px, max 8 |
| Rebuilt before vs rebuilt after | — | — | — | 0 |

The rebuilt-before row is invalid stream content: the blank stream, all of it inside the player. Outside the player, the before and after rebuilds are identical. The resting chrome, placement and composer reproduce, and the only static deltas are stream raster.

## FRICTION

- cv2 has no OCR, so I read the labels visually from crops of the full-resolution frames. The animated blue cursor overlay pollutes the header-colour mask on the left edge, so I cross-checked geometry against the metadata and visual sheets.
- A fresh static rebuild was skipped for the reasons above. The numbers come from the prior pass's rebuilds and are labelled as such.
- I used no forge, server or browser actions. The only writes are scratch files under `/tmp/audit3` and the upload of this report.

VERDICT: SUFFICIENT
