Refactoring backlog — CLOSED 2026-08-31 (A1 closure window + GEN1)#
Split out of
refactoring-backlog.mdso that file shows only OPEN work. Nothing here needs action; it is kept because the entries record WHY a thing is the way it is — including two closed by being REFUTED rather than fixed, the kind of finding that gets re-discovered if the reasoning disappears. Earlier rounds:refactoring-backlog-archive-2026-08-10.md,refactoring-backlog-archive-2026-08-27.md,refactoring-backlog-archive-2026-08-30.md.
Full narrative in CHANGELOG.md (2026-08-31, “Linux backlog closure window”).
Gates at closure: make lint clean (276 files), make preflight green,
make test-linux-scripts 38 suites / 1001 assertions. No container build
was run — everything here is static-gate-proven only.
Closed on 2026-08-31#
✅ logging.sh ERR-trap dynamic-scope bug — FIXED.
_install_trap’son_errread the reporting action from the installer’slocal actionvia dynamic scope; the trap fires long after the installer returns, so underset -uthe handler died withaction: unbound variable, REPLACING the real error (it masked the 2026-08-30 parallel-GCC apt-lock failure) and never runningerr/warnat all.on_erris now top-level and the action is baked into the trap string viaprintf -v '%q', withLINENO/BASH_COMMANDstill expanding AT FIRE TIME._LOG_TRAP_ACTIONexists forbuild-gcc.sh:709, which re-arms the BARE trap string by hand aftertrap - ERR— a caller the original brief did not know about. Regression suitetests/test-logging-err-trap.sh(30 assertions) verified to fail 29/30 against the pre-fix file. NB behaviour note: inside aset +ewindowinstall_err_trapnow EXITS 1 where it used to print noise and continue. No current caller has such a window (checked repo-wide); this is the documented intent, but it is a real change if one is ever added.✅ Complexity-queue survivors — DONE. (
_cross_stage_build_implwas already a single impl behind two thin wrappers, closed 2026-08-30 — do NOT re-add it to this queue.)append_tvm_cmake_args15 positionals → named options (both call sites,tvm.sh+tvm-python.sh, with golden-value tests asserting the emitted array is byte-identical);_build_vulkan_targets(137 lines) and_llvm_cross_setup_and_build(146 lines) decomposed along real seams;build_iree_wheelssplit into nine_iree_*stages;base-image.sh parse_options(116-line nested case/while) collapsed to a data table preserving the--ports-url${2-}vs--archive-url${2:-}empty-vs-unset asymmetry, theinstall-vulkan-runtime-files--/-*/*passthrough with itsREMAINING_ARGS, and every die-message string verbatim.
append_tvm_cmake_args — the keyword contract#
linux/scripts/05-frameworks/tvm-config.sh. Appends TVM’s -D flags to the
caller’s array, which is passed by name (nameref). Options are keywords,
not positions: the predecessor took 15 positionals of which the last six were
optional-with-defaults, so a dropped or transposed argument produced a silently
WRONG feature set that surfaced only hours into the build — or, worse, as a
mis-featured shipped wheel. An unknown option is now a hard die.
Required — must be PRESENT, though the value may legitimately be empty:
Option |
Effect |
|---|---|
|
name of the caller’s array to append to |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Optional, with defaults: --use-cuda (0), --use-opencl (0),
--spirv-tools-lib (empty), --cross-link-flags (empty), --vulkan-library
(empty), --vulkan-include (empty).
Two implementation details that are easy to undo by accident:
Every internal local is
_tvm_-prefixed. A nameref whose target resolves to a local of the same function is a circular reference; the oldout_ref/llvm_dir/ … names were in exactly that line of fire, since the caller’s array name is arbitrary.The presence loop over the nine formerly-mandatory positions exists so a dropped option fails at the call, not as a wrong
-Dflag three hours into the compile. It tests${_tvm_seen[$opt]+x}, not:-, so an unset key is safe underset -uon bash 4.3 as well.
Both call sites (tvm.sh, tvm-python.sh) are covered by
linux/scripts/tests/test-tvm-cmake-args.sh, which asserts the emitted array is
byte-identical to the positional version’s.
base-image.sh parse_options — the option table#
BASE_IMAGE_OPTION_SPECS maps "<command> <flag>" to
"<target-var>|<value-mode>|<side-effect>", and base_image_apply_option
applies one <flag> <value> pair or dies with the per-command unknown-argument
message. The value passed is the caller’s "${2-}", so an absent value
collapses to empty and every mode below handles that.
value-mode |
Meaning |
|---|---|
|
value must be non-empty ( |
|
an EMPTY value is legal and is assigned verbatim |
|
required, then validated/normalized by |
The
--archive-url/--ports-urlasymmetry is REAL, not an oversight: an empty--ports-urlis accepted and means “derive the ports mirror from the archive URL”, while an empty--archive-urlis a caller bug. Do not harmonize them.boolassigns through the nameref rather than via a temporary, so that whenparse_bool_flagdies inside its command substitution the target lands empty andset -eends the process — byte-for-byte what the pre-tableVAR="$(parse_bool_flag …)"arm did.The single side effect any flag carries beyond its own assignment is
use-fast-mirror: naming a mirror URL implies opting INTO the fast mirror.install-vulkan-runtime-filesis the ONLY command that takes positionals. It stops at the first non-flag (or at an explicit--) and hands the rest toinstall_vulkan_runtime_filesthroughREMAINING_ARGS, which no other command sets.
Covered by linux/scripts/tests/test-base-image-parse-options.sh.
Three seam contracts the decomposed helpers now depend on, none of which is visible from a helper’s own body — a later “cleanup” that breaks either one fails only in a multi-hour cross build:
vulkan.sh— the_vulkan_target_*helpers. They are the inline body of_build_vulkan_targets(copy headers → loader → SPIRV-Tools → glslang → verdict), and the split preserves two things. (1) Every call is a plain statement — never inside anif, a&&chain or a$( )— soset -estays live inside the helper exactly as it was when the code was inline; moving one call into a condition would silently disable errexit for that whole helper. This is why_vulkan_target_link_glslang_aliasesends in an explicitreturn 0: its last statement is a[ -e … ] && lnloop whose test is false whenever the final name is absent, so without thereturnthe helper exits non-zero and aborts the SDK stage. Inline, the next statement was the_vk_ok=assignment, which always reset$?to 0. A genuinely failinglnstill aborts — errexit fires on it first. (2) They read the caller’s_xbuild_cc/_xbuild_cxx/_xbuild_procand update its_vk_attempted/_vk_okcounters by dynamic scope (the same contract_cross_build_sdk_componentalready carried). None of them may declare those five nameslocal, and none is callable from outside_build_vulkan_targets— underset -uthe counters would be unbound. The step ORDER in the caller is also a contract: headers before loader, because the loader build needs them.tests/test-vulkan-target-decomposition.shpins the order, the per-component argv and thereturn 0.llvm-cross.sh— nameref name collisions. The helpers take the state map and the out-arrays BY NAME and bind them withlocal -n. A nameref whose target has the same name is a bash circular reference, so the_build_llvm_cross_corestate array must not be called_r,_cfgor_bi(the names the pre/post-build, configure and install helpers bind it with), and the same holds for the arrays passed to the_llvm_cross_*_argshelpers. Getting it wrong turns every write into an “unbound variable”. Separately,_llvm_cross_cmake_configurereadsCC,CXX,AR,CROSS_TARGET_*andCMAKE_SYSROOTfrom the environment thatsetup_linux_cross_envexported into_llvm_cross_setup_and_build’s subshell, so it is only callable from inside that subshell.tests/test-llvm-cross-stanza.shpins the injected arg groups’ insertion points and the four build/install calls.build-app-wheelhouse.sh— the_iree_*stages. The nine helpers are the inline body ofbuild_iree_wheels, in pipeline order, and they follow the convention_torch_run_setup_pyalready used in that file: each one readsbuild_iree_wheels’ locals through dynamic scope, and several write back into them, so none of those names may be declaredlocalin a helper. Concretely:_iree_check_prereqssetswheel_platform._iree_setup_compiler_cachefillsccache_cmake_argsand sets the global_iree_launcher, andexports theCCACHE_*/SCCACHE_*environment that every later build step relies on. It never fails.ccache_cmake_argsis declared in the CALLER for exactly this reason: alocal -ainside the helper would hide the array from the build stages below it._iree_fetch_sourceand_iree_patch_setup_py_abi3work onsrc_dir._iree_append_qnn_cmake_argsappends tocmake_args._iree_build_host_stagereadssrc_dir,host_build,host_install,host_cc,host_cxxandccache_cmake_args._iree_build_target_crossreadssrc_dir,target_build,host_install,host_cc,host_cxx,cmake_args,ccache_cmake_argsand_iree_launcher; it setstoolchain_file,iree_target_triple, furthercmake_argsandiree_wheel_projects, and it deliberately leaves the exported TARGET-Python sysconfig (_PYTHON_SYSCONFIGDATA_NAME) in place, because the wheel-packing step still needs it._iree_build_target_nativereadssrc_dir,target_buildandccache_cmake_args, and leavesiree_wheel_projectsEMPTY so the packaging step defaults to both wheels — that empty default is what keeps the native path byte-identical to the pre-split code._iree_package_wheelsreadstarget_build,dist_dirandwheel_platform, may defaultiree_wheel_projects, and is called LAST frombuild_iree_wheels, so ITS status is the function’s status (there is no trailing|| return 1at that call site, and adding one is harmless but redundant).
host_cc/host_cxxare resolved in the CALLER, not inside_iree_build_host_stage, because BOTH cross stages need them: stage 1 builds the host tools with them and stage 2 pins bundled LLVM’s NATIVE tblgen sub-build to them viaCROSS_TOOLCHAIN_FLAGS_NATIVE.Failure convention: every helper that can fail
return 1s, and the caller turns that into its ownreturn 1— i.e. skip IREE, never abort the wheelhouse mid-flight. (build_iree_wheelsreturning non-zero is then FATAL inmain(), since IREE is required on every arch.)tests/test-iree-wheelhouse-stages.shpins the call order and the|| return 1at each fallible call site.✅ modules.sh dir-walker — CLOSED by REFUTATION. Do not re-flag. All four suspected defects were probe-tested and refuted:
_find_scripts_rootprovably terminates for relative, absolute, dot-relative, empty and trailing-slash inputs and cannot cycle (the walk is lexical, so symlinks are irrelevant); thereturn 1signal IS consumed correctly, because a success path can never print an empty string (the[ -n "$d" ]guard), making[ -n "${root}" ]exactly equivalent torc == 0;BASH_SOURCE[1]is the call-site file at ANY nesting depth (verified with a 3-deep cross-file chain), so the “wrong frame when nested” hypothesis is a misreading of the FUNCNAME/BASH_SOURCE pairing; and the space-joinedsearched:list is ambiguous only for paths with spaces, which cannot occur here. What remains is a while loop that looks like a while loop — in a file that is in the base/compiler/media cache closure and whose last mistake was an infinite re-source loop ending in SIGSEGV. Style churn here is a NET NEGATIVE.✅ IREE regression suite over-claimed its coverage — FIXED (toothless-gate class). The new
test-iree-wheelhouse-stages.shheader claimed a missing|| return 1at any call site would fail an assertion; only 2 of 5 sites were actually mutation-covered. Two compounding causes, both now recorded in the file: (1) the first fault-injection attempt silently did NOTHING because the hostgrepis ugrep, which parses a pattern beginning--as an option — fixed withgrep -qE -e, with a comment saying why-eis load-bearing; (2) even with injection working, assertingrc == 1did not discriminate, because_iree_package_wheelsbails at its own[ ! -d "${target_build}/${_proj}" ]guard and returns 1 anyway — the right answer for the wrong reason, and with “produced no runtime/ wheel project” replacing the real configure failure plus its 80-line log. The cases now also assert the PACKAGING diagnostic is ABSENT, which is what actually pins the early abort. All five call sites are now mutation-verified to fail ≥1 assertion.✅ GEN1 — genai wheel for riscv64 — BUILT and WIRED ON (user decision 2026-08-31; the entry’s “only if it has a user” gate was answered). riscv64 now takes the same cross path arm64 takes: the hard arch guard is gone and the cross allowlist is an explicit
arm64|riscv64. Effort was far below the “L” estimate — the arm64 cross path was already arch-generic (CROSS_TARGET_TRIPLET/CROSS_TARGET_PROCESSOR/CROSS_RUST_TARGET/cross_wheel_platform_tag) andplatform.shalready mapped riscv64 completely (triplet,riscv64gc-unknown-linux-gnu,linux_riscv64, ELFRISC-V).One upstream blocker, one-hunk patch:
patches/onnxruntime-genai/001-riscv64-target-platform.patch.cmake/target_platform.cmake’s Linux branchFATAL_ERRORs on any processor that is not arm64/x64/powerpc — configure dies before a single object compiles.genai_target_platformis read ONLY underWIN32/ENABLE_JAVA/ MSVC, so the added arm is inert everywhere else. Proven to apply and re-apply idempotently against a real clone of the pinned tag v0.15.2 (ed5f4e87). The rejected alternative (lying aboutCMAKE_SYSTEM_PROCESSOR) is recorded ingen1-riscv64-genai.md, not shipped. (The patch file itself is 13 lines of pure diff, no comment block.)--use_guidanceKEPT on riscv64 — but note it is auto-DROPPED with only a WARN if a riscv64-only preflight finds rustup lacks the std, which ships a parity-divergent wheel no gate can see (watch item, section A1). (Rust Tier-2-with-host-tools; the crate graph Corrosion imports is pure Rust;install-rust.shadds the std for everyCROSS_TARGETSarch). Dropping it would have shipped a different wheel than amd64/arm64 that the parity table could not distinguish.Escape hatch
GENAI_ALLOW_RISCV64restores the pre-GEN1 placeholder-and-skip exactly. TWO review defects fixed: it never reached the in-container smoke (nerdctl runinherits nothing, and the media final stage isFROM media-inputs, so the ARG/ENV on theonnxruntimebuilder stage is not in the shipped image) — now forwarded via-e; and producer (:-false) vs verifier (:-true) defaults disagreed in the FAILING direction, so the producer would skip and the verifier then hard-fail on the placeholder tree it had just created. The producer now drops a.gen1-lane-offmarker and the verifier reads the producer’s ACTUAL decision instead of re-deriving it.smoke_genai_py()(insmoke-common.sh, runs on EVERY arch) asserts the version against the versions.env pin, that the loaded extension’s ELF machine is the target’s, and that the pybind API objects exist. A review defect was fixed here too: it conflated “no wheel on this arch” with “wheel installed but its native library will not load” — both exited 3 = benign SKIP, and every other gate is blind to the second case (smoke-torch-venv’sinstalled_version()falls back toimportlib.metadatawhen the import raises; ARCH-PARITY only reads dist-info directory names). An installed-but-unimportable distribution is now a hard FAIL.
Also closed 2026-08-31 (second pass, same window — the risk-REDUCING half of Section B)#
Done deliberately BEFORE the rebuild: both make the rebuild more likely to catch a real GEN1 failure instead of shipping it. The window was already open and no build was running, so the cache cost was zero.
✅ GenAI libraries were scanned by NOTHING — FIXED.
validate-media-runtime.shchecked unresolvedNEEDEDonly overARTIFACTS(the gst/libcamera/ffmpeg binaries) plus the gst plugin dir; itsLIB_DIRSsweep checks ELF machine only and is advisory./usr/local/lib/onnxruntime-genai/libappeared in NEITHER list, so an unresolvedNEEDEDinlibonnxruntime-genai*.sowas invisible — exactly the documented riscv64-latomicrisk (GenAI’s CMake, unlike upstream ORT’s, has no libatomic probe). Two changes: the prefix joinsLIB_DIRS(resolution root + advisory arch sweep), and the lib dir is now walked byscan_plugin_directory— misnamed but generic, so it was reused rather than copied — feeding the existingALL_MISSINGrepair/deny machinery. Guarded by[ -d ], so it is a no-op when the lane is off and the placeholder tree has an emptylib/. amd64/arm64 are expected to resolve exactly as before because genai’s own deps (libonnxruntime.soand friends) live in/usr/local/lib/onnxruntime-cpu/lib, already aLIB_DIRSroot — but this is a NEW gate on all three arches and has not been run against a real image.✅
prune_conflicting_onnx_wheelsdeleted the wheel the same file needs — FIXED. On the DEFAULTONNX_PACKAGE=onnxruntimepath it ranrm -f /opt/wheels/*genai*.whl, whose glob matches the CPU wheelonnxruntime_genai-<ver>-…whlthat this media lane now builds on every arch — the very wheelbuild_uv_sync_argslooks for 60 lines later, and that ARCH-PARITY now asserts must be installed. Ordering confirmed:install_project_environmentprunes BEFOREbuild_uv_sync_argslooks, so a successfulrmwould have resurrected the GENAI-DRIFT bug — no local wheel, silent fallback to the app lock’s PyPI genai. It was inert ONLY by accident:/opt/wheelsis a read-only bind mount (Dockerfile.torch:80) so thermfails and|| trueswallows it; making that mount rw would have broken all three arches at once. Narrowed to the GPU-flavoured variants the arm actually means (*genai_cuda-*,*genai_rocm-*,*genai_directml-*), verified against a fixture that the CPU wheel and plainonnxruntime-*survive while*_gpu-*and*genai_cuda-*are removed. GEN1 turned this from dormant to self-contradictory.