# Refactoring backlog — CLOSED items, archived 2026-08-27

> Split out of [`refactoring-backlog.md`](refactoring-backlog.md) so that file
> shows only OPEN work. Nothing here needs action; it is kept because several
> entries record WHY a thing is the way it is, and because a few were closed by
> being REFUTED or RETRACTED rather than fixed — which is exactly the kind of
> finding that gets re-discovered if the reasoning disappears.
>
> The older round (2026-08-10 and before) is in
> [`refactoring-backlog-archive-2026-08-10.md`](refactoring-backlog-archive-2026-08-10.md).

## Closed on 2026-08-27 and before

- ✅ **DONE 2026-08-26 — buildkitd upgraded, but READ THE VERSION.** The host
  went nerdctl 2.3.4 → **2.3.5**, buildctl/buildkitd v0.31.1 → **v0.31.2**,
  containerd 2.3.2 → **2.3.3**, daemons confirmed reporting the new versions,
  51 cache-mount records unchanged. CORRECTION to this item's original text:
  **v0.32.2 was not reachable this way.** buildkitd here comes from the
  nerdctl-full bundle, and 2.3.5 — the newest release — ships BuildKit
  **0.31.2**. So #6916 (daemon crash under concurrent builds, v0.31.2) WAS
  obtained; **#6955 (parallel-build cache miss) was NOT** — it landed in
  v0.32.0 and no nerdctl-full ships it. Getting it would mean installing
  buildkit outside the bundle, i.e. breaking the version-matched set. Not
  worth it; revisit when a nerdctl-full bundles 0.32.x. Tooling:
  `linux/host-config/install-nerdctl-full.sh` (needs
  `NERDCTL_INCLUDE_ROOTFUL=1` on this host; see
  docs/linux-host-setup.md § B3b. That section carries the rationale and
  the reference run).
- ✅ **DONE — buildkitd.toml gcpolicy is live.** Repo and
  `~/.config/buildkit/buildkitd.toml` are in sync and the daemon restarted
  during the upgrade above, which is the event it was waiting for.
- ⛔ **OPEN AND BLOCKING — disk.** 72G free; the playbook wants **≥150G** for a
  full from-base rebuild. prune-safe first; the 215G kata-buildcache trim
  stays the operator's call, not the agent's. **Do not launch the rebuild on
  72G** — ENOSPC mid-lane is the expensive way to learn this.
- ⬜ Run preflight.sh (the full gate battery incl. the GCC-literal gate).

**Phase 1 — pin-bump pass (versions.env window): ✅ DONE (7a2639e)**
- Swept: CMAKE 4.4.3, UV 0.12.6, CARGO_C 0.10.25, OLLAMA 0.33.0 (+ SHA
  pairs). VULKAN deliberately held at 1.4.357.0 with a documented reason
  (1.4.357.1 is a Linux-only republish; windows.txt still says .0 and the key
  is shared — bumping it would break the Windows lane, which is out of scope
  here).
- Riders landed: **AP6** decided → `ORT_ENABLE_LTO=false`; `LLVM_COMMIT=`
  opt-in key added; `RUFF_VERSION`, `APPIMAGETOOL_VERSION`, `ABSEIL_VERSION`
  and the `PY_*` keys now live in versions.env.
- Still open in this phase: the **MESON-GI probe** — if meson >1.12 resolves
  g-i's glib subproject again, lift the riscv64 pin in this window; else it
  stays with its reason (tracked in § E).
- MESON-GI probe: if meson >1.12 resolves g-i's glib subproject again, lift
  the riscv64 pin in this window; else it stays with its reason.

**Phase 2 — producer fixes (media/android lane): ✅ mostly DONE**
- ✅ GENAI-DRIFT producer half — the arm64 genai wheel builds. The tolerance
  line at smoke-torch-venv.sh:179 must be deleted AFTER the rebuild proves
  0.15.2 ships, not before.
- ✅ ORPHAN-PINS — PyAV is built (own stage + own script dir); the TVM half
  closed by 202634c.
- ✅ RV1-FREETYPE — freetype ENABLED against staged static target harfbuzz;
  OFF survives only as the not-staged fallback.
- ⬜ **LOG2 open half — build the wasm asyncify/jspi flavors.** The only
  Phase-2 item still outstanding.

**Phase 3 — toolchain trims (from-base rebuild validates):**
- TG1 residual (fuller toolchain-closure trim) + TG3 residual (collapse the
  two toolchain RUNs). Both were bitten before — implement + adversarial
  review + the rebuild as the only accepted proof.

**Phase 4 — DECIDED by the operator 2026-08-24:**
- S3 registry cache mode=max: **NO** — inline stays; the design doc
  (docs/build-cache-tiers.md) remains for a future revisit. S3 moves to § D
  as decision-recorded, do not re-pitch without new evidence (e.g. another
  cold-rebuild incident).
- USE_FAST_UBUNTU_MIRROR: **YES** — the rebuild launches with the knob ON,
  which exercises the APT-HTTP https-restore for real (its validator).

**The rebuild itself:** from BASE, all three arches, --parallel-archs
--max-parallel-archs 3, launched CLEAN — it doubles as the long-owed PAR1
timing run. It auto-validates the already-landed-but-unproven set (media
source-caches, TS8, DUP2 SSOT, cerbero extra_mirrors, C3 :?-guards, TS4 llvm
keying, CERB-CACHE warm behaviour) and fires the § E triggers:
GCC_PARALLEL_TARGETS, gcc-prereq facets, post-restart base cache-miss.

**After the ship:** evidence-audit the logs (the wave-6 ritual), fold
validated items with quotes, THEN: GPU lane opt-in build (GPU1-7), first
compose up + WEBUI_SECRET_KEY rotation (user-side).

- ✅ **`preflight.sh` exits 0 on failure — RETRACTED 2026-08-27, the bug is not
  real.** The script's summary block ends `exit 0` / `exit 1` / `exit 2` and
  those propagate correctly: `PREFLIGHT_ONLY=<unknown-slug>` returns 2 when run
  directly. Both "observations" were the same measurement error —
  `bash preflight.sh | tail | sed; echo $?` reports SED's status, not the
  script's. Demonstrated: `(exit 1) | tail -1 | sed 's/^//'; echo $?` prints 0,
  while `(exit 1); echo $?` prints 1. I added the "re-confirmed 2026-08-27"
  note to this entry on the strength of that artifact, so the retraction is
  mine to make. `exit 1` has been in place since 928e745 (2026-08-25).
  LESSON worth keeping: when checking an exit code, do not pipe.

