Refactoring backlog archive — 2026-09-09#
Closed on 2026-09-09 and moved out of
refactoring-backlog.md, which is OPEN-only.
Each entry keeps the evidence that closed it.
One entry, and a build closed it. VK2 had been open since 2026-09-05 with the
standing note that only a real chain could settle it. Two sdk runs settled it:
sdk-20260908-132426 for aarch64 and sdk-rv64-20260908-211949 for riscv64.
Read the numbers here as dated, not current. Two of the figures in the first
draft of this entry did not survive an adversarial re-derivation — the
x86_64/bin count and the composition of its delta were both quoted before
either directory had been listed. Both were re-measured against the pushed
digest before this file was written; the method is stated with each.
VK2. CLOSED — 20/20 on both foreign arches, and a fifth defect nobody had named [M, ★★★]#
What closes this entry, as the entry itself required: <arch>/bin carrying
everything x86_64/bin does that is not structurally host-only.
#20 4328.1 [INFO] Vulkan cross-targets riscv64: 20/20 component(s) built
#20 3705.1 [INFO] Vulkan cross-targets aarch64: 20/20 component(s) built
riscv64 at out/build-logs/sdk-rv64-20260908-211949/sdk-riscv64.log:20826
(a live 4328 s build, pushed @sha256:09a4d255); aarch64 at
out/build-logs/sdk-20260908-132426/sdk-arm64.log:21316 (3710 s, pushed
cross-sdk-arm64@sha256:de6eaad2). 20 = the 17 _VK_TARGET_COMPONENTS rows plus
the three hardwired ones (loader, SPIRV-Tools, glslang); the counter has exactly
four increment sites and the logs carry 20 distinct Cross-building <label>
lines, name for name.
The four named defects: all four diagnoses held, all four fixes landed#
vulkan-profiles/ jsoncpp PIC —-DCMAKE_POSITION_INDEPENDENT_CODE=ONmoved into_cross_build_sdk_component’s fixed args rather than the one row, since any archive handed to the SDK can end up inside a layer.so.gfxreconstructheader bridge —_vulkan_setup_sdk_includesnow bridgesX11 xcb GL KHR EGL GLES2 GLES3.KHRwas not optional:GL/gl.handglcorearb.hinclude<KHR/khrplatform.h>.vulkancapsviewer/ raw loader path —-DVULKAN_LOADER_INSTALL_DIR="${archdir}"in thevulkancapsviewer)arm of_vulkan_target_dynamic_args. The “still unproven” note in the open entry — that AUTOMOC/AUTORCC underQT_HOST_PATH=/usrhad never run on a foreign arch — is now proven both ways:[2/17] Automatic RCC for vulkancapsviewer.qrcon riscv64,[17/17] Linking CXX executable vulkanCapsVieweron aarch64.slang/ vendor copy step —_xbuild_extra_targets+=(copy-gfx-slang-modules), the helper extension the entry called the largest of the four.
Two of the entry’s own diagnoses had been wrong and are recorded as such: slang
was never a Canadian-cross problem (it was a prebuilt Dawn WebGPU zip upstream ships
for x86_64 and aarch64 only, whose arch cascade FATAL_ERRORs on riscv64 even with
the backend off), and vulkancapsviewer was never missing target Qt6 in the way the
entry described.
The fifth defect, which no route in VK2 had named#
riscv64 sat at 19/20 through two further runs after all four fixes were in.
vulkancapsviewer alone kept failing, at find_package(Qt6), traced to
install_target_packages: 'qt6-base-dev:riscv64' apt-get exited 100
libappstream5:riscv64 : Depends: libcurl3t64-gnutls:riscv64 (>= 7.63.0) but it is not going to be installed
libproxy1v5:riscv64 : Depends: libcurl3t64-gnutls:riscv64 (>= 7.16.2) but it is not going to be installed
Every package in that message existed in ports at a satisfying version, so the
error named a symptom and not a cause. The cause was a host/target apt pocket
asymmetry: the compiler stage wrote ubuntu.sources for amd64 without
-security and ubuntu-ports.sources with it. libcurl3t64-gnutls is
Multi-Arch: same, so amd64 topped out at 8.18.0-1ubuntu2.4 while riscv64’s
candidate was 8.18.0-1ubuntu2.5, and no common version existed in apt’s view.
A/B on ghcr.io/kataglyphis/kataglyphis_beschleuniger:base, same image, one line
different:
amd64 stanza |
amd64 candidate |
riscv64 candidate |
|
|---|---|---|---|
without |
8.18.0-1ubuntu2.4 |
8.18.0-1ubuntu2.5 |
|
with |
8.18.0-1ubuntu2.5 |
8.18.0-1ubuntu2.5 |
installs |
Nothing about it was Qt-specific or riscv64-specific: it disabled every
Multi-Arch: same library that has ever had a security-only upload. The full
account, including why archive.ubuntu.com made the 0 cost-free, is
cross-build-verification.md#host-and-target-apt-sources-must-expose-the-same-pockets.
Why aarch64 reached 20/20 without the fix: its run ended 16:39, before the
skew had anything to bite on for that lane. It was timing, not immunity — the
same host file, the same Multi-Arch: same rule. That is why VK5 (below, in the
live file) asks for one confirming aarch64 run rather than treating the earlier
20/20 as current.
What the shipped bytes say#
Both bin directories listed inside a container from the pushed
@sha256:09a4d255 — not derived from the log, which never enumerates the
tarball-seeded x86_64 tree:
x86_64/bin52 entries,riscv64/bin37.riscv64 is a strict subset: zero entries exist there that x86_64 lacks.
The 15-entry delta is exactly
dxa dxa-3.7 dxc dxc-3.7 dxl dxl-3.7 dxopt dxopt-3.7 dxr dxr-3.7 dxv dxv-3.7 llvm-tblgen vkconfig vkconfig-gui.spirv-remapis in neither tree, so-DENABLE_SPVREMAPPER=OFFon the cross glslang costs no parity. (It was raised as a concrete reason to doubt subset-ness before the directories were listed; listing them settled it.)Spot-checked ELF machine in
riscv64/bin:vulkanCapsViewer,slangc,gfxrecon-info,vulkaninfo— all RISC-V.
The 15 are what LunarG’s prebuilt x86_64 tarball carries and no arch builds
from source: vkconfig, Vulkan-Configurator and dxc appear nowhere in
vulkan.sh (0 hits, case-insensitively, in 912 lines). That is a never-asked
question rather than a regression, and it is now VK4 in the live file.
What this cost, and the lesson#
Four rebuild attempts of the sdk stage. The three that ended 19/20 each had a
green static battery behind them. No gate in this repo can see this class: the
two sources files are individually valid, the skew is invisible to a same-arch
build, and it only bites once a Multi-Arch: same package receives a
security-only upload. The mirror-consistency gate now asserts the pair rather
than either file, and cross_align_host_apt_pockets repairs an inherited skew at
the point of use so a stage need not be rebuilt to benefit.
VK4 + VK5. CLOSED — all three arches ship 52 binaries [S, ★★]#
Measured on the pushed digests, both directories listed inside a container. Not derived from a log.
amd64 |
arm64 |
riscv64 |
|
|---|---|---|---|
|
52 |
52 |
52 |
|
9 |
10 |
10 |
entries amd64 has and the arch lacks |
— |
0 |
0 |
The sets are identical, not merely equal in size — zero entries in either
direction. dxc, vkconfig, vkconfig-gui and llvm-tblgen report ELF machine
AArch64 and RISC-V respectively, so nothing is a copied host binary. Both foreign
arches are AHEAD on layers: only they ship VkLayer_khronos_timeline_semaphore.
Vulkan cross-targets riscv64: 24/24 and aarch64: 24/24, with zero
unavailable on <arch> lines in either log.
The gap was a question nobody had asked#
LunarG’s ./vulkansdk builds 24 components under all. The HOST list in
_vulkan_build_components named 18. A component not named there is never
checked out, and _vulkan_target_install_component returns at its -d test
before incrementing _vk_attempted — so the three missing ones were never
counted as attempted, and the verdict line read a clean N/N. That is why it
survived months of green runs.
The fix was four table rows (dxc, vulkantools, crash-diagnostic-layer,
yaml-cpp) plus three dynamic-arg arms, and the count now equals the vendor’s
own build_all flag count: 21 rows + 3 hardwired = 24.
Two traps that would have faked success#
vulkantoolswould have reported BUILT with no vkconfig in it. Upstream doesfind_package(Qt6 ... QUIET)and, when Qt6 is missing, skips the entire configurator with amessage()and exits 0._vk_okwould have incremented.-DCMAKE_REQUIRE_FIND_PACKAGE_Qt6=TRUEturns that into an honest failure; the shipped ELF proves Qt6 really was found.dxcfailed the first run, and NOT because of riscv64. Configure completed — LLVM 3.7 does know the host triple, upstream PR #4894 carries. It died compilingexternal/SPIRV-Tools/source/util/timer.hon GCC 16’s-Warray-bounds, promoted by-Werror._vulkan_target_build_spirv_toolsin the same file had carried-DSPIRV_WERROR=OFFfor that identical warning since it was first diagnosed; DXC vendors its own copy of SPIRV-Tools, which never saw the flag.
That fix saved both lanes, not one. The rebuild logged 164 array-bounds
warnings on riscv64 and 93 on aarch64 — arm64 would have died at the same
header. It only surfaced on a foreign arch because GCC inlines differently on
x86_64, where the host build passed.
What it cost, and what it leaves#
Four sdk builds. VK5 is closed in the same breath: arm64’s previous 20/20 was measured against a tree that no longer existed, and the rebuild settles it on the current one.
Left behind, both small and both owner decisions: VK6 (13 shared libraries
the foreign arches do not get, caused by our own ENABLE_OPT=OFF /
SPIRV_CROSS_SHARED flags) and VK7 (11 DXC files the foreign arches ship
that the vendor prunes from the tarball).
VK6 + VK7. CLOSED — the foreign prefixes are 2 files from amd64, and both are explained [S]#
Measured inside a container on the pushed digests, both lib/ trees listed.
Not derived from a log.
amd64 |
arm64 |
riscv64 |
|
|---|---|---|---|
|
118 |
123 |
123 |
|
52 |
52 |
52 |
layers |
9 |
10 |
10 |
only amd64 has |
— |
2 |
2 |
The gap VK6 opened at 72 is now 2, identically on both arches:
VulkanLoader (a layout difference the consumers already assume: the foreign
prefixes keep libvulkan.so* flat) and libshaderc_util.a (below).
libglslang.so, libSPIRV.so and libspirv-cross-c-shared.so report ELF
machine AArch64 and RISC-V respectively.
VK6: the entry’s own fix would have made it worse#
BUILD_SHARED_LIBS is exclusive, not additive. Flipping it ON would have
gained 9 files and LOST 6, because glslang guards its static installs of
MachineIndependent, GenericCodeGen and OSDependent behind
if(NOT BUILD_SHARED_LIBS). The vendor configures glslang twice into one
prefix, and the order is load-bearing: the second install owns
lib/cmake/glslang, so the STATIC pass must land last or find_package(glslang)
silently starts describing SHARED imported targets. Both passes are now in
_vulkan_target_build_glslang, and a test asserts the order rather than just
their presence.
SPIRV_CROSS_SHARED=ON is the separate, genuinely additive half.
libshaderc_util.a is deliberately NOT replicated. It has no install()
rule anywhere — the vendor copies it by hand after cmake --install — and
amd64 ships no libshaderc_util headers, so the archive is unlinkable as
shipped even there. Copying it would be file-name parity for something nobody
can use.
Also left alone, and worth knowing: our libglslang.so is still not the same
library as amd64’s, because we pass ENABLE_OPT=OFF and the vendor does not.
That is a deeper difference than a file list and belongs in its own measured
change.
VK7: mirrors the vendor’s own prune, with the guard that matters#
_vulkan_target_prune_nonsdk_dxc deletes exactly what clean_nonsdk_files
deletes under BUILD_DXC=1 — four include trees and four libs, all
arch-relative. It is guarded on include/dxc/dxcapi.h, the vendor’s own marker
that a dxc install landed in THIS prefix. Without that guard the helper would
rm -rf include/llvm out of whatever directory it is handed, and
/opt/llvm-target/include/llvm is 41 MB of real LLVM 23 headers that
LLVMConfig.cmake and 1.3 GB of libLLVM*.a resolve through. Four mutations,
one of them on that guard.
VK5 closed with them#
arm64’s earlier 20/20 was measured against a tree that no longer existed. Both
foreign arches have now been rebuilt from the current one and report
24/24 component(s) built with zero unavailable on <arch> lines.