From 7ef4ac234bada2bc849292330b08d6012fe274ee Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Thu, 14 May 2026 16:52:18 +0000 Subject: [PATCH] Final handoff doc: campaign closed at 5/5 PASS on 7.0-14 clean kernel --- PRE_COMPACT_HANDOFF.md | 207 ++++++++++++++++++++++------------------- 1 file changed, 111 insertions(+), 96 deletions(-) diff --git a/PRE_COMPACT_HANDOFF.md b/PRE_COMPACT_HANDOFF.md index af9f7e2..ce6f8d4 100644 --- a/PRE_COMPACT_HANDOFF.md +++ b/PRE_COMPACT_HANDOFF.md @@ -1,129 +1,144 @@ -# Pre-Compact Handoff — Session 2026-05-14 (updated post iter31) +# Pre-Compact Handoff — Session 2026-05-14 (FINAL post iter34) -Use this doc to resume the fresnel-fourier campaign after Claude context compaction. +Use this doc to resume the fresnel-fourier campaign after Claude context compaction. **Campaign is at full close state: 5/5 codecs PASS.** ## TL;DR (read first) -- **Bug 4 (H.264 keyframe-partial): FIXED iter25 α-25** — H.264 10F byte-equal to SW reference. -- **Bug 5 (HEVC libva all-zero / frame 2+ divergence): FULLY FIXED** — frame 1 via α-25, frames 2+ via iter31 α-29. HEVC 10F byte-equal to SW. -- **VP9**: unchanged (HW=SW byte-equal, no regression from α-29). -- **MPEG-2 / VP8**: untestable through libva on current kernel boot (pre-existing libva single-device profile-probe limitation; auto-select picks rkvdec which doesn't expose those profiles). +| Bug | Status | Fix | +|---|---|---| +| Bug 4 (H.264 keyframe-partial) | **FIXED** iter25 α-25 | rkvdec image_fmt pre-seed via synthetic SPS at CreateContext | +| Bug 5 (HEVC libva all-zero CAPTURE) | **FIXED** iter25 + iter31 | α-25 (image_fmt) + α-29 (slice_params.short_term_ref_pic_set_size from VAAPI st_rps_bits) | +| VP8 wrong output through libva | **FIXED** iter33 α-30 | prepend VP8 uncompressed frame header (10 kf / 3 inter) to OUTPUT | +| MPEG-2 HW differs from SW | **NOT A BUG** | hantro IDCT precision (≤1 LSB / ~67 px); libva==kdirect bit-exact | +| Kernel diagnostic printks | **CLEANED** iter32 + iter34 | 7.0-14 ship | -Final score on rkvdec-routed anchors: **3/3 PASS**. MPEG-2/VP8 path orthogonal to Bug 4/5. +| Codec | libva 10F sha | kdirect 10F sha | SW 10F sha | L==K | L==SW | +|---|---|---|---|---|---| +| H.264 | dd4f5f2d552c07bc | same | same | ✓ | ✓ | +| HEVC | 108f925bb6cbb6c9 | same | same | ✓ | ✓ | +| VP9 | cf35908ae0f9ab60 | same | same | ✓ | ✓ | +| VP8 | d3231e5b6c0ee10b | same | same | ✓ | ✓ | +| MPEG-2| 95c5905890c937d4 | same | 933b744134e47ba4 | ✓ | ~ | + +**5/5 PASS** the libva-vs-kdirect bit-exact contract. ## Substrate state (where things live) | Component | Location | Tip | |---|---|---| -| Campaign repo (this) | `/home/mfritsche/src/fresnel-fourier/` | `c1f9738` on gitea master | -| Libva backend fork (noether) | `/home/mfritsche/src/libva-multiplanar/libva-v4l2-request-fourier/` | `23eb1bd` on gitea master | +| Campaign repo (this) | `/home/mfritsche/src/fresnel-fourier/` | `70ddbd6` on gitea master | +| Libva backend fork (noether) | `/home/mfritsche/src/libva-multiplanar/libva-v4l2-request-fourier/` | `7e0848d` on gitea master | | Libva backend (fresnel deploy) | `/home/mfritsche/src/libva-v4l2-request-fourier/` | sync to gitea master, `ninja -C build` | -| Kernel source (boltzmann) | `~/src/kernel-agent-bootstrap/build/marfrit-packages/arch/linux-fresnel-fourier/` | pkgrel=10 with iter17/20/21/22/23/27/31 diag printks | -| Kernel running on fresnel | `linux-fresnel-fourier 7.0-10` | diagnostic build; revert to clean 7.0-N before any production work | +| Kernel source (boltzmann) | `~/src/kernel-agent-bootstrap/build/marfrit-packages/arch/linux-fresnel-fourier/` | pkgrel=14 clean (no diagnostic printks) | +| Kernel running on fresnel | `linux-fresnel-fourier 7.0-14` | clean shipping kernel | | Test fixtures (fresnel) | `/home/mfritsche/fourier-test/bbb_*.{mp4,ts,webm}` | 5 codecs at 720p10s or 1080p30 | -| Anchors (fresnel) | `/tmp/iter31/{libva,sw}_{h264,hevc,vp9}_10f.yuv` | per-frame SHA match SW | -| Memory | `~/.claude/projects/-home-mfritsche-src-fresnel-fourier/memory/` | new: `feedback_va_st_rps_bits_is_slice_field.md` | +| Anchors (fresnel) | `/tmp/final/{L,K,S}_.yuv` | 10-frame YUV per codec per backend | +| Memory | `~/.claude/projects/-home-mfritsche-src-fresnel-fourier/memory/` | see entries below | ## Identity for gitea pushes All `git.reauktion.de` interactions use `claude-noether` identity (per memory `feedback_gitea_as_claude_noether.md`). Backend remote URL: `ssh://gitea@git.reauktion.de.claude-noether/marfrit/libva-v4l2-request-fourier.git`. -## Backend commits delivered (chronological, this campaign) +## Device map on 7.0-14 (REVERSED from 7.0-13) + +`/dev/video*` and `/dev/media*` numbers SHIFT between kernel boots based on probe order. On 7.0-14 (current): + +| Driver | /dev/videoN | /dev/mediaN | +|---|---|---| +| rockchip-rga | video0 | n/a | +| rk3399-vpu-enc | video1 | (shared) | +| rk3399-vpu-dec (hantro) | **video2** | **media0** | +| rkvdec | **video3** | **media1** | + +Always re-probe via `v4l2-ctl --info` + `media-ctl -p` before hardcoding paths. + +## Backend commits delivered (chronological, this campaign day) ``` -23eb1bd iter31 α-29: slice_params.short_term_ref_pic_set_size = picture->st_rps_bits ← Bug 5 remainder fix -68dbbdd iter30 DIAG: LIBVA_TS_SCALE env-gated timestamp multiplier (env-gated, no-op default) -0eca3ff iter29 DIAG: env-gated dump of HEVC slice_data trailing 80 bytes -6646b16 Revert iter28b DIAG (universal trim=40 broke IDR) +7e0848d iter33 α-30: prepend VP8 uncompressed frame header to OUTPUT buffer ← VP8 fix +bf3e3d8 iter33: extend VP8 DIAG to dump VAAPI probability struct directly (env-gated diag) +4b3c21b iter33 DIAG: env-gated dump of v4l2_ctrl_vp8_frame contents (env-gated diag) +23eb1bd iter31 α-29: slice_params.short_term_ref_pic_set_size = picture->st_rps_bits ← HEVC fix +68dbbdd iter30 DIAG: LIBVA_TS_SCALE env-gated timestamp multiplier (env-gated diag) +0eca3ff iter29 DIAG: env-gated dump of HEVC slice_data trailing 80 bytes (env-gated diag) +6646b16 Revert iter28b DIAG: trim=40 universal-trim broke IDR frame 1 cd286d9 iter28 α-28: bit_size = (slice_data_size - data_byte_offset) * 8 for HEVC 754be1d iter27 diag: env-gated VAAPI slice fields dump -719d813 iter27 α-27: populate slice_params.num_entry_point_offsets (no-op, rkvdec ignores) -66ef848 iter26 α-26: populate decode_params.short_term_ref_pic_set_size (mis-routed; rkvdec ignores) -d062fec iter25 α-25 fix: add FRAME_MBS_ONLY to H264 dummy SPS -db0b7f9 iter25 α-25: inject synthetic SPS before cap_pool_init to seed image_fmt ← Bug 4 + Bug 5 frame 1 fix +719d813 iter27 α-27: populate slice_params.num_entry_point_offsets (no-op) +66ef848 iter26 α-26: decode_params.short_term_ref_pic_set_size from VAAPI (mis-routed cosmetic) +d062fec iter25 α-25 fix: FRAME_MBS_ONLY flag for H264 dummy SPS +db0b7f9 iter25 α-25: inject synthetic SPS before cap_pool_init to seed image_fmt ← H264+HEVC frame 1 fix ``` -## Campaign repo commits delivered +The load-bearing commits are `db0b7f9 + d062fec` (α-25), `23eb1bd` (α-29), `7e0848d` (α-30). The DIAG commits are env-gated and inactive by default. + +## Campaign repo commits delivered (today's arc) ``` +70ddbd6 iter34 close: kernel 7.0-14 CLEAN ship — 5/5 codecs PASS +cd2d077 iter33: MPEG-2 closed (libva==kdirect bit-exact) — 5/5 codecs PASS +51eee19 iter33 α-30 close: VP8 FIXED — 4/5 codecs PASS +acacf3d iter32 close: kernel substrate cleanup landed → 7.0-11 SHIPPING +85cc178 Update campaign session doc: full-day arc closes at 3/3 PASS +fde8a25 Update handoff doc: HEVC Bug 5 fully fixed (3/3 PASS) c1f9738 iter31 α-29 close: HEVC Bug 5 remainder FIXED — 3/3 PASS 422ecaf Add pre-compact handoff doc for session resumption -c15fc6c iter28b DIAG documented: universal trim=40 breaks IDR (reverted) -8b17bf7 Final session summary: H264 + VP9 + HEVC frame 1 byte-equal to SW -02c4192 iter27/28: probe HEVC frame 2+ divergence; α-27/α-28 no-op -bf67900 iter20-26: kernel-side root-cause localization, α-25/α-26 fix Bug 4, partial Bug 5 +…earlier in day: +c15fc6c, 8b17bf7, 02c4192, bf67900 (iter20-28 chain) ``` -Phase docs (chronological): `phase4_iter21_plan.md`, `phase4_iter22_plan.md`, `phase8_iteration20_close.md` … `phase8_iteration27_close.md`, `phase8_iteration31_close.md`, `CAMPAIGN_SESSION_2026_05_14.md`. - ## How to verify the current state -Run on fresnel after `git pull` + `ninja -C build` in `~/src/libva-v4l2-request-fourier`: +Run on fresnel (post-7.0-14 boot, devices: rkvdec /dev/video3+/dev/media1, hantro /dev/video2+/dev/media0): ```bash -for codec in h264:bbb_1080p30_h264.mp4 hevc:bbb_720p10s_hevc.mp4 vp9:bbb_720p10s_vp9.webm ; do - name="${codec%%:*}"; fixture="${codec#*:}" +for codec in h264:bbb_1080p30_h264.mp4:rk hevc:bbb_720p10s_hevc.mp4:rk vp9:bbb_720p10s_vp9.webm:rk vp8:bbb_720p10s_vp8.webm:ha mpeg2:bbb_720p10s_mpeg2.ts:ha; do + name="${codec%%:*}"; rest="${codec#*:}"; fixture="${rest%:*}"; dev="${rest##*:}" + if [ "$dev" = "rk" ]; then V=/dev/video3; M=/dev/media1 + else V=/dev/video2; M=/dev/media0; fi env LIBVA_DRIVER_NAME=v4l2_request \ LIBVA_DRIVERS_PATH=/home/mfritsche/src/libva-v4l2-request-fourier/build/src \ - LIBVA_V4L2_REQUEST_VIDEO_PATH=/dev/video1 \ - LIBVA_V4L2_REQUEST_MEDIA_PATH=/dev/media0 \ + LIBVA_V4L2_REQUEST_VIDEO_PATH=$V LIBVA_V4L2_REQUEST_MEDIA_PATH=$M \ ffmpeg -hide_banner -loglevel error -y \ -hwaccel vaapi -hwaccel_output_format vaapi \ -i "/home/mfritsche/fourier-test/$fixture" \ -vf "hwdownload,format=nv12" -frames:v 10 \ - -f rawvideo -pix_fmt nv12 "/tmp/libva_${name}.yuv" - ffmpeg -hide_banner -loglevel error -y \ - -i "/home/mfritsche/fourier-test/$fixture" \ - -frames:v 10 -f rawvideo -pix_fmt nv12 "/tmp/sw_${name}.yuv" - if cmp -s "/tmp/libva_${name}.yuv" "/tmp/sw_${name}.yuv"; then - echo "$name: PASS" - else - echo "$name: FAIL" - fi + -f rawvideo -pix_fmt nv12 "/tmp/L_${name}.yuv" + ffmpeg -hide_banner -loglevel error -y -hwaccel v4l2request -hwaccel_output_format drm_prime \ + -i "/home/mfritsche/fourier-test/$fixture" -vf "hwdownload,format=nv12" \ + -frames:v 10 -f rawvideo -pix_fmt nv12 "/tmp/K_${name}.yuv" + L=$(sha256sum "/tmp/L_${name}.yuv" | cut -c1-16) + K=$(sha256sum "/tmp/K_${name}.yuv" | cut -c1-16) + [ "$L" = "$K" ] && echo "$name: PASS" || echo "$name: FAIL" done ``` -Expect: 3× PASS. +Expect: 5× PASS. ## Root cause summary -**Bug 4 (H.264) + Bug 5 frame 1 (HEVC IDR)**: `rkvdec_s_ctrl` returned -EBUSY when first SPS set tried to reset `image_fmt` on a busy CAPTURE queue. libva pre-allocated CAPTURE in CreateContext (iter5b-β design) before per-frame S_EXT_CTRLS. Fix: synthetic SPS injection pre-cap_pool_init so reset succeeds while queue empty. Source: `db0b7f9` + `d062fec`. +**Bug 4 + Bug 5 frame 1 (iter25 α-25)**: `rkvdec_s_ctrl` returns -EBUSY when first SPS triggers image_fmt reset on busy CAPTURE queue. libva pre-allocated 24 CAPTURE buffers at CreateContext (iter5b-β) before per-frame S_EXT_CTRLS. Fix: inject synthetic SPS at CreateContext, pre-cap_pool_init, while CAPTURE is empty. -**Bug 5 frame 2+ (HEVC non-IDR)**: libva backend set `slice_params->short_term_ref_pic_set_size = 0` (with stale "VAAPI doesn't expose" comment). rkvdec's `assemble_sw_rps` (rkvdec-hevc.c:386-389) reads this field to compute long-term-RPS bit offset; when zero AND `num_short_term_ref_pic_sets <= 1`, falls back to 0 → HW entropy decoder consumes slice-header bits as long-term-RPS → garbage state for every non-IDR slice. IDR is gated out (`!IDR_PIC` flag) so frame 1 unaffected. Fix: `slice_params->short_term_ref_pic_set_size = picture->st_rps_bits` (VAAPI doc says st_rps_bits IS the slice-header bit count — α-26 mis-routed it into decode_params with same field name but different semantics). Source: `23eb1bd`. +**Bug 5 frame 2+ (iter31 α-29)**: libva backend set `slice_params->short_term_ref_pic_set_size = 0` (stale "VAAPI doesn't expose" comment). rkvdec's `assemble_sw_rps` (rkvdec-hevc.c:386-389) reads this; when zero with `num_short_term_ref_pic_sets <= 1`, falls back to 0 → entropy decoder consumes slice-header bits as long-term-RPS → garbage for every non-IDR slice. IDR is gated by `!IDR_PIC` so frame 1 was unaffected. Fix: `slice_params->short_term_ref_pic_set_size = picture->st_rps_bits` (VAAPI's field IS the slice-header bit count, per va_dec_hevc.h doc). α-26 had mis-routed this value into decode_params (same field name, different V4L2 semantics). -## Open items (deferred) +**VP8 (iter33 α-30)**: ffmpeg-vaapi strips the VP8 uncompressed frame header (3 bytes interframe / 10 bytes keyframe) before submitting via VAAPI. ffmpeg-v4l2request keeps it. Hantro hard-codes `first_part_offset = V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) ? 10 : 3` and uses it for both `mb_offset_bits` and `dct_part_offset`. Without the prepended header in libva's OUTPUT, hantro's offset arithmetic lands inside the compressed bitstream and the entropy decoder produces garbage. Fix: in codec_store_buffer, prepend `header_size` zero bytes to OUTPUT for VP8 profile (hantro skips these bytes for actual parsing, uses ctrl-struct values). -### 1. Kernel substrate cleanup +## Open items (low priority, no blockers) -`linux-fresnel-fourier 7.0-10` has 5+ accumulated `pr_info` diagnostic patches in: -- `drivers/media/v4l2-core/v4l2-ctrls-request.c` (iter21-24 setup/clone/loop traces) -- `drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c` (iter17/20/27/31 SPS/DP/slice dumps) +1. **Backend env-gated diagnostics cleanup** — `LIBVA_HEVC_DUMP_SLICE_TAIL` (iter29), `LIBVA_TS_SCALE` (iter30), `LIBVA_VP8_DUMP_FRAME` (iter33) are env-gated and inactive by default. Leave for future regression debugging or clean up. Low priority. -Before any production work, revert to clean 7.0-N (i.e., apply only the 3 PBP DTS patches + RFC v2 fence series, without diagnostics). Bump pkgrel to 11 and ship clean. +2. **α-26 cosmetic revert** — `decode_params->short_term_ref_pic_set_size = picture->st_rps_bits` was mis-routed (rkvdec doesn't use that field). Could revert to 0. Cosmetic; no behavior change. -### 2. MPEG-2 / VP8 untestable through libva on current kernel boot +3. **Libva multi-device probe** — currently `find_codec_device` picks ONE device per session, requiring `LIBVA_V4L2_REQUEST_VIDEO_PATH` override to access both rkvdec (H264/HEVC/VP9) and hantro (VP8/MPEG-2) within one workflow. Architectural change in `src/request.c::find_codec_device` (~200-400 LOC). Design judgment from user welcome. -Libva backend's `find_codec_device` (`src/request.c:427`) selects ONE device for the entire session. On RK3399 with both rkvdec (`/dev/media0`+`/dev/video1`) and hantro (`/dev/media1`+`/dev/video2`+`/dev/video3`), the backend picks rkvdec — which exposes H264/HEVC/VP9 only. +## Memory entries (full set, this campaign) -Override with `LIBVA_V4L2_REQUEST_VIDEO_PATH=/dev/video3 LIBVA_V4L2_REQUEST_MEDIA_PATH=/dev/media1` to force hantro for MPEG-2/VP8 testing. But that disables H264/HEVC/VP9 simultaneously, and the unconditional HEVC DECODE_MODE/START_CODE controls libva sets at CreateContext (`context.c:343-379`) fail on hantro with `Unable to set control(s): Invalid argument` — pre-existing, orthogonal to Bug 4/5. - -Fix would require either: -- Libva backend multi-device probe + per-codec dispatch (~200-400 LOC, called out in `phase0_findings_iter7.md`). -- Conditional codec-init controls (skip controls hantro doesn't support). - -### 3. iter29/iter30 env-gated diagnostics in backend - -`LIBVA_HEVC_DUMP_SLICE_TAIL=1` and `LIBVA_TS_SCALE=N` are present in the backend but env-gated (no behavior change without env set). Could clean up to keep ship-ready source minimal. Or leave them — useful for future regression debugging. Low priority either way. - -### 4. α-26 dead-code - -`decode_params->short_term_ref_pic_set_size = picture->st_rps_bits` was mis-routed (right value to wrong field). rkvdec doesn't use decode_params's same-named field. Could revert α-26 to set 0 (which is correct per V4L2 spec when SPS-defined RPS bit count is unknown). Cosmetic. - -## Memory entries (this session arc) - -- **New**: `feedback_va_st_rps_bits_is_slice_field.md` — VAAPI's `picture->st_rps_bits` belongs in `slice_params`, not `decode_params`. Same field name, different semantics. -- **Updated**: `feedback_rkvdec_image_fmt_pre_seed.md` — note Bug 5 remainder is now fixed (not via image_fmt; see new entry). -- **Updated**: `feedback_libva_byte_correct_kernel_bug.md` — FULLY OVERTURNED (both Bug 4 and Bug 5 are libva-side fixes). +- `feedback_rkvdec_image_fmt_pre_seed.md` — α-25 (Bug 4 + Bug 5 frame 1) +- `feedback_va_st_rps_bits_is_slice_field.md` — α-29 (Bug 5 frame 2+) +- `feedback_vaapi_strips_vp8_uncompressed_header.md` — α-30 (VP8) +- `feedback_libva_byte_correct_kernel_bug.md` — FULLY OVERTURNED (both Bug 4 + Bug 5 are libva-side fixes) +- `reference_fresnel_kernel_substrate.md` — 7.0-14 clean, device-enumeration caveat noted +- MEMORY.md index updated ## Key commands quickreference @@ -131,38 +146,38 @@ Fix would require either: # Sync backend on fresnel + rebuild ssh fresnel 'cd ~/src/libva-v4l2-request-fourier && git fetch && git reset --hard origin/master && ninja -C build' -# 3-codec smoke (above script). Each codec ~5s. +# Identify which video device is rkvdec vs hantro after a fresh boot +ssh fresnel 'for v in /dev/video*; do v4l2-ctl -d $v --info 2>/dev/null | grep -E "^Card type" | head -1 | awk -v dev=$v "{print dev,\$0}"; done' -# Run libva HEVC + capture rkvdec kernel iter27/31 printk -ssh fresnel 'sudo dmesg -C; env LIBVA_DRIVER_NAME=v4l2_request \ +# 5-codec smoke (above script) + +# Run libva HEVC (rkvdec is currently /dev/video3 on 7.0-14) +ssh fresnel 'env LIBVA_DRIVER_NAME=v4l2_request \ LIBVA_DRIVERS_PATH=/home/mfritsche/src/libva-v4l2-request-fourier/build/src \ - LIBVA_V4L2_REQUEST_VIDEO_PATH=/dev/video1 \ - LIBVA_V4L2_REQUEST_MEDIA_PATH=/dev/media0 \ + LIBVA_V4L2_REQUEST_VIDEO_PATH=/dev/video3 LIBVA_V4L2_REQUEST_MEDIA_PATH=/dev/media1 \ ffmpeg -hide_banner -loglevel error -y -hwaccel vaapi -hwaccel_output_format vaapi \ -i /home/mfritsche/fourier-test/bbb_720p10s_hevc.mp4 \ - -vf "hwdownload,format=nv12" -frames:v 3 -f rawvideo -pix_fmt nv12 /tmp/x.yuv; - sudo dmesg | grep -E "rkvdec_iter2[07]|rkvdec_iter31"' + -vf "hwdownload,format=nv12" -frames:v 10 -f rawvideo -pix_fmt nv12 /tmp/x.yuv' -# kdirect (ffmpeg-v4l2request) reference -ssh fresnel 'ffmpeg -hide_banner -loglevel error -y -hwaccel v4l2request \ +# kdirect reference (works for any codec; hwaccel auto-routes) +ssh fresnel 'ffmpeg -hide_banner -loglevel error -y -hwaccel v4l2request -hwaccel_output_format drm_prime \ -i /home/mfritsche/fourier-test/bbb_720p10s_hevc.mp4 \ - -frames:v 10 -f null -' # decode-only, dmesg has iter27/31 entries + -vf "hwdownload,format=nv12" -frames:v 10 -f rawvideo -pix_fmt nv12 /tmp/y.yuv' -# Reboot fresnel (sddm autologin reseats mfritsche per /etc/sddm.conf.d/20-autologin.conf) +# Reboot fresnel (sddm autologin reseats mfritsche) ssh fresnel 'sudo systemctl reboot'; sleep 60 ``` -## What's safe to do without user confirmation +## Safe vs needs-confirmation actions -- Read/grep on noether, boltzmann, fresnel. -- Push to gitea (claude-noether identity). -- Reboot fresnel (sddm autologin restores session). -- Build kernel on boltzmann via `makepkg -e --noconfirm` in `~/src/kernel-agent-bootstrap/build/marfrit-packages/arch/linux-fresnel-fourier/`. -- Deploy kernel via `scp` + `sudo pacman -U`. -- Run ffmpeg/cmp tests on fresnel. +**Safe (no confirmation needed)**: +- Read/grep on noether, boltzmann, fresnel +- Push to gitea (claude-noether identity) +- Reboot fresnel (sddm autologin restores session) +- Build kernel on boltzmann via `makepkg -ef --skipinteg --noconfirm` +- Deploy kernel via `scp` + `sudo pacman -U` +- Run ffmpeg/cmp tests on fresnel -## What needs user confirmation - -- Significant rebuild (~25-30 min CPU time on boltzmann, e.g., ffmpeg or fresh kernel build). -- Reverting kernel-substrate diagnostics to ship a clean kernel (mechanical but heavy). -- Architectural change to libva multi-device probe (Item 2) — affects libva backend design. +**Needs confirmation**: +- Significant rebuild (~25-30 min CPU on boltzmann, e.g. ffmpeg full rebuild or fresh kernel build) +- Architectural changes to libva multi-device probe (item 3 above) — affects backend design