- ✅ **stdout-as-return-value — CLOSED 2026-08-27 with a GATE, not a sweep.**
  The exposure turned out to be small and the risk structural, so the fix is
  `linux/scripts/verify-stdout-returns.py`, wired into preflight as
  `stdout-returns`. It cross-references every function consumed via `$( )`
  (411 of them) against every `log`/`info` call inside one, because those reach
  fd 1 while `warn`/`err` reach fd 2 (logging.sh:77-83).
  Found exactly one live offender: `normalize_llvm_cmake_dir` (tvm-detect.sh),
  whose stdout is a path consumed at three call sites — latent, firing only
  when an LLVM CMake path actually needs normalising. Both of its log lines are
  redirected now; the gate found the SECOND one after the first had been fixed
  by hand, which is the whole argument for having it. Mutation-checked in both
  directions.

- ✅ **`--ccache` renamed 2026-08-27.** build-gcc.sh and build-clang.sh now take
  `--compiler-cache` ("sccache first, ccache fallback"), with `--ccache` kept as
  a DEPRECATED alias so llvm.sh:28 and any operator habit keep working. Both
  spellings verified to parse; an unknown flag still warns.

- ✅ **sccache stats — FIXED 2026-08-27.** build-gcc.sh zeroed the counters
  before EVERY compiler, so five GCCs produced five disjoint windows and no
  aggregate. Zeroed once per run now, via a /tmp marker whose lifetime matches
  the sccache server's. Fixed alongside the third dead `= "sccache"` identity
  check in the same file, which had every GCC stage reporting CCACHE stats
  while sccache did the work.

- ✅ **Mount-id keys — RESOLVED 2026-08-27, but NOT by unifying all of them.**
  Investigated rather than swept. The two conventions are mostly principled:
  `${TARGETARCH}` is BuildKit's automatic value and resolves to amd64 for every
  cross lane, so the caches keyed on it are SHARED across targets --
  pip/uv/rustup, cargo, ccache/sccache, llvm-src. That is correct and desirable:
  wheels carry a platform tag, sccache keys on compiler+flags, crate sources and
  LLVM sources are arch-independent, so sharing raises the hit rate and splitting
  them would only cost a cold rebuild. `${TARGET_ARCH}` is the explicit per-target
  value and keys the caches whose CONTENT is arch-specific (apt debs,
  ffmpeg-sdks). Also correct.
  The one real defect was cargo: Dockerfile.toolchain:228-229 used
  `${TARGETARCH}` while Dockerfile.media:863-864 used `${TARGET_ARCH}`, so the
  same registry/git caches carried two different ids and the toolchain's copy
  could never help the media stage. Aligned on the shared convention, since
  crate sources are arch-independent.

- ✅ **versions.env watch note MOVED 2026-08-27.** The -nostdinc++ libstdc++
  c++23 patch note now sits at the GCC_VERSION pin, where the patch actually
  lives (src/c++23/Makefile.in), instead of under LLVM_RELEASE.

- ✅ **UNOWNED env knobs — CLOSED 2026-08-27.** All five (HARFBUZZ_VERSION,
  IREE_CCACHE_MAXSIZE, IREE_SCCACHE_LOG, PY_MLC_Z3_STATIC_VERSION,
  SCCACHE_CONF) are registered in lint-env-knobs.allow with their reason and
  reader. Verified under KNOB_GATE=1, which is the failure the entry warned
  about: the gate now passes on its first strict use instead of failing.

- ✅ **IREE host stage no longer builds LLVM** [was never tracked here — the win
  the wave went looking for]. `-DIREE_BUILD_COMPILER=ON` on the HOST stage was
  a leftover; the target reads only `iree-c-embed-data` + `iree-flatcc-cli`
  out of `IREE_HOST_BIN_DIR`, both LLVM-free, so the host stage runs
  COMPILER=OFF. Review corrected the fallback: a failed *build* now fails fast
  instead of escalating to a multi-hour LLVM compile that provably cannot
  succeed where OFF failed. **Watch in the rebuild:** the riscv64/arm64 IREE
  host stage must NOT show clang/MLIR compilation, and both tools must exist.
- ✅ **ORPHAN-PINS closed** — PyAV is BUILT (new
  `linux/scripts/03-media/build/pyav/build-pyav.sh` + a `pyav` stage landing
  `av-<ver>-cp314-cp314-linux_<arch>.whl` in /opt/wheels); the TVM half was
  already closed by 202634c. **Watch:** the PyAV wheel must appear on all
  three arches and the wheel-smoke warning must disappear.
- ✅ **RV1-FREETYPE fixed** — riscv64 opencv freetype is ENABLED against staged
  static target harfbuzz; `-DBUILD_opencv_freetype=OFF` survives only as the
  not-staged fallback. **Watch:** the riscv64 `opencv-freetype` wheel-smoke
  warning must be gone — if it is not, the static-harfbuzz staging is what
  failed, not the module.
- ✅ **TVM ffi honesty** — a failed `tvm-ffi` wheel build now WITHDRAWS the wheel
  set instead of leaving an apache-tvm wheel that dies at `import tvm`.
  **Watch:** either a complete tvm+tvm_ffi pair, or an explicit skip reason —
  never "python wheel staged" alone.
- ✅ **GStreamer known-broken guard repaired** — it compared space-delimited
  against a newline-separated list, so it stopped firing as soon as TWO
  plugins failed, with a new hard FAIL resting on it. **Watch:** no spurious
  ARCH-PARITY abort at the end of a green build.

- Media source-cache mounts, cerbero extra_mirrors fallback, C3 `:?`
  guards, TS4 llvm checkout keying (**PROVEN LIVE 2026-08-26**, log
  quote: `[INFO] Evicting stale llvm checkout llvm-project-22.1.8 (superseded
  by llvmorg-23.1.0)` — the LLVM 23 bump found a 22.1.8 tree in the cache mount
  and evicted it instead of silently reusing it, which would have shipped an
  image claiming 23.1.0 while containing 22.1.8; fold on ship), CERB-CACHE warm/evict behaviour,
  APT-HTTP restore (only if the fast-mirror knob is ON), DUP2 SSOT +
  literal gate — each shipped and reviewed; the rebuild is their proof.
  Fold them with log quotes in the post-ship evidence audit.

- ✅ **TS8 — ALREADY DONE, closed 2026-08-27.** Verified in the source rather
  than assumed: build_python.sh calls `ubuntu_write_deb822_source` from the
  shared ubuntu-mirror.sh writer, with a comment recording that the hand-rolled
  stanzas are gone and the output is byte-identical. Eight files now use that
  writer. The entry had simply gone stale.

