Read PR #39's own gate log rather than the tick (CLAUDE.md §2b). Every stage
this branch could affect is green: decisions-log, lint, l0, render-golden, l1,
and approval-policy (which evaluated for real on the pull_request event and
passed — no sensitive path touched). The one failure is `gitea-settings`:
required_approvals: DECLARED 1 but LIVE is 0
Gitea has DRIFTED from infra/gitea/expected-branch-protection.json
Every other declared setting matched, and one runner is online. So live `main`
currently requires ZERO approvals while the declaration says 1, and because
.gitea/workflows/gate.yml sets REQUIRE_GITEA_SETTINGS=true, that drift blocks
EVERY PR in the repo, not only this one. The check that would notice approvals
are unenforced is the check that is failing.
Not touched here: infra/gitea/** is a sensitive path (non-author approval
required) and outside this branch's documentation-only scope. Re-asserting the
protection or changing the declaration is an operator decision in its own PR;
local branch fix/admin-approval-override (616a497) looks like that work in
flight.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
11 KiB
date, branch, title
| date | branch | title |
|---|---|---|
| 2026-10-02 | fix/plex-quicksync-verification | Hardware transcoding works — and `runbooks/plex-quicksync.md`'s verification section was wrong |
What changed
Hardware transcoding is confirmed working on prod. Measured 2026-10-02 from
Plex Media Server.log in the running pod: vaapi on /dev/dri/renderD128
(Intel CoffeeLake-S GT2 [UHD Graphics 630]), using hardware decode accelerator vaapi,
zero-copy support present, using zero-copy transcoding, and
final decoder: vaapi, final encoder: vaapi. VAAPI for both directions, zero-copy, so frames
never round-trip through the CPU. The device-plugin mechanism from PR #37 works end to end.
And runbooks/plex-quicksync.md's verification section was wrong, which is the correction this
branch exists to publish. The page said the Plex dashboard showing Transcode (hw) was "the only
proof available, and nothing else will ever tell you". The operator had hardware transcoding fully
working while Dashboard → Activity showed nothing of the sort — so the page's authoritative check
was silent on a working system, and sent someone looking for a fault that did not exist. Three
inference errors are now corrected on the page, two of them recorded as named false leads so the next
reader does not re-walk them:
- The dashboard is not proof. Replaced as the primary check by Plex's own log — the grep is in
the page, in its redacting form, because
Plex Media Server.logcarries API URLs containingX-Plex-Token(CLAUDE.md §6; a credential has been leaked into a transcript here before). The page also now says explicitly thatPlex Transcoder Statistics.logis the wrong file — it holds XML<Variant .../>records, not ffmpeg output, and the name invites the mistake — and whygrep -ais required. The dashboard is kept but demoted to a secondary, best-effort signal, with its absence stated to prove nothing. - An fd snapshot is not evidence. A
/proc/*/fdsweep inside the pod found no holder ofrenderD128while hardware transcoding was active; transcode sessions are short and the scan landed in a gap. Recorded as a documented false lead, with the reliable process-level check in its place: the transcoder's argv (-hwaccel,-hwaccel_device,vaapi,renderD128), found viaps -eo pid,user,comm | grep -i transcoder. - A missing preference key does not mean the setting is off. The page told readers to treat an
absent
HardwareAcceleratedCodecsinPreferences.xmlas "never enabled". With hardware acceleration demonstrably on and working, that key was absent from all 27 attributes whileHardwareDevicePathwas written and the log saidhardware transcoding: enabled. First-command #4 now reads both keys and states plainly thatHardwareDevicePathpresent is a positive signal, absence ofHardwareAcceleratedCodecsmeans nothing either way, only="0"is conclusive, and the log settles it.
Also on the page:
- GID
992is no longer presented as a number to expect. Under the plugin the injected device arrivesroot:root, so the image'scont-init.dhook resolves GID0and addsplexto the root group — measuredGroups: 0 100 1000,id plex→groups=1000(plex),0(root),100(users). 992 was the hostPath-era hostrenderGID. The page now says so, marks the root-group membership as an inherent property of the plugin'sDeviceSpecrather than a defect, and repeats that it is not a reason to reintroducesupplementalGroups(still a no-op:s6-setuidgidrebuilds the group list from/etc/group). The staleGroups: 100 1000claim in the escalation section is gone. - A new troubleshooting entry for legitimate per-title fallbacks, marked as a hypothesis.
Between two fully hardware sessions, one logged
enabled, but no hardware decode accelerator foundand empty final decoder/encoder. UHD 630 decodes H.264, HEVC 8/10-bit and VP9 but not AV1 and not 10-bit 4:2:2 HEVC, so the likely cause is the source file, not the platform — explicitly written as a lead to check first, not a confirmed diagnosis of that item. - The status is stated at the top of the page and in a rewritten provenance footer that separates the 2026-10-02 confirmations from the superseded 2026-09-30/2026-10-01 hostPath-era readings.
runbooks/README.md's index entry for the page was stale in the same two ways (it still described a/dev/drihostPath and the dashboard-only proof) and is corrected to match.
Unchanged on purpose: the "STOP HERE IF PLEX IS Pending" section and the escape hatch (delete the
hardware: block from catalog/plex.yaml, re-render) keep exactly their existing prominence — they
are correct and they are the 2am material. The "no alert is possible" reasoning is also kept verbatim
in substance: Plex declares no metricsEndpoint, renders no ServiceMonitor, and a fallback alert
would be a rule evaluating against nothing.
Documentation only. No change to catalog/, schemas/, apps/ or platform/.
What I verified
-
Gate, in the pinned toolbox from this worktree (
DEV_KUBECONFIG=/dev/null,ENGINEshim bind-mounting the main checkout's.git): rc 3 UNDETERMINED, diff baseorigin/main @ 948875aad37d, with exactly the three expected skips and no failures. Green:decisions-log,lint,l0,render-golden,l1,approval-policy. Skipped:spec-immutability(spec repo unavailable → R-TEST-06 not verified),gitea-settings(SETTINGS_API_TOKENunset),conformance(CLUSTER_AVAILABLE!=true→ L2/L3 not run). Touched requirements: R-OBS-04, reportedUNVERIFIEDfor lack of a cluster.approval-policypassed asN/Alocally (event='none', not apull_request) — it evaluates for real on the PR. rc 3 is not green, it is the calibrated worktree result, and it is reported here as rc 3. -
The gate was run three times, with the same rc 3 / three-skip result on each committed tip — including the final one. Worth recording from the first attempt: run before committing, with
HEAD == origin/main, the touched-requirement set isNOT DETERMINABLE (on-base)and the R-TEST-03 coverage gate cannot be applied at all. Commit before gating from a fresh worktree branch. -
runbooks/kustomization.yamlstill carries- plex-quicksync=plex-quicksync.mdin theconfigMapGeneratorfiles:list (line 110) — checked explicitly, because without it the page merges and still 404s.README=README.mdis likewise present, which is what serves the corrected index entry. -
This change touches no
sensitive_paths.infra/gitea/approval-policy.jsonlistsci/**,.gitea/**,conformance/**,clusters/**,bootstrap/**,infra/gitea/**,platform/kyverno/**,platform-config/kustomization.yaml,.sops.yaml,**/*.sops.yaml,**/rbac.yaml,CLAUDE.md.runbooks/**anddecisions/**are not among them, and theapproval-policystage passed. -
No YAML was added or edited, so
indent-sequences: consistent(which blocked a sibling branch today) is not in play here;lintpassed over the full tracked YAML set regardless. -
DECISIONS-LOG.mdis untouched — this handoff is its own file, per CLAUDE.md §7. -
The PR's own CI run was read, not assumed (PR #39, run 208, task 261, head
1677dff5688b). It is RED, and not because of this branch. Every stage this change could affect is green —decisions-log,lint,l0,render-golden,l1,approval-policy(approval-policyevaluated for real on thepull_requestevent and passed: no sensitive path touched). The single failure isgitea-settings, quoting the measured field:[fail] required_approvals: DECLARED 1 but LIVE is 0 [fail] Gitea has DRIFTED from infra/gitea/expected-branch-protection.json. [fail] MERGE GATE: BLOCKEDEvery other declared setting matched (
block_admin_merge_override,block_on_rejected_reviews,dismiss_stale_approvals,enable_status_check,enable_push=false,status_check_contexts = ["gate / gate (pull_request)"],runners online: 1). So livemaincurrently requires ZERO approvals, the declaration still says 1, and withREQUIRE_GITEA_SETTINGS: "true"in.gitea/workflows/gate.ymlthat drift blocks every PR in the repo, not just this one. Note the shape of it: the check that would notice approvals are unenforced is the check that is failing. Local branchfix/admin-approval-override(616a497, "fix(ci): let an administrator merge their own green PR") looks like the in-flight work that means to change the declaration. I did not touch it —infra/gitea/**is a sensitive path, needs a non-author approval, and is outside this branch's documentation-only scope. It is an operator decision: re-assert the protection, or change the declaration deliberately in its own PR. -
The repository-wide grep for
992to find what else still asserts it: remaining occurrences are incatalog/plex.yaml,catalog/render.py,schemas/renderer-contract.md,platform/intel-gpu-plugin/README.md,platform/intel-gpu-plugin/kustomization.yaml, and three olderdecisions/entries.
What I did NOT verify
- I did not re-measure anything on the live cluster in this session. Every platform number on the
page — the VAAPI/zero-copy log lines,
RW-OPEN-OKas uid 1000, thegpu.intel.com/i915limits and requests on the pod spec,Groups: 0 100 1000,id plex, the 27Preferences.xmlattributes, the empty/proc/*/fdsweep — came to me as measurements taken earlier on 2026-10-02 and was transcribed, not re-run. Per CLAUDE.md §2b, treat the page as reporting those readings rather than as proof of current state; re-run the primary check before relying on it. - The per-title fallback cause is unverified and labelled as such. The source codec of the title
that logged
no hardware decode accelerator foundwas never read. The AV1 / 10-bit-4:2:2-HEVC explanation is inferred from UHD 630's capability set alone. - Whether Plex's dashboard ever shows
Transcode (hw)on this deployment is unknown. It was observed silent during working hardware transcoding; whether that is a client/version quirk, a reporting bug, or something about this setup was not investigated. The page therefore demotes it rather than explaining it. - The commands as written in the page were not executed from the page. They are the forms that
were run, re-wrapped for
dev.sh; in particular theexec -it deploy/plex -- shroute for the argv check is noted as possibly lacking a TTY underdev.sh runand was not exercised in that shape. - Stale
992copies outsiderunbooks/were left alone, deliberately — scope was documentation only, andcatalog/plex.yaml,catalog/render.py,schemas/renderer-contract.mdandplatform/intel-gpu-plugin/all still carry the hostPath-eraGroups: 44 100 992 1000reading as if current.render.py --selftestasserts around that comment, so correcting it is a real change with gate consequences, not a doc edit. That is open work. Olderdecisions/entries also cite 992; those are historical records and should stay as written. - CI on the PR. The branch is pushed and the PR opened; I did not watch the Gitea run report, so nothing here should be read as "CI green". The only gate result I can quote is the local rc 3 above.