# Iteration 8 — Phase 7c (α-2 verification + 5-codec regression sweep)

Captured 2026-05-13. Backend SHA `b6a3958a5bca9451…` (iter8 P6 γ + IMP-1 + α-2 POC strip removal). Fork tip `0226684`.

## Verdict on α-2

**α-2 (remove POC sentinel strip) did NOT fix Bug 4.**

| Metric | Before α-2 | After α-2 |
|---|---|---|
| H.264 libva YUV hash | `71ac099b…` | `71ac099b…` (unchanged) |
| Frame 1 non-zero bytes | 512 (16×32 leak) | 512 (same pattern) |
| Frame 2 non-zero | 32 sparse UV markers | 32 same |
| Frame 3 non-zero | 16 sparse UV markers | 16 same |

α-2 DID change the wire-level POC bytes (now matches kdirect's sentinel-encoded `0x00010000` for tfoc/bfoc instead of libva's previously-stripped `0`), but the kernel produces identical output. **POC values are not load-bearing for Bug 4.**

## What Phase 7c eliminated

This iteration's α-2 attempt eliminated one more hypothesis. Combined with prior phases:

| Hypothesis | Status | Eliminated by |
|---|---|---|
| libva mis-reads CAPTURE buffer | ❌ Eliminated | γ dump shows libva reads what's in buffer |
| Slot-binding wrong | ❌ Eliminated | γ shows correct slot index per surface |
| Stale residue from prior runs | ❌ Eliminated | IMP-1 memset experiment (same hash) |
| `constraint_set_flags` (SPS byte 1) | ❌ Eliminated | Phase 5b CRIT-1: rkvdec source review |
| H.264 POC sentinel strip | ❌ Eliminated | α-2 changed wire, no output change |

## What remains as candidate

DECODE_PARAMS / DPB construction beyond POC and flags:

1. **DPB entry ORDER**. Phase 4c strace re-decode shows libva fills DPB in entry-array order (entry[0]=frame_1, entry[1]=frame_2), kdirect fills in short_ref[] order (DPB[0]=most-recent-ref = frame_2). May or may not matter; rkvdec docs unclear.
2. **DECODE_PARAMS fields after dpb[16]** (bytes 512–559 of the 560-byte struct). Phase 3 didn't deep-compare these. Includes nal_ref_idc, frame_num, current frame's tfoc/bfoc, pic_order_cnt_bit_size, dec_ref_pic_marking_bit_size, delta_pic_order_cnt*, flags.
3. **PPS field-by-field**. Phase 5b IMP-2 flagged Phase 3 only shallow-compared 12 bytes. Full byte diff needed.
4. **Slice data encoding**. The OUTPUT bitstream bytes the kernel sees per slice. Phase 3 didn't validate this.

## 5-codec regression sweep on α-2 backend

| Codec | Anchor (iter7) | iter8 α-2 hash | Verdict |
|---|---|---|---|
| H.264 | `71ac099b…` | `71ac099b…` | unchanged (Bug 4 still present) |
| HEVC | `06b2c5a0…` | `06b2c5a0…` | unchanged (Bug 5 still present) |
| VP9 | `4f1565e8…` | `4f1565e8…` | unchanged (PASS direct) |
| MPEG-2 | `19eefbf4…` | `19eefbf4…` | unchanged (PASS) |
| VP8 | `bcc57ed5…` | `bcc57ed5…` | unchanged (Bug 6 still present) |

**Zero regression on the 4 non-H.264 codecs.** α-2 is a hygiene cleanup that aligns libva's wire payload more closely with kdirect's, even though it doesn't close Bug 4.

## Iter8 criteria final reckoning

| # | Criterion | Verdict |
|---|---|---|
| 1 | libva H.264 == kdirect H.264 | **FAIL** (PARTIAL — Bug 4 narrowed but unfixed) |
| 2 | VP9 unchanged | PASS |
| 3 | MPEG-2 unchanged | PASS |
| 4 | HEVC unchanged | PASS |
| 5 | VP8 unchanged | PASS |
| 6 | Control-payload anchors hold for 4 non-H.264 codecs | PASS (4 codec hashes unchanged ⇒ controls unchanged) |

**5 of 6 PASS.** Criterion 1 PARTIAL: Bug 4 significantly narrowed (3 hypotheses eliminated) but not fixed.

## Lasting code changes from iter8

The fork now carries 4 commits beyond iter7:

| SHA | Description | Ship status |
|---|---|---|
| `7eae6ea` | γ env-gated CAPTURE buffer diagnostic dump | Diagnostic, env-gated off |
| `66ecbef` | IMP-1 env-gated CAPTURE pre-zero diagnostic | Diagnostic, env-gated off |
| `6f4e583` | stdlib.h include for picture.c | Mechanical fix |
| `0226684` | α-2: POC sentinel strip removal | Hygiene cleanup, no behavior change |

All four commits are zero-regression by env-gating or by being mechanically necessary. Net effect on default behavior: nothing changed except internal POC values sent to rkvdec are sentinel-encoded (which doesn't change decoder output for any of 5 codecs).

## Backend SHA chain

| Stage | SHA |
|---|---|
| iter7 close | `520507f6d0a1a7eb3797bed42c6f74e0f3a4826ac8a22ed2655e01a6f20aa874` |
| iter8 P6 γ | `94467f0c0aaba804233206d01666302e42a5cbcaf513a8157045623a66fae58e` |
| iter8 P7 IMP-1 | `e4649a4854a24ec3209ea5ce6705cef8ed9b80cb277701ecb34eb822516c6030` |
| iter8 P6c α-2 | `b6a3958a5bca945164262339dea5cc28f17accce13d57bd9f0c5a5dabbdf1b53` |

## iter8 → iter9 handoff

iter8 closes PARTIAL with significant Bug 4 narrowing. Three hypotheses eliminated (constraint_set_flags, POC strip, libva-readback). iter9 candidates:

- **α-3 PPS full-byte diff**: re-strace and dump all 12 bytes of PPS for libva vs kdirect. If diff exists, target it. Cost: 30 min for strace + analysis + small fix.
- **α-4 DPB entry order**: change `h264_fill_dpb` to fill in most-recent-first order matching kdirect. Cost: ~10 LOC + verify.
- **α-5 Slice data encoding**: deep-dump the OUTPUT buffer bytes libva writes vs kdirect's. Look for NAL-header / start-code / EBSP differences. Cost: 60 min + maybe small fix.
- **α-6 DECODE_PARAMS post-DPB fields**: re-strace with `-s 1024` to capture full DECODE_PARAMS; diff bytes 512-559. Cost: 30 min.

Recommended order: α-3 (cheapest) → α-6 → α-4 → α-5.

## Lessons distilled

1. **Reviews caught a dead-end early**. Phase 5b CRIT-1's empirical rkvdec source read saved a ~15 LOC futile change. Memory rule reinforcement: `feedback_review_empirical_over_theoretical.md` direction (reviewer reads source > author's plan-level speculation).
2. **Wire-byte changes aren't always behavior changes**. α-2 visibly changed the V4L2 ioctl bytes (POC values flipped) but the decoder output was identical. Wire-level diffs are necessary but not sufficient.
3. **γ-then-α was the right empirical sequence**. The γ dump confirmed libva-readback wasn't the issue; without it, iter9 would still be considering libva-side fixes.