- ✅ **DUP2 — deleted 2026-08-27 as its own text instructed.** The entry ended
  "Residual: nothing to build — just run preflight.sh before launch (AGENTS.md
  already says so). Delete on the next groom if no counter-evidence appears."
  This is that groom, preflight ran repeatedly today at 32/32, and no
  counter-evidence appeared.

- ✅ **QUEUED BUMPS — CLOSED 2026-08-27, three of four were already applied.**
  Re-checked with the tool rather than trusted: UV 0.12.6, CMAKE 4.4.3 and
  CARGO_C 0.10.25 all report "up to date", so they landed in an earlier wave and
  this entry had gone stale. Only OLLAMA had moved — 0.33.0 -> 0.33.1, both
  SHA256s written by bump_versions.py, "rebuilds: llm-stack image only" so it
  costs nothing on the cross chain. Followed by sync_versions --write and the
  check battery: version-snapshot, arg-consistency, env-knobs and (after
  regeneration) the curated SBOM are all green. VULKAN 1.4.357.1 stays blocked
  on the shared-key problem recorded above.

- ✅ **F6 — DONE 2026-08-27, both halves.** (a) ABSEIL now pins the
  DECOMPRESSED-STREAM hash (`ABSEIL_TARBALL_STREAM_SHA256=ec28d875…`), which
  outlives GitHub's "no less than a year" byte-stability pledge because the
  gzip container may be re-encoded while the stream cannot. BOTH sides moved
  together, as the old note demanded: `download_verified_file()` grew a
  `stream` mode, abseil-headers.sh passes it, and bump_versions.py writes the
  stream form under the new key via a new `sha256_of_gz_stream()`. Verified
  end to end — the tool computes exactly the pinned value, the shell accepts
  the right hash, rejects a wrong one, and rejects the stream hash in FILE
  mode, so the two modes cannot be confused. (b) ANDROID_CMDLINE_TOOLS needed
  no change: the repository2-3.xml sha1 cross-check is already written beside
  the key and the pin sits in the tool's manual-tier allowlist.

- ✅ **SCC1 — SUPERSEDED, closed 2026-08-27.** The entry already recorded that
  the owner reversed the 2026-08-17 rejection and that the C/C++ half is
  implemented; it was only ever still open because nobody ticked it. The Rust
  half followed on 2026-08-27 with the always-sccache decision.

- ✅ **SCC-BARE-FALLBACK — DECIDED and implemented 2026-08-27.** The owner's
  answer is "benutze immer sccache": guarded launcher where 01-core is
  mounted, bare sccache where it is not, never uncached.
  build-gstreamer-monorepo.sh was the one writer choosing uncached and now
  agrees with common.sh:449-450. The gate this entry produced was rewritten to
  assert the DECISION rather than the spelling -- the property is "never
  uncached", mutation-checked in both directions.

- ✅ **SCC-DIAG-LEFTOVERS — CLOSED 2026-08-27.** build-app-wheelhouse.sh no
  longer defaults SCCACHE_LOG to sccache=info, so IREE builds are quiet again
  (IREE_SCCACHE_LOG still turns it back on), and compiler-cache.sh's "Remove
  once the cause is known" note now states the cause: concurrent BuildKit RUN
  steps shared one sccache server because the port is not container-local,
  cured by SCCACHE_SERVER_UDS.

- ✅ **MANIFEST-FRESHNESS-WIRING — WIRED 2026-08-27.** verify-manifest-freshness.sh
  now runs from build-runtime-manifest.sh right after create_manifest, with
  EXPECT_RUN_ID threaded from CROSS_RUN_ID. Deliberately ADVISORY: it runs
  after the push, so failing hard could not unpublish anything and would only
  turn a finished run red; MANIFEST_FRESHNESS_STRICT=1 makes it fatal for CI,
  MANIFEST_FRESHNESS_GATE=0 skips it. Sensitivity-checked against the live
  index: PASS with this run's id, FAIL with any other.

- ✅ **BINFMT-UNIT-REINSTALL — DONE 2026-08-27.** The dev host's unit now
  carries `PartOf=containerd.service` and the fix was verified against a real
  containerd restart. The mechanism and the measurement live in AGENTS.md
  § Prerequisites (QEMU/binfmt) — one owner, not two.

- ✅ **XC2-PARTIAL-RUN — FIXED 2026-08-27 at the consumer, not the orchestrator.**
  runtime_android_pin returned empty whenever the android stage had not built
  in this run, so a `--only runtime` resume shipped wrappers carrying run-id
  and build-type but no parent-digest — which is what today's published index
  did. It now resolves the mutable android tag to a digest itself when the
  threaded pin is absent. Chosen over re-threading ANDROID_PIN through the
  orchestrator because that is a wide change with no validating run available;
  this is one function. Guarded on both halves (needs registry_pin_ref AND a
  configured repo), so a --no-push run behaves exactly as before. The unit
  test now pins the new contract and caught an unbound-variable crash in the
  first version of the fix.

## Closed 2026-08-28 — SHIPPED record, consumed phase plan, and verdicts

Moved out of `refactoring-backlog.md` so that file shows only OPEN work. The
2026-08-27 ship and its post-ship audit, the consumed CLOSURE WINDOW 3 phase
plan, the ALREADY-MAXIMAL anti-re-sweep block, the "already landed / watch"
items, the declined/disproven verdicts, and the consumed B3-PLAN reference.

### SHIPPED 2026-08-27 — and the audit that followed it

`:latest-cross` = index `a26bf2f4dbc8`, children amd64 `a0d1a144` / arm64
`2d354459` / riscv64 `7e0ed041`, run id `20260827-073226-d491cb10`. Freshness
VERIFIED against the registry: every index child matches its per-arch tag and
all three share this run's id.

The ship itself needed two rescues. The chain died after 5.5 hours on a QEMU
binfmt registration that the 2026-08-26 daemon restart had silently taken with
it — the guard for exactly that existed but sat AFTER the builds it protects.
Resumed with `--only runtime`, it then died again on an ARCH-PARITY arm that
correctly reported the IREE compiler wheel the same day's IREE fix had removed.

