ADR 0030 Phases B+C: stage-1 initramfs and the RT kernel become packages of the one build - #166
Merged
Merged
Conversation
…oward vanilla Buildroot rt-defconfig is the rt twin of de10nano-defconfig. A Buildroot pin move (or a 'make clean', which keeps every .config) leaves output-rt/.config written on the previous pin, and Buildroot's own syncconfig then stops 'make rt' at an interactive Kconfig prompt. Reproduced non-interactively from the stale file; the regenerated config takes zero prompts. README, buildroot-config.md and the RT doc now name the step and the trap. ADR 0030 records the research behind the owner's request to shrink the 1,302-line wrapper toward a plain-Buildroot interface: what the Makefile does, what must survive (artifact, CI-contract and make-semantics constraints, each cited), what Buildroot 2026.08 offers natively, two measurements (a glibc-static stage-1 BusyBox is 942,660 B vs musl 263,308 B; CI already serialises the RT kernel leg), the options, and the accepted direction: stage 1 as a package (amends ADR 0002), RT as a linux-rt kconfig-package (amends ADR 0021), a Kconfig profile package replacing Buildroot-config fragments, a ~25-line wrapper, and the toolchain as a hash-verified release asset pending an image-identity proof. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V21CRKVv5f66XFNbcrteMD
…e B, amends ADR 0002) The stage-1 initramfs is no longer a second Buildroot configuration with its own musl toolchain in output-initramfs/. It is package/mister-initramfs in the main configuration: - mister-initramfs-busybox: a kconfig-package over the same board/mister/common/initramfs-busybox.config, built with the main glibc toolchain, CONFIG_STATIC pinned, upstream's busybox patches applied, the tarball shared with package/busybox via _DL_SUBDIR. Nothing reaches the rootfs; `make mister-initramfs-busybox-menuconfig` edits the config. - mister-initramfs-exfatprogs: the same for fsck.exfat, linked -static; only that binary is taken, so ADR 0026's trim of the other five tools is structural instead of a post-build deletion. - mister-initramfs: assembles the tree + /init + /dev/console under host-fakeroot, packs a --reproducible cpio with SOURCE_DATE_EPOCH mtimes, runs verify.sh (the applet / forbidden-binary / /init / `ash -n` checks that were `make initramfs-verify`, now failing the package), and installs images/mister-initramfs.cpio. - linux/linux-ext-mister-initramfs.mk + linux/Config.ext.in: a br2-external linux extension (BR2_LINUX_KERNEL_EXT_MISTER_INITRAMFS). Included by linux/linux.mk before the kernel package is evaluated -- the only hook early enough to make the kernel DEPEND on the package (external.mk is too late). The CONFIG_INITRAMFS_SOURCE fixup moves there from external.mk unchanged; MISTER_INITRAMFS_CPIO stays overridable for mk-sdcard.sh. Why: ADR 0030 §3.1 measured a static-glibc stage-1 BusyBox at 942,660 B against musl's 263,308 B, in a 16 MiB zImage_dtb budget (U-Boot's memory map, not card size) with ~7.4 MB of headroom; the owner accepted the cost. ADR 0002 §10 rejected glibc for a Buildroot *configuration* (BR2_STATIC_LIBS is not offered with glibc; the rootfs would carry the shared libc) -- neither applies to a package that links one binary -static into its own directory. Retired: output-initramfs/, output-initramfs-de25/, the initramfs-* fragment stacks and their golden lines, `make initramfs`/`check-initramfs`/ `initramfs-verify`/`de25-initramfs*` (Makefile 1,302 -> 1,051 lines), board/mister/common/initramfs-post-build.sh, external.mk's embedding block, check-config-fragments' check (f), the initramfs host cache in CI. The BR2_arm gate becomes the extension symbol, set in the de10nano fragment (so the RT kernel-only stack embeds it too) and not in de25nano (ADR 0029 D11: enabling it there is now the one-line switch). ci-tests.sh asserts the artifact instead of calling the retired targets: the built kernel .config names images/mister-initramfs.cpio and the cpio exists. test-initramfs.sh reads the cpio from output/images (DE10) and output-de25 (DE25) and uses each board's own toolchain. Golden hashes regenerated for de10nano, de10nano-kernel and rt; check-config-fragments OK 4/4. Build verification is recorded in the follow-up commit. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V21CRKVv5f66XFNbcrteMD
…ig lookup ci-tests.sh's new "initramfs embedding" check referenced a kernel-config variable the script never defines (caught by the suite's own `set -u` on the first run); it now resolves the tree with the suite's uniqueness-guarded linux-[0-9]* glob like every other kernel check. Results of the from-scratch worktree build (recorded in ADR 0002 §8c): `make all` exit 0; the kernel-embedded cpio is byte-identical to images/mister-initramfs.cpio; the applet set and /init are identical to the retired musl cpio (the 36 dropped entries are Buildroot skeleton files /init never touches); zImage_dtb 9,717,297 B with 7,059,919 B of headroom; ci-tests 366 pass / 0 fail / 6 skip, test-initramfs.sh 8/8 QEMU cases. Rig boot still owed (owner flashes). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V21CRKVv5f66XFNbcrteMD
… (ADR 0030 Phase C, amends ADR 0021) The RT kernel is no longer a kernel-only Buildroot tree (output-rt/) whose module tree the wrapper Makefile staged into an overlay for the next `make all`. It is package/linux-rt, a kconfig-package of this br2-external in the shape of Buildroot's own barebox-aux (one source, a second config, one tree): - reuses linux.mk's LINUX_MAKE_FLAGS / LINUX_MAKE_ENV / LINUX_KCONFIG_OPTS and its whole LINUX_KCONFIG_FIXUP_CMDS (every BR2_-driven fixup, the PACKAGES_LINUX_CONFIG_FIXUPS, and the stage-1 cpio embedding the linux extension appends), so the two kernels are configured by the same code; - base config = board/mister/de10nano/linux.config, layered with linux-rt.fragment (kconfig-package fragment support); patches from board/mister/de10nano/patches/linux-rt -> ../linux-patches-beta, whose `series` apply-patches.sh honours; tarball shared with dl/linux via _DL_SUBDIR, hash in package/linux-rt/linux-rt.hash (signed-manifest rule); - a POST_CONFIGURE hook asserts CONFIG_PREEMPT_RT=y in seconds, before the compile; modules_install lands in TARGET_DIR; a target-finalize hook runs depmod for its own kernelrelease and asserts a non-empty modules.alias; INSTALL_IMAGES emits zImage_dtb-rt (zImage + DTB, check-zimage-dtb.sh budget-checked) and linux-rt.config. Selected by BR2_PACKAGE_LINUX_RT=y + BR2_PACKAGE_LINUX_RT_VERSION="7.2.4" in de10nano-image.fragment (the pin moves there from the deleted configs/mister_rt.fragment; scripts/hash-sync-kernel.sh --pin=rt and renovate.json's kernel-rt-7.2 manager follow). Retired: output-rt/, work/extra-modules-overlay + its stamp and rt-clean's two-place removal, the `make rt` before `make all` rule, rt/rt-*/rt-defconfig targets (Makefile 1,051 -> 799 lines), configs/mister_rt.fragment, configs/fragments/kernel-only.fragment and the DE10NANO_KERNEL stack (with check-config-fragments' variant stacks + lockstep check (c) and their golden lines), scripts/list-kernel-variants.sh, scripts/check-kernel-defconfig-sync.sh, the build-kernel matrix in build.yml and release.yml, the kernel-leg and merge-kernel-modules actions, and the variant dl/host caches. release.yml stages zImage_dtb-rt and linux-rt.config from output/images/; the legal-info-rt.tar.gz asset folds into legal-info.tar.gz (linux-rt has a manifest row and its 42 patches under sources/). buildroot-build's `variant` input now errors on anything but main. Verified (ADR 0021 amendment): 42/42 patches applied, PREEMPT_RT proof passed, zImage_dtb-rt 10,091,326 B (headroom 6,685,890), exactly 6.18.50 + 7.2.4 module trees in the one linux.img (90 RT modules, depmod'd), legal-info covers linux-rt, ci-tests 366 pass / 0 fail / 6 skip; the RT .config differs from the v2026.09.04-beta one only in the gcc 15.3 bump, post-beta linux.config changes, the cpio path and host rustc probes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V21CRKVv5f66XFNbcrteMD
…hase D)
Each board is now ONE committed Buildroot defconfig, loaded the vanilla way:
make mister_de10nano_defconfig && make
make O=output-de25 mister_de25nano_defconfig && make O=output-de25
The shared package sets are Kconfig PROFILES -- one symbol that `select`s a
documented list -- so two boards share a userspace by enabling the same
symbol and cannot drift apart on it:
- package/mister-firmware (31 selects: the /lib/firmware set, both boards)
- package/mister-drivers (4: the out-of-tree USB WiFi + xone drivers)
- package/mister-userspace (207: the DE10 userspace, with the old fragment's
32 section headings carried as comments)
The DE25 selects only mister-firmware (rule 5: DE10 packages reach it by
owner decision). Two Kconfig facts shaped the profiles and are asserted by
the new checker: a `select` of a kconfig `choice` member is SILENTLY ignored
(BR2_PACKAGE_ZLIB_NG therefore stays in the defconfig), and a `select` of a
symbol that does not exist is ignored too (mister-initramfs's select of
BR2_PACKAGE_HOST_FAKEROOT -- a host package with no Kconfig symbol -- is
removed; the .mk dependency was always what carried it).
Proof: the candidate defconfigs were resolved with `make <name>_defconfig`
and diffed against the fragment stacks' resolved configs -- identical for
both boards except for the profile symbols themselves (set lines AND
`is not set` lines). The committed files are then Buildroot's own
`savedefconfig` form plus a header comment. BR2_PACKAGE_LINUX_RT_VERSION
loses its Kconfig default so the pin is a defconfig line savedefconfig
keeps (a default would have been dropped and the pin left unmanaged).
Retired: configs/fragments/ (every fragment, stacks.mk, golden.sha256),
scripts/check-config-fragments.sh, scripts/hash-sync-golden.sh (case 8),
scripts/lib/config-stacks.sh. New scripts/check-defconfigs.sh replaces
them, the way upstream Buildroot's own CI checks its defconfigs: each
loads, every line survives (support/scripts/check-dotconfig.py -- the
2026.08 headers regression would have failed here instead of moving a
hash), savedefconfig reproduces the file, every profile select lands, and
the boards agree on the old `common` layer's seven symbols.
The wrapper Makefile is rewritten (799 -> 99 lines): pin, fetch + verify +
unpack (scripts/fetch-buildroot.sh), the GNU-install shim
(scripts/hostshim.sh), `all` with a "configure first" hint, `de10nano-defconfig`
/ `de25nano-defconfig` / `de25` aliases for CI and muscle memory, `sdcard`,
clean/distclean, help (ours + Buildroot's), and `%` forwarding with O= and
BR2_EXTERNAL set. Bare `make` builds, as in Buildroot. The DE25 artifact
assertions the old `de25` recipe carried are already made by
board/mister/de25nano/post-image.sh, so nothing moved.
Consumers repointed to configs/mister_de10nano_defconfig: ci-tests.sh,
test-initramfs.sh, test-sdcard-install.sh, lint-kernel-patches.sh,
hash-sync-kernel.sh (both pins), renovate.json (both kernel managers),
export-kernel-tree.sh / check-export-tree.sh, the CI toolchain fingerprint
and dl cache key, renovate-hash-sync.yml's path filter (golden case gone).
README's build section, docs/buildroot-config.md (banner: where each
section's lines now live), docs/ci.md and ADR 0030's status follow.
Verified: check-defconfigs.sh OK (3 defconfigs); `make
mister_de10nano_defconfig && make` on the Phase C tree rebuilt nothing
(target-finalize + images only); ci-tests.sh 366 pass / 0 fail / 6 skip;
shellcheck clean on every touched script and the composite actions.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V21CRKVv5f66XFNbcrteMD
…follow-through) .github/actions/buildroot-build: 545 -> 213 lines, 18 -> 16 steps. The `variant` and `board` inputs and everything hung off them -- the sentinel table (scripts/lib/board-expectations.sh, deleted), the variant-keyed host cache and make targets, the two conditional build steps -- are gone: since ADR 0030 Phases B-D there is one configuration and one tree. Cache key strings are unchanged (buildroot-tarball-*, br-dl-*, br-host-*, ccache <version>) so existing entries keep hitting. The `ccache-max-size` input nobody passed is a literal. The header comment now states the four caches and the two rules that keep them honest; the fingerprint check is one grep for BR2_arm=y instead of a table. Makefile: the de10nano-defconfig / de25nano-defconfig aliases go (their only caller, release.yml's azcopy job, now says `make mister_de10nano_defconfig`); `de25` stays because the DE25 docs use it. scripts/check-kernel-fragment-noop.sh had no caller and is removed (docs/de25-kernel-config.md says so). Docs: a fixed path/target map applied to the 33 live docs (fragment paths -> the defconfig or the profile that now carries the line; `make rt` -> `make linux-rt`; output-rt/ and output-initramfs/ artifacts -> output/images/), with the passages the map could not fix rewritten by hand (docs/renovate.md's kernel-pin rows and its retired golden-hash case; the debug-tooling and RT build blocks). docs/ci.md gains "The pipeline today" at the top -- nine workflows in a table, the action's four caches and two rules -- and the 1,400-line narrative moves under a "Historical design notes" heading with its anchors intact. The dated DE25 planning ledgers get a layout-note banner instead of line edits. ADRs and kernel-recon records are left as written. Verified: every workflow/action parses; composite-action shellcheck and the script sweep clean; check-defconfigs.sh OK; Makefile parses. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V21CRKVv5f66XFNbcrteMD
….yml 1,110 -> ~560 lines) scripts/mk-release.sh owns everything release.yml's build job used to do inline between `make` and the sdcard image: read /MiSTer.version back out of linux.img and check the Downloader contract, stage the image assets and the single legal-info bundle, fetch + verify + extract the pinned stock archive (scripts/verify-stock-payload.sh, unchanged), assemble files/linux with our linux.img/zImage_dtb/7za, pack release_YYYYMMDD.7z, round-trip it under the pinned ARM 7za via qemu-arm, re-verify uboot.img/updateboot and the member list, and write SHA256SUMS. It runs at a terminal with the STOCK_* pins in the environment; proven locally against the worktree build (release_20260904.7z, 84,632,824 B, every check green). release.yml's build job goes from 27 steps to 9 (checkout, derive version, build, verify-image, mk-release.sh, sdcard, summary, report, upload); the two sdcard steps become one with SDCARD_CORES from the dispatch input; the publish job checks a fixed asset list and `sha256sum -c SHA256SUMS` instead of discovering kernel variants; the dependency-graph job extracts the one manifest. The STOCK_* pin block, the build-azcopy job and the release-notes prose are carried over verbatim. docs/ci.md's release row names the script. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V21CRKVv5f66XFNbcrteMD
CI's first clean build of Phase B (PR #166 run 34629457509) stopped at the kernel's kconfig fixup: "stage-1 initramfs cpio not found". A linux extension only orders the kernel's PATCH stage after the extension's PATCH stage -- linux.mk derives LINUX_PATCH_DEPENDENCIES from LINUX_EXTENSIONS, which is all xenomai's prepare-kernel needs -- so on a from-scratch `make` the kernel could reach its fixup before mister-initramfs had been built. The local verification did not see it because that package had been built first by hand. linux-ext-mister-initramfs.mk now also appends the package to LINUX_DEPENDENCIES; the extension file is included before linux.mk evaluates the kernel package, so the edge takes effect (external.mk would be too late). `make linux-show-depends` lists mister-initramfs. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V21CRKVv5f66XFNbcrteMD
The golden-case removal in Phase D consumed the COMMITMSG line that closes the `git commit -F -` heredoc (actionlint/shellcheck on PR #167: SC1044). Restored after the last bullet, with the guarded push it precedes; the stale GOLDEN_CHANGED condition on the step is dropped too. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V21CRKVv5f66XFNbcrteMD
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V21CRKVv5f66XFNbcrteMD
…onlint) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V21CRKVv5f66XFNbcrteMD
…s' patch 0031 The rig, still on an RT 7.2.3 kernel built 2026-09-05 (before PR #152's 7.x re-anchor of the exFAT symlink patch shipped there), Oopsed on the first symlink update_all.sh's Arcade Organizer created on /media/fat: PC 0x0 from page_symlink() via exfat_symlink() -- the NULL write_begin that the DE25's aarch64 QEMU leg had found on 2026-09-06. With CONFIG_PANIC_ON_OOPS=y and panic=15 the board rebooted 17 s later. Nothing executed that patch as 32-bit ARM: the DE10 QEMU leg builds at the shipped 6.18 pin with the shared (page_symlink) form, and the only runs of the 7.x rewrite were aarch64. `--kernel rt` (de10nano only) builds the same multi_v7 test kernel at BR2_PACKAGE_LINUX_RT_VERSION with linux-patches-beta/0031 and its own work/test-initramfs-rt* caches. ci-tests.sh runs its three exfat-driver cases after the DE10 leg. Verified locally at 7.2.4: symlink, exfat, fsck-request pass; the 6.18 form of the patch applied to the same 7.2.4 source reproduces the rig's Oops in QEMU (PC is at 0x0, LR is at page_symlink), so the leg catches the bug it exists for. The shipped 6.18.50 leg still passes symlink. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V21CRKVv5f66XFNbcrteMD
test-initramfs: --kernel rt, the 32-bit QEMU leg for the RT series' exFAT symlink patch
release.yml: one script from a built output/ to a verified dist/
ADR 0030 Phase D: Kconfig profiles, committed defconfigs, and a 99-line Makefile
mcfbytes
added a commit
that referenced
this pull request
Sep 16, 2026
ADR 0030 Phases B+C: stage-1 initramfs and the RT kernel become packages of the one build
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
The first two implementation phases of ADR 0030 (shrinking the wrapper Makefile toward a plain-Buildroot interface), plus the
rt-defconfigfix that started the investigation. Four commits, one per unit; each is reviewable on its own.rt-defconfig+ ADR 0030 — the stale-config-after-a-Buildroot-bump trap, and the research/options record (status: accepted in direction).package/mister-initramfs(+-busybox,-exfatprogs, both static glibc from the main toolchain) replaces the separate musl Buildroot tree;linux/linux-ext-mister-initramfs.mkis a br2-external linux extension, the only hook early enough to make the kernel depend on the package. Retiresoutput-initramfs*, theinitramfs-*stacks,make initramfs/check-initramfs/initramfs-verify, the initramfs host cache.ci-tests.shfix.package/linux-rt(amends ADR 0021). A kconfig-package in the shape of Buildroot's ownbarebox-aux, reusinglinux.mk's make flags and its whole fixup body. Retiresoutput-rt, the extra-modules overlay and stamps,make rt/rt-*,configs/mister_rt.fragment, the kernel-only stack, thebuild-kernelmatrix, thekernel-legandmerge-kernel-modulesactions, the variant caches,list-kernel-variants.sh,check-kernel-defconfig-sync.sh.legal-info-rt.tar.gzfolds intolegal-info.tar.gz.Makefile: 1,302 → 799 lines (the full rewrite to ~25 lines is the next phase, after the profile package).
build.yml:gate → lint-config → build → status.Verification (worktree, from scratch on Buildroot 2026.08)
make allexit 0 for both phases;ci-tests.sh366 pass / 0 fail / 6 skip after each;test-initramfs.sh8/8 QEMU boot cases on the glibc stage 1.images/mister-initramfs.cpio; applet set and/initidentical to the musl cpio; cpio 1,577,984 B (busybox 942,660 + fsck.exfat 611,508);zImage_dtb9,717,297 B, headroom 7,059,919 B.series;CONFIG_PREEMPT_RT=yasserted at configure;zImage_dtb-rt10,091,326 B, headroom 6,685,890 B; exactly6.18.50+7.2.4module trees in the onelinux.img(90 RT modules, depmod'd);legal-infocarrieslinux-rtwith its 42 patches; the RT.configdiffers from v2026.09.04-beta's only in the gcc 15.3 bump, post-betalinux.configchanges, the cpio path and host rustc probes.check-config-fragments.shOK (2 stacks); shellcheck clean on every touched script; the composite-action shellcheck wrapper clean.Not done here
v2026.09.11-beta(pre-refactor, old pipeline) is the parity baseline; content comparison, not raw hashes, sinceBR2_REPRODUCIBLEonly holds for the sameO=path.rt/initramfs; they go with the rewrite phase rather than being polished twice.Caveat for newcomers (owner's ask)
Part of the package selection now lives in Kconfig rather than the defconfig: the stage-1 packages are selected by the
BR2_LINUX_KERNEL_EXT_MISTER_INITRAMFSextension and the RT kernel byBR2_PACKAGE_LINUX_RT. Their Kconfig help texts, the README tree, and the Makefile help say so; the upcoming profile package will carry the same pointer.🤖 Generated with Claude Code
https://claude.ai/code/session_01V21CRKVv5f66XFNbcrteMD