libtaskmanager/virtualdesktopinfo.cpp defines namespace X11Info at namespace
scope in terms of QNativeInterface::QX11Application, which does not exist in a
Qt built without the xcb platform:
error: 'QX11Application' is not a member of 'QNativeInterface'
All three call sites were already inside #if HAVE_X11 -- only the definition
was exposed -- so wrapping the block changes no reachable behaviour.
Restricted to depth-0 'namespace' blocks whose body names an X11-only native
interface. Narrow on purpose: brace-matching arbitrary function definitions is
much easier to get wrong, and a bad transform here produces invalid C++ rather
than a clean failure.
Distinct from the KWindowSystem-API gating: these are the Xlib/XCB headers
themselves, which a Wayland-only sysroot does not ship, so each is a hard
compile failure rather than a lost feature:
appmenu/appmenu.h:13: fatal error: xcb/xcb.h: No such file or directory
Two of the affected directories are genuinely compiled and would have failed in
turn: kcms/kfontinst (gated only on FONTCONFIG_FOUND) and logout-greeter
(CMakeLists.txt:428, outside any X11 gate). kcms/cursortheme is already gated
upstream by `if(WITH_X11 AND X11_Xcursor_FOUND)` and ksmserver by if(WITH_X11);
guarding them too is harmless and keeps the rule uniform rather than
maintaining a list of exceptions.
Only touches includes at preprocessor depth 0 with respect to HAVE_X11, so
anything already guarded is left alone.
Verified: preprocessor balance OK across all 59 files carrying guards, and a
second run is a no-op (0 files, 0 includes) -- the recipe re-runs this on every
build, so idempotency is a correctness requirement, not a nicety.
config-X11.h.cmake declares the macro with `#cmakedefine01 HAVE_X11`, which
always DEFINES it -- as 0 when X11 is off, not undefined. So `#ifdef HAVE_X11`
is true in BOTH configurations and the guard does nothing:
#ifdef HAVE_X11
#include <xcb/xcb.h> <- compiled even with HAVE_X11 == 0
#endif
That is how appmenu.h reached
appmenu/appmenu.h:13:10: fatal error: xcb/xcb.h: No such file or directory
while looking correctly guarded. `#if HAVE_X11` reads the value and behaves as
intended.
An upstream bug invisible on any system that has X11 headers installed, because
there the include just succeeds. 3 occurrences across appmenu.h and
panelview.h; plasma-desktop has none. Normalisation runs on every file rather
than only those using the X11-only KWindowSystem APIs, since the defect is
about the guard form, not the guarded content.
ICU emits its data as generated assembly (icudt75l_dat.S). Without
-Wa,--noexecstack the assembler produces an object with no .note.GNU-stack
section, and modern binutils warns:
ld: warning: icudt75l_dat.o: missing .note.GNU-stack section implies
executable stack
Harmless alone -- except KDE's ECM links with -Wl,--fatal-warnings, so every
KDE consumer of static ICU fails outright (first hit: plasma-workspace
applets/digital-clock, collect2: error: ld returned 1). Fixed at the source
rather than suppressed downstream, which would have to be repeated per consumer.
Also gates the '#'-inside-a-backslash-continuation trap, which terminates the
continuation and silently drops every remaining argument. It has now bitten
three times, most recently while writing THIS commit: the noexecstack rationale
was first placed between two continued configure flags, which would have
dropped the rest of ICU's configure line. Moved above the invocation.
The check scans the RAW file, not the parsed TOML. In a multi-line basic string
a trailing backslash is itself a TOML line-continuation escape, so the newline
is gone before the value is handed over and the parsed string has no trailing
backslashes at all. The first version scanned the parsed value and silently
found nothing -- caught by a self-test, not by review, which is the same class
of invisible failure the gate exists to prevent.
brush records `# Upstream: https://github.com/reubeno/brush`, but the check
matched `upstream:` case-sensitively AND required the URL to end in .tar/.git,
so it missed on both counts and filed a genuine fork as first-party.
Now matches an explicit upstream/snapshot/origin label followed by a URL, any
case. Deliberately NOT any bare URL in a comment: a stray bug-tracker link must
never read as provenance, since that error direction (first-party treated as
vendored) is the one that loses irreplaceable work.
Remaining inaccuracy is missing DATA, not detection. libepoxy, libpciaccess,
libudev, libxcvt and libdisplay-info are upstream projects whose recipes carry
only `path = "source"` with no origin recorded anywhere, so nothing can tell
them apart from our own code. They classify first-party, which is merely
stricter. The real fix is to record their upstream in the recipe.
The name list was wrong in principle. It started as redbear-*, then needed cub,
then tlc -- and protection that depends on someone remembering to edit this file
is not protection. A new internal program starts out unguarded and the omission
stays invisible until the code is already gone. tlc proved it: first-party,
exempt from out-of-tree staging, protected by nothing but this gate, and
matching no pattern.
The real property is structural -- our code has no upstream to restore from. A
recipe is first-party when its recipe.toml records no fetchable origin: no
`tar =`, no `git =`, no upstream URL. That identifies ~96 recipes against the 3
the list covered, so the list was guarding roughly 3% of the exposure.
The heuristic errs toward first-party deliberately. A vendored recipe recording
its origin only in prose gets treated as ours, which just makes drift fatal
instead of a warning. The opposite error loses irreplaceable work, so the
default fails in the safe direction.
Verified: fires on redbear-netctl, cub and tlc (none of which are named
anywhere in the code now); does NOT fire on vendored kirigami; silent on a
clean tree; every probe restores.
Extends the fatal first-party check beyond redbear-* to cub (system) and tlc
(tui). Both are Red Bear's own programs with no upstream anywhere, so the
"restore it from the tarball" recovery that makes vendored drift a warning does
not exist for them.
They sit on opposite sides of the staging boundary, which is why both need the
gate for different reasons:
cub is staged out of tree (no escaping cargo path deps), so staging already
keeps recipe seds off the tracked copy.
tlc is EXEMPT from staging: its manifest has a path dependency escaping the
source tree, so nothing keeps a sed off the real files. For tlc this
gate is the only protection.
The check is now a redbear_is_firstparty() helper rather than an inline glob, so
adding the next internal program is one line.
Verified: silent on a clean tree; fires on a one-line edit to cub and to tlc;
both restore cleanly.
redbear-* recipes are not vendored upstream code -- they are Red Bear's own
programs and exist nowhere else. A vendored tree can be restored from its
tarball or git remote; first-party source cannot. If a recipe sed or an `rm`
damages it and that gets committed, the work is gone.
Not hypothetical. Seven redbear-* recipes rewrite their own source during the
build (greeter, btusb, btctl, ime, dnsd, accessibility, keymapd), and all seven
are exempt from out-of-tree staging because their cargo manifests carry path
dependencies escaping the source tree. They are simultaneously the least
protected and the most irreplaceable code here.
Uncommitted drift in them now fails preflight instead of printing a note that
scrolls past. Vendored trees keep the existing warn-by-default behaviour.
Override: REDBEAR_ALLOW_DIRTY_FIRSTPARTY=1.
Verified: gate is silent on a clean tree, fires on a one-line edit to
redbear-authd, and the tree restores cleanly.
Two systemic fixes for failure classes this session kept re-hitting.
1. COOK_JOBS default 4 -> 1. Recipes are not isolated: redbear_qt_ensure_dep_
sysroots repairs OTHER recipes' sysroots, so one cook can relink the include/
or lib/ directory another is compiling against. The tell was a failure that
MOVED between source files across runs (kirigami died in KirigamiTemplates on
build 59, KirigamiPrivateplugin on 61) with a sibling's rm -f/ln -sf
interleaved in the log. Making the relink atomic closed one window; the
sharing itself remains unsound. Compilation is still parallel -- each recipe
keeps the full -j budget -- only the number of recipes in flight drops to 1.
An intermittent build costs more than a serial one.
2. Auto-reset build dirs whose CMakeCache records a different source path.
CMake stores CMAKE_HOME_DIRECTORY and hard-aborts when the source moves:
CMake Error: The source ".../source-staged/..." does not match the source
... used to generate cache
Out-of-tree staging moved every tracked recipe's source and stranded 66 build
dirs. Two rounds of clearing them by hand were not a fix: the next source-path
change strands them again, and the abort surfaces in a downstream recipe
rather than at the cause. cook now detects the mismatch and resets the dir.
Out-of-tree staging made the checker scan source-staged/, which holds the same
upstream third-party patches as source/ -- already skipped because they are not
ours to fix. qtdeclarative's bundled yoga patch then failed the build at
preflight. Same rationale as the existing /source/ entry.
redbear_qt_ensure_dep_sysroots repairs OTHER recipes' sysroots, so with
COOKBOOK_COOK_JOBS>1 it runs while a parallel cook is compiling against the
very directory it is relinking. It did `rm -f` then `ln -sf`, leaving a window
where the path did not exist. kirigami died in that window:
cstdio:47: fatal error:
.../qtdeclarative/target/.../sysroot/include/stdio.h: No such file
with another cook's `rm -f`/`ln -sf` on qtdeclarative's sysroot interleaved in
the same log.
Now: skip entirely when the link already points at the right target (the
common case, and the cheapest way to stop the churn), otherwise replace via
ln -s to a temp name + `mv -T`, a single rename(2) -- readers see the old link
or the new one, never nothing.
Verified with 300 concurrent relinks against 3000 probes: 0 missing
observations, where the old sequence reproduced the gap.
libcanberra: plasma-workspace calls find_package(Canberra) TYPE REQUIRED and
includes <canberra.h> from four translation units with no HAVE_CANBERRA guard,
one of them libnotificationmanager (the core notification library). It cannot
be made optional, so it is ported for real. Built shared: it dlopens its
backend through libltdl, the libtool recipe stages libltdl.so only (no .a), so
a static build left every consumer with an undefined lt_dlopenext.
Audio is SILENT. Upstream 0.30 offers alsa/oss/pulse/gstreamer/null and none
speak Redox; the pulse driver needs PulseAudio's client library, which PipeWire
does not provide. The null driver is real upstream code, so the API/ABI is
genuine and all consumers work -- but nothing reaches the speakers until a
native /scheme/audio driver exists.
KX11Extras et al: gate the five X11-only KWindowSystem headers behind the
HAVE_X11 that plasma-workspace already defines. X11 is compiled OUT, not added.
gate-kx11extras.py brace-matches each block and keeps any trailing else outside
the guard, since wrapping a whole if/else-if chain yields invalid C++.
check-recipe-escapes.py: a single backslash in a TOML multi-line string is a
TOML escape, so a sed written as \bFoo\b parses to <BS>Foo<BS> and silently
matches nothing -- no TOML error, no sed error. This class bit the tree three
times. Now gated in preflight; it also found openssh, which used an invalid \$
and could not be parsed by any compliant parser.
cook_build.rs: build git-tracked vendored sources OUT OF TREE. Recipe scripts
rewrite their source, so builds were mutating version-controlled files: a
no-op sed became indistinguishable from a working one, and the integrity gate
fired so often that clearing it stopped being protective. Uses
cp -a --reflink=auto, not cp -al: sed -i is link-safe but `cp -f` and `>`
truncate in place and would write through a hard link into the tracked
original. Content hashing still runs against the pristine tree, so hashes now
describe committed state. Escape hatch: REDBEAR_IN_TREE_BUILD=1.
Unblocks plasma-workspace/plasma-desktop, which required 14 packages that had
no in-tree recipe. Sources are reproducible from each recipe's tar= + blake3;
the vendored source/ trees are deliberately not committed here (212M).
New recipes:
KF6 6.28.0 kf6-kholidays, kf6-krunner, kf6-kstatusnotifieritem,
kf6-kunitconversion
Plasma 6.7.2 knighttime, layer-shell-qt, libkscreen, libksysguard,
plasma-activities-stats, plasma5support, kscreenlocker
Qt 6.11.1 qtpositioning, qtspeech, qttools
libksysguard carries P0-redox-process-backend.patch: processes_local_p.cpp
dispatches on platform macros and had no __redox__ arm, so ProcessesLocal was
entirely undefined. Adds a real backend reading /scheme/proc/ps (pid, ppid,
real+effective ids, thread count, state) and /scheme/sys/mem, with kill() for
signals. Upstream's generic fallback is a pure stub and was not used. Absent
facilities (no setpriority/sched_setscheduler/ioprio on a microkernel) report
NotSupported rather than pretending.
Restored, no longer disabled:
- night colour: kcms/nighttime + kwin's nightlight plugin, now that
KNightTime/Qt6Positioning/KF6Holidays exist
- KIO FileWidgets (file dialog, places model), wrongly swept in with the
unportable kiod/kssld/kioworkers subdirs
- kf6-ktexteditor text-to-speech: removes a disguised stub that rewrote
speechEngine() to return nullptr and mangled call sites into invalid C++
- kwin KWIN_BUILD_SCREENLOCKER=ON (needs kscreenlocker; see below)
Toolchain and recipe fixes:
- redox-toolchain.cmake: append -lgcc. __extendhfsf2/__extendbfsf2 are
global in libgcc.a but hidden in libgcc_s.so (the Redox libgcc version map
stops at GCC_7.0.0 and never emits the GCC_12/13 nodes upstream exports),
and GCC omits -lgcc for -shared, so no C++23 shared library using
std::format<_Float16|bfloat16_t> could link.
- qtmultimedia: rewrite /usr/src/.../meta_types paths and stage metatypes,
so consumers using qt_internal_add_qml_module can configure.
- qtspeech: qtmultimedia is mandatory, not optional -- upstream return()s
with only a NOTICE without it, yielding a green build over an empty
package. Asserts its own output so that cannot recur.
- narrow Linux-only guards with AND NOT REDOX where the toolchain sets
CMAKE_SYSTEM_NAME=Linux purely to get UNIX=TRUE (kio LibMount,
libksysguard NL/Sensors, plasma-workspace NetworkManagerQt).
- drop REQUIRED components that are declared but referenced nowhere
(Location, QCoro6, Qt6 Test) -- verified by exhaustive grep.
- DESTDIR installs where KDE emits absolute KDE_INSTALL_FULL_* paths that
--prefix cannot re-root, which otherwise write into the build host.
Auth stack:
- pam-redbear: add PAM_MODULE_UNKNOWN (28), missing from both the header and
lib.rs; realign 30/31 to Linux-PAM's PAM_CONV_AGAIN/PAM_INCOMPLETE, which
previously held Red Bear-only names on standard values, so anything built
against stock PAM headers mis-decoded them.
- redbear-authd: support yescrypt ($y$ -- the default shadow format on
current Debian/Ubuntu/Fedora, previously an unexplained login failure),
bcrypt and md5-crypt. Plaintext shadow entries now require the
/etc/redbear/allow-plaintext-passwords sentinel and warn on every use,
instead of being compared silently. 19/19 host tests pass.
Build-system hardening:
- verify-tracked-sources.sh, wired into preflight: fails on deletion of any
tracked vendored source, and on modifications with no baseline entry.
local/sources/ had a dirty gate and version checks; local/recipes/*/source/
had none, which allowed an rm -rf to delete 7958 tracked files and a
pristine re-extract to silently overwrite committed fixes (kwin's
std::expected/vulkan-hpp and X11 gating, kio's Q_OS_REDOX resolver guards).
- validate-source-trees.py: resolve recipe.toml through the overlay symlink;
it reported false MISSING for symlinked recipes.
- test-sddm-virgl-qemu.sh: the null-proxy check used report(), which PASSES
on regex match -- so the known Qt6 Wayland null+8 fault would report PASS
when present. Added report_absent().
kscreenlocker builds only its cmake-level Wayland-only changes so far; the
C++ X11 removal (61 call sites) is not yet applied, so kwin's screenlocker
flag is blocked on it.
The fork was an unmapped tree -- verify-fork-versions.sh reported
'libc NOT-MAPPED'. Register it properly so it is governed like every other
Cat 2 fork rather than sitting outside the checks:
- local/fork-upstream-map.toml: libc -> rust-lang/libc 0.2.189, snapshot
mode (vendored from the registry, so its history is unrelated to
upstream's), with the temporary status noted inline.
- verify-fork-versions.sh: src/unix/redox/mod.rs added to the declarative
expected-differ list, so the content check keeps verifying the rest of
the tree instead of being blanket-skipped.
- local/docs/VENDORED-LIBC-FORK.md: what the fork adds, the relibc source
and evidence for every symbol, why the values come from relibc rather
than Linux (Redox's idtype_t is c_int where glibc uses an enum), and a
five-step retirement procedure.
- local/AGENTS.md: listed in the fork table, marked temporary.
Kept vendored for as long as it is required. The gap is upstream-
reportable and belongs in the libc crate.
verify-fork-versions.sh reads the 4th column of fork-upstream-map.toml
and skips the content check for forks marked 'diverged', with a WARN.
verify-fork-functions.sh had no notion of mode and hard-failed every
fork, so kernel (30), bootloader (5) and installer (2) blocked every
canonical build for drift the map itself records as accepted, with
comments saying a full rebase is later work.
Both verifiers now read the same source of truth. Diverged forks are
reported and counted as warnings; every other fork still gates. This
turned a 5-fork / 43-function hard failure into 2 real findings, which
are fixed in the accompanying relibc and base commits.
New local/docs/FORK-BUMP-PATCHING-POLICY.md, wired into AGENTS.md,
local/AGENTS.md and docs/README.md. Core rule: a version bump is a
REBASE, never a REPLACEMENT.
Written from the libredox 0.1.19 incident, which hit three failures at
once -- wholesale replacement that dropped the acpi re-export,
F_DUPFD_CLOEXEC, the mandated authors entry and the Single-Repo
repository URL; a version-named branch that also tripped the build's
fork-branch gate; and a stale fork-upstream-map.toml that failed
verify-fork-versions.sh with a fake-label violation. Covers the vendored
recipe-fork mirror image (metadata moves, build input does not -- how Qt
shipped 6.11.0 against a 6.11.1 recipe), patch handling during a bump,
and toolchain bumps as version bumps.
Also fixes the two bookkeeping gaps that failed this build:
- local/fork-upstream-map.toml: libredox 0.1.18 -> 0.1.19, never updated
when the fork was bumped.
- verify-fork-versions.sh: record redoxfs's legitimate divergence in the
declarative allowlist the script already provides for libredox, with
the originating commit for each file -- a936d00 (Red Bear, Vec from
alloc for no_std bootloader builds) for filesystem.rs and record.rs,
852a971 (Red Bear, RecvFd EOPNOTSUPP) for mount/redox/mod.rs, and
d807dd3 (upstream symlink fix imported ahead of the 0.9.1 tag) for
mount/redox/scheme.rs. Documented rather than bypassed with
REDBEAR_SKIP_FORK_VERIFY.
The Redox target port applier claimed validation on 16.1.0 but its
idempotency probe used each block's longest line, which for three blocks
is generic upstream boilerplate. Against a pristine 16.1.0 tree that
silently skipped the libgcc target arms and BOTH crossconfig.m4 arms
(libgcc/config.host and crossconfig.m4 contain zero redox references, yet
the applier reported 'already present'), producing a half-ported tree --
the exact failure the script documents itself as preventing. Probe on the
longest redox-bearing line instead, matched whole.
Two port requirements the original extraction missed, both fatal:
- gcc/config/redox.opt.urls. GCC 16 requires a .opt.urls companion for
every .opt; s-options fails without it. Contents match what
regenerate-opt-urls.py emits for these two options, cross-checked
against the six upstream .opt.urls declaring the same pthread/rdynamic.
- libtool has no redox host. Upstream Redox gets shared libraries from
recipes/dev/libtool (a Redox-patched libtool 2.5.4-redox-9510) via
libtoolize during autoreconf, not from GCC's bundled libtool.m4. That
route does not apply to GCC 16, which bundles 2.2.7-era macros plus its
own ltgcc.m4. Without redox arms _LT_SYS_DYNAMIC_LINKER leaves
dynamic_linker=no, libstdc++ builds static-only, and the desktop stack
cannot link -- libQt6Core.so and every KF6 library carry DT_NEEDED
libstdc++.so.6. apply-libtool-redox.py registers the four arms that
matter, verbatim from the Redox libtool macros already in prefix/.
Result: x86_64-unknown-redox-gcc 16.1.0 with libstdc++.so.6.0.35 (SONAME
libstdc++.so.6, NEEDED libc.so.6 + libgcc_s.so.1 -- identical to the
13.2.0 library it replaces, exports a superset up to GLIBCXX_3.4.35).
std::ranges::to now compiles for the Redox target; GCC 13.2.0 fails the
same test, which is what blocked kwin's 16 affected files.
install-gcc16-toolchain.sh installs into all three locations a recipe can
resolve a compiler from -- including ~/.redoxer, which src/cook/script.rs
puts highest on PATH -- and is reversible with --restore. Its cstdlib
strtold patch is guarded; the mk/prefix.mk sed is not, and had applied
that block 17 times to the GCC 13 toolchain.
The upgrade decision compared the newest upstream git TAG against $base,
which callers pass as the fork's Cargo.toml CRATE version. For several
Redox crates those namespaces are unrelated, so the comparison was
meaningless:
relibc git tags 0.5.0 / 0.6.0, but Cargo.toml AT tag 0.6.0 is 0.1.0,
and our fork is 0.2.5 -> reported '0.2.5 -> 0.6.0 upgrade'
when upstream master is 0.2.5, i.e. ALREADY CURRENT
libredox newest tag is v0.1.13, but upstream master is 0.1.19
Not cosmetic: upgrade-forks.sh consumes the result as --to=<ref> and does
'git reset --hard <ref>' before reapplying Red Bear commits as a net diff.
Acting on the relibc answer would have reset the fork onto an unrelated
lineage and reapplied our commits against it.
These forks are Cargo path deps with [patch.crates-io] and our label is
<upstream-version>+rb<branch>, so the CRATE version is the upstream
identity Cargo must satisfy -- compare crate-to-crate. Tag scanning
remains as the fallback for forks with no readable Cargo.toml.
Report after the fix:
syscall 0.9.0 -> 0.9.1 upgrade (tag 0.9.1 also exists)
libredox 0.1.18 -> 0.1.19 upgrade (master only; no matching tag)
relibc 0.2.5 ok (was a false positive)
redoxfs / redox-scheme / userutils ok
Known gap, documented at the call site: a fork whose crate version has no
matching tag (libredox) will make upgrade-forks.sh --to=<version> fail
loudly rather than reset onto a wrong ref -- the safe outcome. Threading
the upstream branch ref through as the rebase target is follow-up work.
qt-sysroot.sh: Qt installs QML modules under <prefix>/qml, KDE under
<prefix>/lib/qml, and ecm_find_qmlmodule() only searches the KDE root. A
REQUIRED Qt module was therefore reported missing even though its recipe
staged it correctly (plasma-framework aborting on
Qt5Compat.GraphicalEffects) while every org.kde.* module resolved fine.
Bridge the two roots with relative symlinks, never clobbering a real KDE
module of the same name.
plasma-framework: kf6-kwindowsystem is built KWINDOWSYSTEM_X11=OFF, so
KX11Extras and KWindowInfo do not exist, yet core translation units
reference them. Every such call already sits behind a runtime
KWindowSystem::isPlatformX11(), which is always false here, and the
package already ships some #if HAVE_X11 sites -- this finishes the job
upstream started. Done in Python, not sed: the transform needs brace
matching, and an if/else chain must keep its ELSE body (appletpopup.cpp
sets the Wayland surface role there; wrapping the whole chain would have
deleted the Wayland path). Also guards the unconditional <xcb/xcb.h>
include and drops BlurEffectWatcher's QAbstractNativeEventFilter base,
whose only override is itself X11-guarded, leaving the class abstract.
Pin WaylandScanner_EXECUTABLE to the host tool for the same reason breeze
needed it.
kf6-knotifications: the range-comment sed had an UNANCHORED start address
and an anchored end, so on re-runs it matched the '#if (NOT APPLE ...'
it had produced itself while '#endif()' no longer matched -- the range
ran to END OF FILE, commenting a little more of CMakeLists.txt on every
rebuild until install(FILES ...) lost its closing paren. Anchor the start
so it cannot match its own output, and drop the companion
'sed 127,137 s/^#*//' hack that existed only to undo the damage.
warm-cargo-offline-cache.sh: builds run cargo with --offline --locked, so
resolution comes from the sparse-index CACHE, which only an online cargo
run refreshes. A lockfile can pin a version whose .crate is already
downloaded while the index cache does not list it, producing
error: failed to select a version for the requirement quinn = ^0.11.1
(locked to 0.11.11)
candidate versions found which didn't match: 0.11.9
on a dependency that is in fact present. 13 lockfiles were missing 42
crates this way. --check reports offending manifests without fetching.
test-sddm-virgl-qemu.sh: boots redbear-full on virtio-gpu-gl with
egl-headless so the guest takes the accelerated redox-drm -> Mesa (virgl)
-> Wayland path, captures the serial console and classifies each boot
marker independently rather than collapsing to pass/fail.
The extractor matched '(?:pub )?(?:async )?(?:unsafe )?fn \w+' and then
stripped only the 'fn ', leaving the visibility modifier inside the key.
So 'fn foo' and 'pub fn foo' were different keys, and 'pub(crate) fn' was
not matched at all. Merely changing a function's visibility made it look
deleted: base was reported as missing remove_dentry, which the fork
demonstrably still has, and the report printed nonsense like 'fn pub
socket'. The exclude files had accumulated duplicate entries ('foo' and
'pub foo') to work around this.
Compare bare names on both sides, and normalize the function half of each
exclude entry so existing files keep matching.
Real data from a running build: 6 recipes cooking concurrently but only ONE
actual compiler process, load 2.2/16. Recipes almost never hit their compile
phase simultaneously — most cook-time is single-threaded configure/link/IO — so
the strict per_make = jobs/concurrency division starved the one recipe that was
compiling (mesa pinned to -j2 while 14 cores idled).
Give each concurrent cook the full -j; the active compiler now uses the
whole machine and the OS scheduler absorbs the rare overlap. Worst-case
parallelism is bounded by cook_jobs * jobs, so build-redbear.sh now defaults
COOKBOOK_COOK_JOBS to 4 (was JOBS/2) to cap simultaneous heavy C++ compiles for
RAM safety. A shared make jobserver would cap total jobs precisely while keeping
full per-recipe -j — noted as the proper long-term fix.
JOBS/4 left cores idle (observed load ~4.5/16 during a build). The make-job
budget division keeps total compile parallelism ~JOBS regardless of COOK_JOBS, so
RAM stays bounded; JOBS/2 keeps per-recipe make jobs >= 2 (fully using the -j
budget on busy levels) while adding enough recipe-concurrency to saturate the
cores. Higher (e.g. JOBS) would floor per-recipe jobs to 1 and waste budget.
Export COOKBOOK_COOK_JOBS (default JOBS/4, min 1) so the full build cooks
independent same-level recipes concurrently. Override with COOKBOOK_COOK_JOBS=1
for serial. See the cook orchestrator's run_parallel_cook.
kf6-kcoreaddons 6.28.0 requires ECM >= 6.28.0 but the vendored ECM source/ was
still 6.10.0: the sync engine skipped it because ECM's version marker is a bare
set(VERSION "X.Y.Z") that the parser didn't recognize (only KF_VERSION /
PROJECT_VERSION / RELEASE_SERVICE_VERSION). Re-lay ECM from its 6.28.0 source.tar
(pure cmake modules, no Redox port) and teach both the sync engine and the
validator to read set(VERSION) so this class is caught, not silently skipped.
Converting [source] git=/tar= to path="source" removed the URL cookbook parsed
the package version from, so non-Cargo recipes without an explicit
[package].version failed at packaging ('cannot guess version', hit on
ninja-build). Add version = "0.3.1" (the branch version, matching the vendored
convention e.g. brush) to the 3 affected recipes, and make vendor-recipe.sh add
it automatically on future conversions.
verify-patch-sanity.py validates every active recipe .patch has internally-
consistent hunk line counts — catching the 'malformed patch at line N' failure
at commit/CI/preflight time instead of hours into a cook. This cycle hit that
class three times (qtwaylandscanner, sddm, xwayland), each only discovered when
cookbook tried to apply the patch.
Running it across the repo found 29 latent malformed patches (validated against
GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22').
They were harmless only because they sit in vendored recipes (baked, not re-
applied) — but would fail on any version-bump re-derivation. --fix recounts the
hunk headers (body untouched) and repaired all 29.
Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test
(test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats
(empty placeholders, bare-@@ git hunks).
vendor-recipe.sh converts a transient recipe to the vendored full-fork model
(repo fetch -> strip .git -> [source] path=source with provenance comment ->
un-ignore -> stamp). Used to vendor 23 of the 26 transients. xwayland deferred
(corrupt redox.patch needs regeneration; X11-compat, not login-critical);
smallvil + seatd-redox not in the full set.
Proves build-redbear.sh's vendored source/ staleness loop: seeds on first sight
(no spurious rebuild), stays quiet unchanged, and invalidates target/ on
committed/uncommitted/untracked source/ changes. Keeps the loop body in sync.
The staleness gap behind the qtshadertools failure: cookbook keys its build
cache on the source.tar hash, but the actual build input for a vendored recipe
is the git-committed source/ tree. A version bump that propagates into source/
without changing source.tar (Qt's source.tar was already 6.11.1 while source/
was a 6.11.0 muddle) leaves cookbook reusing a stale stage — the desktop then
builds the old version. New loop fingerprints each vendored source/ by its git
tree hash (+ dirty flag) and rm's target/ on change; first observation only
seeds the fingerprint so it never forces a spurious full rebuild.
The 6 Plasma modules (breeze/kdecoration/kf6-kwayland/kglobalacceld/kwin) and
konsole were 'no-marker(skip)' because src_version/the validator only knew KF's
KF_VERSION. Now both also read set(PROJECT_VERSION) (Plasma) and the split
RELEASE_SERVICE_VERSION_MAJOR/MINOR/MICRO (KDE Gear), so these vendored modules
are version-checked and rebasable. Test E23.
The engine silently no-op'd ('!! not found') when given full paths like
local/recipes/kde/kf6-kio/ — resolve only understood bare names or group/name
tokens. Now strips a trailing slash, an absolute ROOT prefix, and a
local/recipes/ prefix before matching. Test E22.
9th silent-failure bug: the version stamp was checked BEFORE the real code marker
(Qt .cmake.conf / KDE KF_VERSION). A code-only revert leaves the stamp stale, and
the stale stamp then reported the module 'consistent' -> the engine skipped a
needed rebase and the validator false-passed a divergent tree. Both now check the
authoritative code marker first and use the stamp only as a fallback for
marker-less trees. The validator also learned KDE's KF_VERSION marker. Test E21.
Data-loss bug: rsync --delete during a rebase dropped RedBear-added files that
carry no __redox__ token (a Redox Wayland-crash wrapper .sh, a utmp compat
header, a generated D-Bus interface) because the capture was grep-on-marker
only. The engine now, for EVERY vendored rebase, fetches pristine(Vold) and
carries forward every committed file absent from Vold (content-agnostic, matched
by path) — so RedBear additions survive. If Vold cannot be fetched it rejects
(manual) rather than produce a lossy tree. New tests E20 (non-marker added file
preserved) + Vold fixtures for the rebase cases. 41 tests green.
Found by extending the edge-case suite to the baked-shim path (E17-E19) after a
sync reported 'ready' but silently dropped a module's Redox shim:
- git show used an ABSOLUTE recipe path ('HEAD:/mnt/.../source/f') which git
rejects (needs repo-relative) -> empty content -> garbage delta. Now uses
${dir#$ROOT/}.
- diff|patch under 'set -o pipefail': diff exits 1 whenever files differ (always
here), so the pipeline looked failed even when patch succeeded -> EVERY shim
false-rejected. Now captures the delta to a file and checks patch's own exit.
- a shim whose file was renamed/removed upstream, or unreadable from HEAD, was
silently skipped. Now: Redox-added files are carried in verbatim; a missing
target or unreadable HEAD file -> SHIM-REJECT (manual), never a silent drop.
Also: old-version URL derivation now rewrites the major.minor dir component too
(KDE mirrors nest tarballs under .../6.28/foo-6.28.0). CI branch glob widened to
[0-9]* so 0.4.0/0.5.0 release branches keep triggering.
Integration (answers 'does it run automatically'):
- redbear-ci.yml now runs test-versioning-machinery.sh (33 cases) and
verify-external-source-versions.sh on every push/PR; trigger widened to
0.3.* so a release-branch increment keeps CI (and these checks) firing.
- The validator already runs in build-preflight.sh Phase 1.0C (every build),
and bump-graphics-recipes.sh (invoked by bump-release.sh --with-external)
now calls the sync engine — so an external bump propagates + is validated.
Engine hardening (both regression-tested):
- reconcile_blake3: a vendored bump often left recipe blake3 stale (URL bumped,
hash not); the engine now sets recipe blake3 to the authoritative source.tar
hash (only when source.tar version matches the URL version; no-op under --check).
- transient recipes (no vendored source/) now get their stale cached source.tar
bumped to the declared version too, instead of being skipped.
Edge-case suite (local/scripts/test-versioning-machinery.sh, 27 cases, zero
network via file:// tarballs) covering the validator and the sync engine.
It found and fixed three silent-failure bugs — the exact class the user flagged:
- sync engine used 'rsync -a --delete' (size+mtime quick-check): a changed file
of identical size with a coincidental mtime was skipped, leaving stale content
in source/. Now uses --checksum (content comparison).
- apply_patch relied on patch's default fuzz: a patch whose context no longer
matched the new upstream was force-fitted instead of rejected. Now --fuzz=0
(exact context; line offsets still allowed).
- decorrupt collapsed ANY run of identical lines, mangling legitimate paired
lines; now only collapses the corruption pattern (runs >=4).
Also: unknown-version recipes are skipped (never clobber a ported tree like
mesa), and grep patterns use [[:space:]] for portability.
Root cause of the Qt 6.11.0/6.11.1 and KDE 6.10.0/6.28.0 divergences: a version
bump rewrote recipe tar=/blake3= but never propagated into the vendored source/
tree (the actual build input), and nothing caught the resulting drift.
Adds the missing pieces (see local/docs/VERSIONING.md):
- sync-recipe-source.sh: propagation engine — rebases a vendored source/ onto
the recipe's declared version (pristine + patches + captured baked delta),
with baked-shim preservation and corruption drop; reports patches/shims that
need a manual rebase instead of applying with fuzz.
- verify-external-source-versions.sh: preflight gate (build-preflight.sh Phase
1.0C, REDBEAR_SKIP_EXTERNAL_SOURCE_CHECK) that fails loudly on recipe-vs-
source-vs-tarball version divergence — the tar-recipe analogue of
verify-fork-functions.sh.
- bump-graphics-recipes.sh: now calls the sync engine after a bump, so a bump
can never again silently no-op on a vendored recipe.
- source/.redbear-src-version stamp; seeded for the corrected qt modules.
The redoxer toolchain ships a prebuilt host LLVM built X86;AArch64;RISCV only.
Mesa resolves dependency('llvm', modules:[amdgpu,...]) against that host
llvm-config (COOKBOOK_HOST_SYSROOT), so radeonsi/radv AND the Intel iris/ANV
CLC precompile path fail and the whole desktop stack silently drops. The
binary prefix never regenerates that host LLVM, so add an idempotent pre-cook
hook (redbear-full only): if the toolchain llvm-config lacks AMDGPU, cook
host:llvm21 (recipe carries X86;AMDGPU;NVPTX) and splice its llvm-config +
libLLVM.so + AMDGPU headers/static libs into the toolchain, backing up the
originals first. Fails loudly; a no-op once AMDGPU is present.
The mini ISO built and booted the kernel but pid1 `init` panicked right after
switchroot with "memory allocation of 1343504 bytes failed" (with GBs free) —
a stale-ABI skew. base-initfs is a `custom` recipe that builds the static
boot-critical initfs binaries (init/logd/randd/zerod/acpid/pcid/vesad/fbcond/…),
but its content-hash cache does not track the boot-ABI crates
(relibc/libredox/syscall/redox-scheme). On a boot-ABI change it stayed "cached"
with binaries linked against the old ABI, so init died before login.
Add base-initfs to ABI_CRITICAL_PKGS (relinked with base/redoxfs/userutils when
the boot ABI changes) and to PRECOOK_PKGS (republished via the reliable single
`repo cook` path, ordered after base whose sysroot binaries it stages).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The boot-ABI invalidation ("relink ONLY the static initfs-critical binaries:
base redoxfs userutils bootstrap") rm's each package's repo pkgar + target to
force a relink. Only relibc was in PRECOOK_PKGS, so only relibc got re-published
via the reliable single-recipe `repo cook` path. base/redoxfs/userutils cook
"successful" inside `make live`'s nonstop graph but lose their stage.toml before
publish, so repo marks them outdated and never publishes base.pkgar — the
installer then fails with `Package PackageName("base") not found` at image
assembly (mk/disk.mk).
Add base/redoxfs/userutils to the pre-cook set (both configs, ordered after
relibc which they depend on). The `[ ! -f repo/$pkg.pkgar ]` guard makes it
zero-cost when they were not invalidated. bootstrap is omitted (part of the
base recipe, not a standalone package).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
redbear-dnsd is an in-house recipe (path = "source"), but recipes/system/
redbear-dnsd was a real dir with only a recipe.toml file-symlink, so
'cargo install --path recipes/system/redbear-dnsd/source' found no Cargo.toml
(the source lives in local/recipes/system/redbear-dnsd/source). Make it a
whole-dir symlink to local/ like every other in-house recipe (ehcid, netctl,
...), and add it to apply-patches.sh's link list so it is recreated durably.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bootloader builds for its own custom targets (targets/*.json / UEFI), so
cargo check --target x86_64-unknown-redox mis-reports it (e.g. 'cannot find
macro println'). Exclude it from the sweep to avoid false-positive gating.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New opt-in flag runs 'cargo check --target <T> --offline' via the redoxer
toolchain on the fork sources (always built) plus the config's local Rust
recipes, BEFORE the cook/prefix cycle. cargo check does no linking (needs no
relibc.a/linker), so it is fast (~seconds/fork) and surfaces ALL type/borrow
errors at once instead of one per multi-minute full build; any failure aborts
before building. Target-scoped: local recipes are filtered to the config's
package set (a mini build does not check graphics recipes).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
verify-overlay-integrity.sh scanned the entire recipes/ tree including
recipes/wip/, so a broken WIP symlink for a graphics recipe
(qt6-wayland-smoke) failed the overlay check even when building the text-only
mini target that has nothing to do with graphics. recipes/wip/ is upstream
work-in-progress scratch — the local-over-WIP policy stages shipped packages
into local/recipes/ and links them into recipes/<cat>/, never recipes/wip/ —
so its symlink health must never clutter or block a build. Exclude it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Several docs still referenced files in local/docs/legacy-obsolete-
2026-07-25/ after that directory's 2026-07-27 cleanup deleted most of
its contents. The directory now only contains SUPERSEDED.md; all
other legacy-obsolete entries were fully removed. The doc cleanup
phase of the round-2 D-Bus audit identified each broken reference and
fixed it by pointing at the current canonical location.
Repairs:
- ACPI-IMPROVEMENT-PLAN.md, BUILD-SYSTEM-INVARIANTS.md,
INIT-NAMESPACE-MANAGER-SCALABILITY-PLAN.md,
NETWORKING-IMPROVEMENT-PLAN.md, USB-IMPLEMENTATION-PLAN.md:
legacy-obsolete-2026-07-25/IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md
-> IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md
(restored to top-level local/docs/).
- CONSOLE-TO-KDE-DESKTOP-PLAN.md:
legacy-obsolete/BUILD-SYSTEM-HARDENING-PLAN.md
-> COLLISION-DETECTION-STATUS.md
- TOOLS.md, RELEASE-BUMP-WORKFLOW.md:
legacy-obsolete/HOOKS.md
-> RELEASE-BUMP-WORKFLOW.md § 'Git Hooks' (content merged).
- patches/README.md, RATATUI-APP-PATTERNS.md:
removed dangling refs to legacy-obsolete/PATCH-PRESERVATION-AUDIT
and redbear-power-improvement-plan (both deleted with no successor
doc; the related guidance lives in the canonical plans).
Each replacement preserves the link's intent: every old reference was
pointing to a doc whose content has either been restored to top-level,
absorbed into a different canonical doc, or replaced by a plan
reference that covers the same surface.
Companion to the prior 'address 5-lane review blocking findings' commit
(which only contained the restored dbus symlink). This commit bundles the
remaining review-driven fixes for the implementation scope.
- sessiond power_off/reboot/suspend: propagate write_all errors. The
'let _ = f.write_all(...)' pattern meant a successful open followed
by a failed write was reported to the caller as success. Now checked
with explicit error propagation: write failure resets
preparing_for_shutdown/sleep to false and returns a D-Bus error.
- notifications: drop un-implemented capability advertisement
('actions', 'persistence'). Real capabilities now: ['body', 'body-markup'].
Body and app_name no longer printed to stderr verbatim (length only).
Server info version bumped 0.3.0 -> 0.3.1 to match Cargo.toml.
- wifictl: enable dbus-nm feature by default. Previously
default = [] made the 656-line NM interface dead code behind a
feature gate that the recipe never enabled.
- guard-recipes.sh --restore: add parent-symlink guard (same as --fix).
Without it, restoring a recipe whose parent directory is a symlink
into local/recipes/ deletes the real file from disk.
- verify-fork-functions.sh: narrow blanket exclusion to fmt+eq only.
drop/deref/hash/clone/etc. must go through fork-specific exclude file.
Verified:
redbear-notifications: 8/8 tests pass
redbear-sessiond: 51/52 tests pass (1 pre-existing failure
from later commit a9e1c34e27 outside scope)
redbear-wifictl: compiles with dbus-nm default
Per AGENTS.md 'DO NOT edit files under mainline recipes/ directly', create
a Red Bear fork under local/recipes/net/openssh/ that the build system
materializes via apply-patches.sh symlink.
Replaces two upstream placeholders:
1. The 'sed -i AddressFamily inet' workaround with a real capability
detection that probes relibc's <netinet/in.h> for AF_INET6 and
selects AddressFamily=any (dual-stack) or inet (IPv4-only)
accordingly. The OPENSSH_FORCE_IPV4=1 environment variable
overrides the probe to force IPv4-only.
2. The commented-out '# ssh-keygen -t ... -N ""' TODO with a real
subshell-rendered postscript that generates ed25519, rsa, and
ecdsa host keys (idempotent re-runs skip existing keys).
The patch list in the fork recipe is identical to the mainline
(just 'redox.patch' — the upstream-tracked Redox port). The Red Bear
modifications to the build are entirely in 'script =' below, so
no additional Red Bear overlay patch file is required.
Also extends local/scripts/apply-patches.sh with the 'net/openssh'
symlink entry under a new '# Network fork recipes' section.