Then two audits read the result rather than the code, and between them found
24 defects — of which 22 were fixed, one refuted, and one retracted as my own
measurement error. The ones that had SHIPPED: Dockerfile.package expanded two
prefixes to empty inside their own ENV, so PATH carried `/bin` twice and
GST_PLUGIN_PATH pointed at `/lib/gstreamer-1.0`; the Android ONNX Runtime
carried Microsoft's 1DS telemetry because only the native lane passed
`--no_telemetry`; amd64 alone shipped a setuid-root gst-ptp-helper; Node.js was
an alpha its own npm refuses; and TVM had been missing from both cross arches
behind four stacked causes.

Three gates were reporting success over things they never tested, and are
fixed: `check_ffmpeg` fell back to an absolute path exactly when PATH
reachability broke, the app-wheel smoke printed one identical PASS over 15/15,
14/15 and 12/15, and the shipped-content byte gate hid behind a boot smoke so
riscv64's wrapper reached the index unchecked.

### The previous ship, for the record

**WAVE-6 SHIPPED 2026-08-24** (the gate-truth build — three blind gates now actually work)

`:latest-cross` = amd64 `a25a38c5` / arm64 `bd9953a9` / riscv64 `d3710282`.
Run id `20260823-223111-d0336283` — and for the FIRST time that is a
verifiable statement: all three wrappers carry it as an image LABEL, read back
from the REGISTRY (not the local store). cv2 GStreamer:YES + FFMPEG:YES holds
on the new bytes. Runtime smokes 0 failures x3.

**What this build proved** (each had been silently broken for months):
  - **XC3** — provenance is written and READ. `per-arch wrapper generation check:
  OK` with NO "carry no run-id" warning (it was 3/3 in every prior run), and
  `[ancestry] android→wrapper (<arch>): OK` with real digests on all three.
  The label also says *android*, confirming the XC2-STAGE fix: the writer
  stamped android while the check resolved "package", which would have thrown a
  false STALE ANCESTOR on every run the moment provenance returned.
  - **AP4** — `AP4 strip verified: libavcodec.so.63.1.100 has no .symtab`.
  Every previous ship printed `check skipped (could not extract ...)` while the
  wrapper gate said PASS: `tar --occurrence=1` exits early, SIGPIPEs the
  exporter, and pipefail turned that into a failure.
  - **SMK1** — the cv2 media assert is hard on all three arches.
  - **CERB-CACHE** — validated the hard way. wave6a lost all three lanes to five
  freedesktop 503/404 flaps, but the cerbero state survived (15.7 GB / 14.5 GB
  on the cachemounts); wave6b restarted WARM (`HIT: resuming ... 13G / 20G`)
  and completed with ZERO fetch failures. Before this, wave5k and 5l discarded
  the full ~50-minute bootstrap on every flap.

**Wave-5 (2026-08-23, superseded):** amd64 `54ab7f01` / arm64 `7bb70a4b` /
riscv64 `fb701200` — first ship with cv2 GStreamer+FFMPEG on 3/3 arches, and
the ship whose post-audit found the three gates above.

### CLOSURE WINDOW 3 — consumed phase plan (declared 2026-08-24, pre-build wave DONE 2026-08-27)

The operator committed to a fresh build, so the versions.env lock was OPEN
and build-validated items were IN SCOPE. The pre-build wave is worked through
and the validating rebuild ran. 27 unit suites / 677 assertions green,
shellcheck clean over 263 files plus the new SC2215 pass. Preflight 31/32:
the one red check is `docs cross-references`, all three findings Windows-lane
(they belong to the separate Windows backlog). The backlog went 46 → 30 in
the pre-build wave, then back to 39 when the 2026-08-27/28 run was mined (§ G,
§ F LOG19, § B LOG17/LOG18).

Two fixes in this wave are proven by EXECUTION, not inspection: TVM now
compiles and stages its arm64 wheels (four stacked causes, each only visible
once the one before it was fixed), and `PartOf=containerd.service` was tested
against a real `systemctl --user restart containerd`.

CORRECTED 2026-08-28: that PartOf claim was HALF the fix and the sentence
"now self-heals" was wrong. Both reboots that night lost the emulators anyway.
PartOf did fire — the unit re-ran in the same second — but it then FAILED with
`cat: /run/user/1000/containerd-rootless/child_pid: No such file`. `After=`
orders the UNIT start only; containerd-rootless still has to unshare and write
child_pid afterwards, so on a cold boot the binfmt unit wins the race. A manual
re-run always succeeds because containerd is warm by then, which is exactly why
the restart test looked green while every boot stayed broken. Fixed for real in
afefdfc (`wait_for_namespace()` polls for the pid file and a joinable namespace,
plus `Restart=on-failure`). LESSON: a unit that is `active` proves nothing —
prove emulation with a real foreign-arch run.

What the rebuild still has to prove is everything static analysis cannot: today
showed three separate times that a defect only becomes visible after the one in
front of it is gone.

### What the 2026-08-28 maximality audit found ALREADY MAXIMAL (anti-re-sweep)

- **FFmpeg is feature-identical across all three arches.** The `External
  libraries` block is byte-identical on amd64/arm64/riscv64 (45 libraries), and
  so is the hwaccel block. A riscv64 cross build that loses ZERO codecs against
  amd64 is rare — do not "improve" this.
- **Runtime SIMD dispatch is maximal and justifies the conservative `-march`
  baselines**: FFmpeg reaches AVX-512ICL (amd64), SVE/SVE2/SME/SME2 (arm64) and
  `RISC-V Vector enabled yes` + CBO prefetch (riscv64), each with runtime CPU
  detection. OpenCV dispatches to AVX512_SKX / NEON_BF16.
- **Declarative optimization is clean**: 41× `CMAKE_BUILD_TYPE=Release` and 0×
  Debug/RelWithDebInfo across all three media logs; meson `Optimization: 3`,
  `Debugging: false`; stripping centralized and proven on shipped bytes;
  `--gc-sections --as-needed -z now`; amd64 CPython is PGO+LTO.
- **GStreamer is the full monorepo on all three lanes**, Rust plugins included
  (riscv64 builds them in 3m44s), 220/229/222 plugins, amd64−arm64 difference
  empty.
- **The smokes that DO run are functional, not cosmetic**, and run per-arch under
  QEMU on the real shipped bytes: a real `InferenceSession` on a generated ONNX
  protobuf, `iree-compile` + `iree-run-module` with result checking, cv2
  GStreamer appsink with frame shape, torch forward+backward, torchvision
  `ops.nms` through the `._C` extension, an 8-case compiler battery.
- **The anti-fake-green discipline is first-rate**: scan-done stamps, EP
  sentinels, CMDOK markers, SIGPIPE fixes, and parity tables that fail when a
  documented exception STOPS applying.

### Already landed, validated by the rebuild alone (watch, no work)

- **BKD1 — buildkitd session rot: RESEARCHED, no upstream fix exists** [M·★,
  downgraded from ★★ 2026-08-24; host figures refreshed 2026-08-26] host runs
  buildkit **v0.31.2** (nerdctl 2.3.5, containerd 2.3.3) since the 2026-08-26
  upgrade. The session rot is UNAFFECTED by that upgrade. The symptom IS a known
  open upstream issue — moby/buildkit#6422 ("no active session … context deadline
  exceeded", open, no linked PR) and #5624 (same class during cache-export
  registry auth) — and NO release through v0.32.2 (2026-08-04) mentions a
  session/keepalive fix. VERDICT: keep the restart playbook (stop chain → restart
  buildkit.service → relaunch; cachemounts survive). The concurrency rider is
  now PARTLY taken: #6916 (daemon crash under concurrent builds) came with
  v0.31.2; **#6955 (parallel-build cache miss) did not** — it needs v0.32.0,
  which no nerdctl-full bundle ships. Do NOT chase it by installing buildkit
  outside the bundle: the pieces are version-matched. Re-open when a
  nerdctl-full carries 0.32.x.

### Verdicts (anti-re-sweep records — do NOT re-audit without new evidence)

- **S3 — per-stage registry cache refs: operator DECLINED 2026-08-24.** Design
  stays at docs/build-cache-tiers.md (DESIGN ONLY banner); cost ~12-13 GB
  registry upload per run was judged not worth it while prune-safe + local
  caches hold. Re-open ONLY on new evidence (another cold-rebuild loss). v1
  implementation history: reverted twice (fix7 gate token; inert-by-default
  with no coverage) — read the doc before any retry.
- **S5 — shared cargo cache ids: DECLINED, premise falsified 2026-08-26.** The
  entry claimed "cargo cache ids arch-independent". They are not: the media lane
  uses `id=cargo-registry-${TARGET_ARCH}` / `id=cargo-git-${TARGET_ARCH}`
  (Dockerfile.media:857-858), i.e. already per-lane BY DESIGN since PAR2. The 3x
  crate duplication is the accepted cost of lane isolation. Re-open only with
  new evidence that cargo guards a shared store safely.
- **PAR5 — a lone surviving lane stays throttled; the obvious fix is
  DISPROVEN** [S/M·★★, tried and REVERTED 2026-08-23] symptom unchanged: a
  single remaining wheelhouse crawls at 1-2 jobs for HOURS while the host idles
  (wave4f arm64, wave5h riscv64). DO NOT re-attempt the flag-dir live-lane-count
  approach that this entry used to propose — it was built, reviewed and
  reverted: BUILD_MEM_DIVISOR is a build-arg consumed as `ENV BUILD_MEM_DIVISOR`
  when the container build STARTS, so it cannot change while that build runs;
  the clamp could not fire at all in the shipped topology (all lane markers
  exist before any lane retires), and where it could fire it made the divisor
  depend on sibling timing and could drop the intra-step multiplier entirely
  (6→1) — an overcommit in exactly the direction PAR4 exists to prevent. A
  tripwire in test-stage-defs.sh now fails if flag-dir state is wired back into
  the divisor. Achievable options only: PAR4-hard (a host-level memory governor:
  systemd-run MemoryHigh per build, or a global compile-job server), or
  re-sizing at container-build/STAGE boundaries. Full verdict in
  docs/build-parallelism-memory-tuning.md.
- **Nondeterministic file-picks (2026-08-22, PKGCFG-MIRROR post-mortem)**: any
  `grep -rl … | head -1 / grep -m1` that CHOOSES a file to patch is
  readdir-order dependent — it can pick a different file inside the container
  than on the host and still echo success. The v1 pkg-config mirror override
  did exactly that (patched a stray .patch file, cold bootstraps kept 404ing)
  while the host reproduced "correct". Rule: patch the KNOWN path explicitly,
  treat the grep as a supplement, and echo the RESULT (the patched line) as
  proof, never the intent.
- **CI sweep (2026-08-17)**: CI1-3 fixed same day (timeouts, ollama digest-pin,
  env-var login); otherwise CLEAN.
- **Idempotency / GST1 runs-twice class (2026-08-17)**: CLEAN — only
  install-deps + configure-runtime run twice, both second-run-safe; this
  section is the checklist for any NEW double-run path.
- **Bump-tool (2026-08-19)**: BT1+BT2 fixed same day (spec_vulkan SHA,
  artifact-gating, empty-download rejection).
- **A1 allowlist (2026-08-18)**: 148/148 knobs have live readers.
- **onnxruntime 1.28-vs-1.27 (2026-08-15)**: NO source dedupe to do — runtime
  __version__ quirk, already asserted as a union in the smoke; residual folded
  into C3.
- **Version-ARG mirrors / dead stages / USER-ordering (2026-08-17 sweep)**:
  zero drift, clean.
- **Coverage map (2026-08-10, unchanged)**: chain swept end-to-end across six
  rounds; thin spots = GPU lanes, Windows psm1 (sampled), lib/ beyond smoke,
  benchmark-viewer. THE REMAINING DISCOVERY CHANNEL IS RUNS — the classes that
  matter (cache-bust latents, foreign-arch paths, timing/OOM) only surface in
  real rebuilds.

### Reference (consumed)

- **B3-PLAN**: CONSUMED by wave-4 (all listed bumps applied 2026-08-18, incl. the
  F7-coverage PY_* set; TENSORFLOW_C landed at 2.18.1 — last version with a
  published C tarball). Next refresh at the next window via
  `bump_versions.py --check` (now artifact-aware).

## Closed 2026-08-28 (code fixes) — Linux backlog maximality audit

Moved out of `refactoring-backlog.md`. Each was found in the 2026-08-28
maximality audit and fixed with code + assertions in the same session.

- **LOG10 — riscv64 OpenCV RVV false comment** [CLOSED] Fixed the false comment
  in `opencv/android/build-android.sh` that claimed "the Linux riscv64 OpenCV
  keeps RVV under GCC" — the cross build detects no RVV (DETECT finds nothing in
  a cross build). The actual RVV enablement decision remains open as a separate
  item if needed.
- **LOG11 — OpenCV TBB on amd64 only** [CLOSED] Moved `libtbb-dev` from
  `install_deps_preamble` (host) to `target_packages` in
  `03-media/build/opencv/install-deps.sh` so the cross lanes pull the
  target-arch package. Added `Parallel framework: TBB` assertion to
  `smoke-media.sh` after the OpenCV videoio roundtrip.
- **LOG15 — android OpenCV BUILD_JAVA=ON produces no Java wrappers** [CLOSED]
  Changed `BUILD_JAVA=ON` to `OFF` in `opencv/android/build-android.sh` (it
  produced no wrappers anyway — ant was absent). Added `check_opencv()` to
  `smoke-android.sh` asserting Java wrappers are absent.
- **LOG16 — CMAKE_POLICY_VERSION_MINIMUM=3.5 as eight literals** [CLOSED] Added
  `CMAKE_POLICY_VERSION_MINIMUM=3.5` to `versions.env`. Replaced all 8 bare
  literals across 6 files with `${CMAKE_POLICY_VERSION_MINIMUM:-3.5}` (or
  `:=3.5` in android scripts to avoid the version-forwarding test tripwire).
- **LOG20 — FFmpeg ships no drawtext filter** [CLOSED] Added `libharfbuzz-dev`
  and `libfontconfig1-dev` to ffmpeg `install-deps.sh`; added
  `--enable-libharfbuzz`/`--enable-libfontconfig` probes to `build-ffmpeg.sh`.
  Added `drawtext` filter registration assertion to `smoke-media.sh`.
- **LOG22 — FFmpeg enables Vulkan but ships zero *_vulkan filters** [CLOSED]
  Added `glslang-tools` to `install_deps_preamble` in `install-deps.sh`
  (provides `glslangValidator` on PATH for SPIR-V shader compilation). Added
  `scale_vulkan` filter registration assertion to `smoke-media.sh`.
- **LOG23 — shipped Pythons have no readline/curses** [CLOSED] Added
  `libreadline-dev` (required) and `libncurses-dev` (optional) to
  `_CPYTHON_EXT_DEV_PKG_TABLE` in `cpython-dev-packages.sh`.
- **LOG25 — LiteRT GPU/NPU hard-off with no rationale** [CLOSED] Added
  rationale comment to `build-litert.sh` explaining why GPU and NPU delegates
  are OFF (no cross-buildable GPU delegate; no NPU SDK staged).
- **LOG29 — wrapper-smoke stage never built** [CLOSED] Added
  `_runtime_run_package_smoke()` to `runtime-build-fns.sh` that builds the
  `--target wrapper-smoke` stage between package and wrapper.
  `WRAPPER_SMOKE_GATE=0` to skip. Added unit test
  `test-runtime-smoke-gate.sh` (8 assertions).
- **LOG30 — no OPTIMIZATION property assertion** [CLOSED] Added
  `_smoke_optimization_level()` to `validate-compilers.sh` checking CPython
  `sysconfig.get_config_var('OPT')` for `-O0`/missing `-O`. Called from
  `validate_smoke()`.
- **LOG36 — libtvm*.so dropped at media-to-package COPY** [CLOSED] Added
  `libtvm.so*`, `libtvm_runtime.so*`, `libtvm_compiler.so*` to the
  copy_glob allowlist in `copy-media-payloads.sh`. Added the same entries to
  `so-package-map.txt` as `source-built`. Added TVM lib verification to
  `verify-media-artifacts.sh` media-inputs stage.
- **LOG37 — docs say strict smoke set closed the orphaned-smoke class; it lives
  in a stage nothing builds** [CLOSED] Updated `cross-build-verification.md` to
  state wrapper-smoke runs as a separate `--target wrapper-smoke` build.
- **LOG38 — AGENTS.md PartOf binfmt claim** [CLOSED] Corrected the PartOf
  binfmt claim in AGENTS.md to mention `wait_for_namespace()` +
  `Restart=on-failure`.
- **LOG39 — NVIDIA/AMD build recipes broken** [CLOSED] Fixed all NVIDIA+AMD
  build recipes in `docs/linux-accelerator-images.md` (`:sdk` to
  `:cross-sdk-amd64`, `--output type=image,name=...,push=true` to
  `-t ... --push`). Removed `:latest` row from `docs/overview.md`.
- **LOG40 — license/SBOM docs drifted from generator** [CLOSED] Verified
  green — no drift found in version-snapshot or SBOM gates.
- **LOG41 — :latest deleted from registry, docs still reference it** [CLOSED]
  Removed `:latest` row from `docs/overview.md`, fixed `:latest` to
  `:latest-cross` in `docs/linux-cross-builds.md`.

## Closed 2026-08-28 (host-only fixes) — backlog items F1

Moved out of `refactoring-backlog.md`. These three touched only host-only scripts
(not COPY'd or bind-mounted into any Dockerfile), so they could be fixed outside
a closure window. Each was fixed with code + verified via preflight/tests.

- **LOG33 — `verify-shipped-wrapper.sh` had only two hard assertions** [CLOSED]
  Promoted check 3 (onnxruntime .so presence) from advisory to HARD — the image
  is built around ORT, so its absence is always a defect. Promoted check 5 (AP4
  strip) from advisory to HARD when the sentinel lib was successfully extracted
  — a surviving .symtab means the MEDIA_STRIP pass regressed. Kept advisory only
  when extraction itself failed (cannot assert on missing evidence). x265 stays
  advisory (static linking makes a missing shared lib non-proof). The script now
  has four hard assertions (ffmpeg, libtensorflow toggle, onnxruntime, AP4 strip).
- **LOG31 (preflight half) — two preflight checks were structurally incapable of
  failing** [CLOSED] `lint-env-knobs.sh` was advisory by default (exits 0 unless
  KNOB_GATE=1) and its preflight invocation omitted KNOB_GATE=1, so it always
  passed; additionally the `if [ -f ]` guard had no `else`, so a missing file
  silently dropped the check. Fixed: preflight now invokes it with
  `KNOB_GATE=1` and has the same FAIL-not-skip `else` contract as the
  version-snapshot check. Three previously-unowned operator knobs
  (BUILD_ATTEST, CROSS_DISK_GUARD_GB, NO_CACHE_EXPORT) were added to
  `lint-env-knobs.allow`. `verify-runtime-paths.sh` was "ADVISORY ONLY — never
  fails" — it now fails hard on infrastructure errors (missing
  runtime-paths.env, versions.env, or a tracked Dockerfile) while keeping the
  heuristic path-mismatch WARN lines advisory (they produce false positives on a
  healthy tree). The COPY'd half of LOG31 (validate-media-runtime.sh,
  smoke-android.sh) remains OPEN — it needs a closure window.
- **Section C — --no-push multi-stage handoff resolves parents against the
  registry** [CLOSED] Added `_chain_no_push_guard()` to `build-cross-chain.sh`
  that refuses `--no-push` for multi-stage runs (FROM_STAGE != TO_STAGE) on
  this host: BuildKit's OCI worker resolves `FROM` against the registry, not
  the local store, so downstream stages silently build on the last PUSHED
  parent (two runs lost 2026-08-08). Single-stage (`--only`), dry runs, and the
  `CROSS_NO_PUSH_FORCE=1` escape hatch are allowed. The guard runs before any
  build or disk check. The full OCI-layout export + `--build-context` handoff
  (which would make `--no-push` multi-stage actually safe) remains a future
  improvement; the refusal prevents the silent data-loss path until then.

## Closed 2026-08-28 (closure window — 03-media/06-packaging batch)

All edits in this section were batched into ONE commit to minimize cache
invalidation (the 03-media/06-packaging closure re-runs hours of compiles
on any edit).

- **LOG31 (COPY'd half) — DENIED class and Android presence checks now fail**
  [CLOSED] `validate-media-runtime.sh`: the DENIED class (source-built SONAME
  missing — meant to be empty by construction) now exits 1 instead of WARNING.
  The broader unresolved/unmappable classes stay advisory (vendor trees must
  be excluded first). `smoke-android.sh`: ndk-build and aapt2/zipalign/apksigner
  now have `fail` branches — a missing NDK clang or apksigner previously passed.
- **LOG32 — Vulkan smoke checks lifted into runtime smoke** [CLOSED] Added a
  Vulkan section to `smoke-media.sh` with three gates: `vulkan/vulkan.h` present,
  the `active` symlink resolves, and `glslangValidator --version` runs. Together
  ~7 GB of shipped Vulkan SDK now carries a gate that can fail.
- **LOG35 — smaller gate gaps, one pass** [CLOSED] `verify-media-artifacts.sh`
  media-inputs: replaced the broken `verify_A || verify_B` lib→lib64 fallback
  with `verify_any_lib` (the old idiom counted A's failure even when B passed).
  Added `libvvdec` to the FFmpeg buildconf-vs-registered codec check loop.
  Added a `/opt/cmake` functional assertion to `smoke-media.sh`.
- **LOG9 — arm64 GStreamer introspection enabled** [CLOSED]
  `build-gstreamer-monorepo.sh`: arm64 cross builds now keep introspection
  ENABLED when the qemu g-i wrappers exist (pre-setup.sh already creates them
  for arm64, same as riscv64), falling back to disabled only if they don't.
  Mirrors the riscv64 block with `gi_cross_binary_wrapper` /
  `gi_cross_ldd_wrapper` meson args. Success is measured by the `.typelib`
  count (was 0 on arm64, 47 on amd64, 38 on riscv64).
- **LOG21 — OpenCV highgui: cross arches documented as headless-by-design**
  [CLOSED] Cross arches disable GTK (libpango1.0-dev is not
  multiarch-coinstallable). Added a smoke assertion in `smoke-media.sh` that
  confirms `GUI: NONE` is deliberate on cross arches and fails on a NATIVE
  build that loses GTK.
- **LOG24 — OpenCV ONNX Runtime DNN backend enabled** [CLOSED] Added
  `-DWITH_ONNXRUNTIME=ON -DDOWNLOAD_ONNXRUNTIME=OFF` to the OpenCV cmake core
  opts. The image ships a source-built ONNX Runtime; cv2.dnn can now use it.
  Added a smoke assertion that checks the ORT backend is available via
  `cv2.dnn.getAvailableBackends()`.
- **LOG26 — OpenCV AVIF/HDF5/non-free + riscv64 torch USE_OPENMP + FFmpeg
  PulseAudio** [CLOSED] Added `-DWITH_AVIF=ON -DWITH_HDF5=ON
  -DOPENCV_ENABLE_NONFREE=ON` to OpenCV cmake opts and `libavif-dev` /
  `libhdf5-dev` to install-deps.sh. Documented the riscv64-only
  `USE_OPENMP=0` rationale in `build-app-wheelhouse.sh` (cross GCC's libgomp
  not coinstallable in the riscv64 sysroot; amd64/arm64 get PyPI wheels with
  OpenMP). Added `libpulse-dev` to FFmpeg install-deps.sh for the
  PulseAudio input/output device.

## Closed 2026-08-28 (closure window — 01-core/02-toolchain batch)

All edits in this section were batched into ONE commit to minimize cache
invalidation (the 01-core / 02-toolchain / 03-media closure re-runs hours of
compiles on any edit). LOG12 was documented but remains OPEN as a decision
item (see backlog).

- **LOG8 — the locked apt mounts serialize the entire intra-lane fan-out**
  [CLOSED] Added per-stage apt mount ids to the six parallel media head stages
  in `Dockerfile.media` (onnx `-onnx`, litert `-litert`, tvm `-tvm`, opencv
  `-opencv`, app-wheelhouse `-wheelhouse`, ffmpeg `-ffmpeg`). Each head vertex
  now holds its own apt lock instead of a shared one, so they no longer
  serialize. Armnn already had its own ids. Leaves PAR2 (lane axis) alone. The
  memory caveat from the original finding (peak 43.6 GB with only tvm+litert
  live) still applies — this removes the lock contention, not the memory
  pressure; PAR4-hard remains the real cap.
- **LOG13 — the android stage does not know sccache exists** [CLOSED]
  `android-build-preamble.sh`: `android_build_preamble_init` now sources
  `compiler-cache.sh`, calls `setup_ccache`, and `setup_lld_linker`, so the
  android stage gets the same `compiler_cache_launcher()` resolution as the
  media lane. android-iree (previously the only genuinely uncached android
  lib, 524 s amd64 / 386 s arm64) now resolves through the launcher. The five
  RUN blocks in `Dockerfile.android` stay byte-identical
  (verify-android-stage-parity.sh).
- **LOG14 — the cross SDK lanes build host x86_64 Vulkan components nobody
  consumes** [CLOSED] `vulkan.sh`: added nine host-only Vulkan components to
  the `_vulkan_skip` map for cross builds: ValidationLayers, shaderc,
  SPIRV-Cross, SPIRV-Reflect, Vulkan-Profiles, Vulkan-ExtensionLayer, volk,
  VMA, vul. The cross lanes no longer build ~390 s of host x86_64 components
  that have no cross consumer. The clones that fill
  `source/SPIRV-Tools|glslang|Vulkan-Loader` (needed by `_build_vulkan_targets`)
  are preserved.
- **LOG17 — lift the Android AGP/Gradle versions into versions.env** [CLOSED]
  Added `ANDROID_AGP_VERSION=8.3.1` and `ANDROID_GRADLE_VERSION=8.7` to
  `versions.env` after `ANDROID_API_LEVEL=34`. These are visibility pins —
  `bump_versions.py --check` can now see them. The actual version still lives
  inside `patches/onnxruntime/001-android-gradle-agp8-compat.patch:10,37`, so
  a bump must sync BOTH the pin and the patch. The Gradle 9.0 deprecation
  warning (`android-arm64.log:5996`) is now visible to the bump tool.
- **LOG18 — CPython falls back to bundled libmpdec on all three arches**
  [CLOSED] Added `libmpdec-dev optional _decimal` row to
  `_CPYTHON_EXT_DEV_PKG_TABLE` in `cpython-dev-packages.sh`. Marked OPTIONAL
  (not required) because promoting to required flips all three arches from
  bundled-static to a dynamic `libmpdec.so` with so-package-map consequences.
  Harmless until Python 3.16 (~Oct 2027); the row documents the gap and makes
  the switch a one-line change when the time comes.
- **LOG19 — the media lane reports zero cache telemetry, and the report is
  truncated where it counts** [CLOSED] Replaced positional `head -N` with
  `grep -E` for sccache stats in four files: `compiler-cache.sh:201`,
  `build-gcc.sh:737`, `probe-sccache.sh:141`, `03-media/core/common.sh:147`.
  The grep selects the lines that matter (`Compile requests`, `Cache hits`,
  `Non-cacheable compilations`, `Unsupported compiler calls`) instead of
  slicing a variable-length report. Added `dump_compiler_cache_stats()` to
  `compiler-cache.sh` — a post-build stats dump with a WARN on
  `requests > 0 && hits == 0` (dead cache). Wired it via an EXIT trap in
  `media_common_init` so every media step reports its cache telemetry at the
  END of the build, not at t≈0.165 s before the first object.
- **ffmpeg/pyav compiler-cache resolution** [CLOSED alongside LOG19]
  `build-ffmpeg.sh:390-403` and `build-pyav.sh:104-112` hardcoded `ccache ${CC}`
  instead of resolving through `compiler_cache_launcher()`. Replaced with the
  helper so sccache is actually asked (was always ccache —
  `media-amd64.log:111146 "Using ccache for faster compilation"`).

## Closed 2026-08-28 (closure window — enable + decision batch)

All edits batched into ONE commit to minimize cache invalidation.

- **LOG12 — ArmNN NEON backend enabled** [CLOSED] `build-armnn.sh`: added
  `-DARMCOMPUTENEON=1` to the cmake args so ArmNN builds with the ACL NEON
  (CPU SIMD) backend. CL stays OFF (no GPU device in the cross-build
  container; OpenCL headers not staged for cross). TOSA Reference stays OFF
  (upstream default). A real build may surface an ACL cross-link break — that
  would be the actual news the backlog item predicted.
- **GCC_HOST_BOOTSTRAP — default changed to 0** [CLOSED] `Dockerfile.toolchain`:
  changed `ARG GCC_HOST_BOOTSTRAP=1` to `=0`. The flag already existed and
  worked (gcc.sh:171-200 passes `--disable-bootstrap` when 0). The decision:
  `=0` for validating rebuilds (saves ~1231 s of stage2+stage3 bootstrap);
  `=1` only when the GCC pin moves (the bootstrap self-check catches
  miscompiles). The proposal sat since 2026-08-10; the stats (6.63% hit rate
  on the bootstrap, 88.56% after all 5 GCCs) quantified the cost.
- **LOG2 — wasm asyncify/jspi flavors built** [CLOSED] The build passes
  already existed: `40-build-wasm.sh` has Pass 4 (asyncify) and Pass 5 (JSPI),
  gated by `ORT_WASM_WEBGPU_FLAVORS=true` (default ON in lib/common.sh).
  `50-build-js.sh` emits the ort.webgpu*/ort.jspi* JS bundles when the WASM
  artifacts are present. The versions.env comment was stale (said "not built"
  when the code builds them). Updated the comment. Watch: a real build must
  confirm the artifacts ship on all arches.
- **GPU bare-sccache sites resolved through launcher** [CLOSED] The three
  CUDA/HIP sites that wrote bare `sccache` (build-opencv.sh,
  30-build-native-nvidia.sh, 30-build-native-amd.sh — all under
  `ENABLE_SCCACHE_CUDA`, default 0) now resolve through
  `compiler_cache_launcher()`. Only sccache-class launchers are accepted
  (ccache can't wrap nvcc/hipcc); the guarded sccache-launcher.sh is preferred
  when mounted, bare sccache as fallback.
- **TVM compiler launcher added** [CLOSED] `tvm-config.sh`: added
  `CMAKE_C_COMPILER_LAUNCHER` and `CMAKE_CXX_COMPILER_LAUNCHER` via
  `compiler_cache_launcher()` in `append_tvm_cmake_args()`. Overrides TVM's
  internal `USE_CCACHE=AUTO` so the decision goes through the standard
  resolver (sccache first, ccache fallback, guarded launcher when mounted).
- **Two-caches-installed decision** [CLOSED] Documented in compiler-cache.sh:
  both ccache and sccache stay mounted. sccache is primary; ccache is the
  documented fallback for invocations sccache refuses. The ~27 GB warm ccache
  is the intentional cost of that fallback.

## Closed 2026-08-30

- **LOG34 — TVM's version assert armed** [CLOSED 2026-08-30] `EXP_TVM` is now
  set from `TVM_REF` in versions.env, making TVM a hard assert in
  smoke-torch-venv.sh. The build proved TVM ships on all three arches (amd64,
  arm64, riscv64). Version check compares major.minor only, since the wheel
  is a dev tag (0.26.dev1) that doesn't match the git tag (0.26.0) exactly.
- **GPU-ROCM10 — ROCm 10.0 TheRock verification build** [CLOSED 2026-08-30]
  `Dockerfile.amd` builds successfully. All post-install assertions pass after
  adapting `setup-rocm-repo.sh` to TheRock's versioned-subdir layout
  (`/opt/rocm/core-10.0/`): convenience symlinks for `/opt/rocm/bin`,
  `/opt/rocm/include`, `/opt/rocm/lib`; library check uses `find` instead of
  `ldconfig` (per-GFX libs in subdirs, not flat).
