Session 14 entry covering b8c1c780d (first C-7 migration
patch + v2 script unfetch-before-fetch fix). Updated
durability caveat: 13 most recent commits cover the full
arc; commit history table gets the new b8c1c780d and
975cda686 rows; test count 120 -> 122. Removed a duplicate
975cda686 row that was added by accident in the previous
edit.
First durable artifact from the C-7 KF6 sed migration: the
inline sed -i chains in local/recipes/kde/kf6-karchive's
[build].script have been captured as a durable external
patch in local/patches/kf6-karchive/01-initial-migration.patch.
This patch was generated by running the v2 migration
script (commit 827895d32) against the live kf6-karchive
recipe. The actual sed edits captured are:
-ecm_install_po_files_as_qm(poqm)
+#ecm_install_po_files_as_qm(poqm)
The other 3 sed chains in the recipe (ki18n_install(po),
.arg(mode), .arg(d->mode)) were no-ops against the karchive
6.26.0 upstream tar (the target lines either no longer
exist or are already in the desired state in this upstream
version). The migration script correctly captures only the
real edits; no-ops produce no patch hunks.
Script fix in this commit:
The migration script's v2 was producing silently empty
diffs on already-cooked recipes because the cookbook's
`fetch` re-uses an existing source/ tree if it finds one
(it does this to avoid re-extracting tars on every fetch).
For C-7 migration we need the truly pristine upstream
state. The fix:
1. Add an explicit `unfetch` step BEFORE the `fetch`
(so the source/ dir is removed before re-extraction)
2. Set `REDBEAR_ALLOW_LOCAL_UNFETCH=1` because kf6-*
and qt* recipes are local-overlay recipes under
local/recipes/, and the cookbook's default policy is
to never clobber a local-overlay source (the env var
overrides that policy for the migration's explicit
unfetch call only)
3. Apply the same env var to the post-capture `unfetch`
at the end of the script
The script header documents this cookbook behavior with
inline comments so a future contributor doesn't re-introduce
the silent-failure mode.
Patch filter:
The migration script's diff includes ECM-autogenerated
files like .clang-format that aren't real sed edits. The
captured patch was 122 lines, of which 95 were the
.clang-format autogeneration. The committed patch is the
filtered 24-line version that drops `.clang-format`,
`.gitignore`, and any `target/` artifacts. (A future
script improvement could do this filter inline.)
Test count: 120 -> 122 (2 new tests in test_migrate_kf6_seds.py):
- test_sets_local_unfetch_env_var: regression guard
against forgetting the env var
- test_unfetches_before_fetching: regression guard
against calling fetch before unfetch (silent-failure
mode in v2)
Next steps for kf6-karchive specifically (manual, not part
of this commit):
1. Edit local/recipes/kde/kf6-karchive/recipe.toml's
[build].script to remove the 4 inline sed -i chains
and add:
REDBEAR_PATCHES_DIR="local/patches/kf6-karchive"
cookbook_apply_patches "${REDBEAR_PATCHES_DIR}"
2. Cook again to verify the patch + rewritten script
produce a byte-identical stage.pkgar
3. Commit the recipe rewrite + the patch together
Verified:
- The migration ran end-to-end on the live tree
- The patch applies cleanly to the pristine upstream
- 122/122 Python tests pass
- The new test_sets_local_unfetch_env_var and
test_unfetches_before_fetching both pass
C-7 status: 1 of 56 KF6 sed-bearing recipes migrated.
55 remaining (next: kf6-attica has the smallest sed chain;
after that, breeze, kf6-syntaxhighlighting).
Add a single-target aggregate `make lint-build-system-all`
that runs every offline-safe lint + every test + every
smoke test in one shot. Per the user request to make the
'build system healthy?' question easy to answer.
New `make lint-build-system-all` target chains:
make test-lint-scripts (120 Python unit tests)
make test-migration-dry-run (C-7 KF6 sed migration)
make test-scratch-dry-run (improvement #10 skeleton)
All exit 0 in offline mode; <3s wall-clock total.
The existing `make lint-build-system` chain was
incomplete — it ran lint-patches, lint-kf6-deps, and
lint-cook-recipe but not lint-recipe, test-migration-dry-run,
or test-scratch-dry-run. This commit fixes that:
make lint-build-system: lint-patches lint-kf6-deps \
lint-cook-recipe lint-recipe
The two aggregates serve different purposes:
- `lint-build-system` is the historical aggregate
including lint-patches. lint-patches returns 2 in
--no-fetch mode (all skipped) so the Gitea workflow
wraps it in a case statement. The original use case was
'is the project build-system clean?', which is
network-dependent.
- `lint-build-system-all` is the new offline-only
aggregate. It does NOT include lint-patches, so it
always exits 0 on a healthy tree. The new Gitea job
depends on unit-tests + lint-recipe + migration-dry-run
+ scratch-dry-run (so it can run after the four per-step
lints have already validated the individual layers).
Wired into:
Makefile:
- `make lint-build-system-all` + `make lint-build-system`
both now include lint-recipe.
- Both targets added to .PHONY.
Gitea Actions:
- New job `lint-build-system-all` (job 7 of 11, depends
on the four per-step lint jobs).
- Renumbered the docs stage to 1i.
BUILD-SYSTEM-IMPROVEMENTS.md:
- Make targets table: added scratch-rebuild, lint-build-system-all.
BUILD-SYSTEM-V6-HARDENING-POSTMORTEM.md:
- Durability caveat: 11 most recent commits -> 12 most
recent (added e1c2e7958); updated flow description to
include 'postmortem rebalance in e1c2e7958'.
Verified:
`make lint-build-system-all` passes in <3s.
11-job Gitea Actions pipeline YAML validates.
120/120 Python tests pass.
Two follow-up items from the #10 PARTIAL commit (0f8ad8a50):
1. Added `make scratch-rebuild` target to the Makefile. The
v2 of scratch-rebuild.sh supports running without --dry-run
but there was no actual make wrapper for it. The new
target runs the script in non-dry-run mode (deletes
target/<arch>/{build,sysroot,stage.tmp}/ per recipe in
the closure and re-cooks in dep order). JOBS=N (default 4)
controls the parallel rebuild workers. Verified end-to-end:
the rebuild correctly deletes the 6-recipe closure's
build dirs and starts a parallel cook. m4 succeeds; bison
fails (missing host toolchain dep) — the failure is
correctly captured to the log without aborting the script.
2. Updated BUILD-SYSTEM-V6-HARDENING-POSTMORTEM.md to reflect
the 13-session / 9.5-DONE / 120-Python-test state:
- Added Session 13 entry covering #10 foundation + tests
+ the Python regex gotcha discovered during testing
(`^[[:space:]]*` vs `^[\s]*`)
- Updated test count: 99 -> 120 Python (now 7 test files
in local/scripts/tests/, was 4 at session 1)
- Updated scope line (12-session -> 13-session)
- Updated durability caveat (10 most recent commits -> 11
most recent commits; added `0f8ad8a50` and `9e5794ea7`)
- Updated 'What remains uncommitted' table
- Updated commit history table with rows for
`827895d32`, `9e5794ea7`, `0f8ad8a50`
- Added `test_scratch_rebuild.py` row to test coverage
table
BUILD-SYSTEM-IMPROVEMENTS.md was already updated in the
#10 commit (PARTIAL status, make target table, Implemented
#10 entry). This commit re-confirms those updates after
the postmortem rebalance.
Total state:
- 9.5/10 build-system improvements DONE (1 PARTIAL on #10)
- 120/120 Python tests + 27/27 Rust tests pass
- 10-job Gitea Actions pipeline
The build-system hardening arc is now as complete as a
single-session work scope allows. Further work requires
either the multi-day #10 full L-sized verification, the
multi-week #7A QML gate, or one of the larger blocked cooks
(sddm, KF6 dep chain).
L-sized improvement #10 (cookbook scratch-rebuild) is now
PARTIALLY shipped: the M-sized foundation is a runnable
script that does the right thing in the common case.
Verification against real cascades + integration with
rebuild-cascade.sh remains for a separate session.
local/scripts/scratch-rebuild.sh (190 lines, +x):
Step 1: discover autotools-using recipes by content regex
(aclocal|autoreconf|libtoolize|automake|autoconf|gettextize|./configure)
PLUS the AUTOTOOLS_CORE list (m4, autoconf, automake,
libtool, bison, flex, gettext) which are always-included
because they are autotools infrastructure even if they
don't directly invoke aclocal.
Step 2: compute transitive closure via BFS over the recipe
TOML dep graph, including both [build].dependencies and
[build].dev_dependencies. Found 6 autotools users in the
live tree: bison, diffutils, flex, grub, libtool, m4.
Step 3: for each recipe in the closure, delete
target/<arch>/{build,sysroot,stage.tmp}/ — PRESERVE source/
so we don't re-fetch the upstream tar.
Step 4: re-cook in dep order with --jobs=N (default 4) so
the rebuild itself runs in parallel via the dep-aware
scheduler (#1).
Cook errors during Step 4 do NOT abort the script with
exit 1 — a failed cook may indicate a missing upstream dep
(legitimate on a fresh checkout) rather than a real bug.
The user inspects the log and re-runs after addressing the
dep. This is documented in the header + Step 4 comment.
Supports --dry-run, --jobs=N, --help. Env overrides for
RECIPES_DIR + LOG_DIR (mirroring the migration script's
test escape hatch pattern, used by the test suite below).
21 unit tests in local/scripts/tests/test_scratch_rebuild.py:
TestAutotoolsCoreList (3) — m4, libtool, bison/flex
in AUTOTOOLS_CORE
TestAutotoolsContentRegex (8) — catches each canonical
autotools command; does
NOT match cmake/make/meson
TestRecipeDepParsing (4) — parses dependencies and
dev_dependencies; both;
neither
TestScriptHelp (1) — --help describes the
script
TestScriptStructure (5) — executable bit; uses
./target/release/repo;
PRESERVES source/; uses
--jobs=N; dry-run safe
Test count: 99 -> 120 (all in <1s).
The test file also surfaces a real Python regex gotcha:
`^[[:space:]]*` (POSIX char class with quantifier) silently
fails to match the empty string under Python's regex
engine, while `^[\s]*` (shorthand) works correctly. The
test regex uses the shorthand to avoid this.
Wired into:
make test-scratch-dry-run -> scratch-rebuild.sh --dry-run
Gitea Actions job scratch-dry-run (job 6 of 10, every PR)
With this commit, 9 of 10 build-system improvements in
BUILD-SYSTEM-IMPROVEMENTS.md are DONE (1 PARTIAL on #10);
the remaining 1 is #7A (QML gate, Qt6 engine fix, not a
cookbook improvement).
Verified: `./local/scripts/scratch-rebuild.sh --dry-run`
correctly discovers 6 autotools users and computes the
6-recipe closure. `make test-lint-scripts` still passes
120/120 tests in <1s. Gitea workflow YAML validates with
10 jobs total (was 9).
Commit 827895d32 added the C-7 KF6 sed migration script v2
and 13 unit tests, but didn't wire the new make target or
Gitea Actions job. This commit adds both so the migration
smoke test runs on every PR.
Makefile:
- New `make test-migration-dry-run` target. Runs
`migrate-kf6-seds-to-patches.sh --dry-run --limit=1`.
Discovers candidates, prints the per-recipe plan, exits 0
on success. Does NOT do any fetches, cooks, or patch
writes. <5s wall-clock. Added to `.PHONY:`.
- Picked up automatically by the existing
`make test-lint-scripts` discovery path (the new test
file is in local/scripts/tests/, so it's already covered
by the existing target — no change there).
Gitea Actions (`.gitea/workflows/build-system.yml`):
- New job `migration-dry-run` (job 5 of 9, depends on
`unit-tests`, runs on every PR + branch push + schedule).
Triggers `make test-migration-dry-run` and treats
exit 0 as success.
- Renumbered subsequent stage headers to 1f (was 1e).
- Updated unit-tests job description: '55 cases' -> '99
cases' (reflects the new 13 migration tests).
Docs:
- BUILD-SYSTEM-IMPROVEMENTS.md: added the new make
target to the Make targets table.
- BUILD-SYSTEM-V6-HARDENING-POSTMORTEM.md: Session 12
entry covers the v2 migration script + 13 tests + CI
integration. Updated test count (86 -> 99 Python),
scope line (11-session -> 12-session), C-7 finding
(now 'migration script v2 ... now runnable; per-recipe
execution + recipe rewrite still manual'), and
durability caveat (10 most recent commits now cover
the migration work + this postmortem itself).
- Added the test_migrate_kf6_seds.py row to the test
coverage table.
Verified:
- `make test-migration-dry-run` discovers 1 candidate
and exits 0 in <1s.
- `make test-lint-scripts` still passes 99/99 tests
in <1s.
- Gitea workflow YAML validates: 9 jobs total
(was 8).
The C-7 KF6 sed migration script shipped in commit ae749ffb2
was a stub with three structural problems that made it
unrunnable:
1. Called 'repo cook $recipe_dir' with a path, but the
cookbook CLI takes bare names — this would have failed
with 'Package name invalid' on first run.
2. Step 2 created an empty pristine_dir via mktemp -d but
never populated it, so the diff was always empty
(zero-byte output, 'no diff' branch taken, no patch
written).
3. Step 4 was 'SKIP — manual rewrite pending', so the
script wrote no patch even when the inline sed chains
actually edited the source.
Replace the stub with a working v2 that:
- Uses 'repo cook $name' (bare names) throughout
- Snapshots source/ → source-pristine/ BEFORE the cook
so the pristine state is real, not empty
- Runs the full cook (with -i || true so a build failure
after the sed step doesn't abort the migration — we
only need the post-sed source state)
- diffs the real pristine vs post-cook tree, with
--exclude='.git' and --exclude='target' so the diff
is the actual sed edits
- Saves the diff as
local/patches/<name>/01-initial-migration.patch with
a header explaining provenance and the cookbook_apply_patches
invocation the recipe should use
- Cleans up source-pristine/ + runs 'repo unfetch $name' so
the next migration run starts from a clean slate
Add a --dry-run mode that lists candidates without fetching,
for safe CI / smoke testing. Add --recipe=<name> and
--limit=N for targeted runs. Add --help.
Add a test escape hatch via REDBEAR_MIGRATE_RECIPES_DIR and
REDBEAR_MIGRATE_PATCHES_DIR env vars so the candidate
discovery can be exercised on a synthetic tree without
touching the live project. Also gate the cookbook-binary
check on DRY_RUN != 1 so --dry-run doesn't require a
pre-built ./target/release/repo.
13 unit tests in local/scripts/tests/test_migrate_kf6_seds.py:
TestCandidateDiscovery (7):
- discovers sed+tar recipe
- skips recipe without sed
- skips recipe with git source (Rule 1 in-tree, not
sed-migration candidates)
- --limit=N caps results
- --recipe=<name> filters
- existing patch triggers SKIP branch (via static analysis)
- --help output describes the script
TestScriptStructure (6):
- regression: uses bare names, not paths
- uses release/repo binary
- creates patches dir
- diff includes .git/target excludes
- unfetches after capture
- idempotent SKIP when patch exists
Test count: 86/86 → 99/99 (all in <1s).
The actual migration run still requires the full KF6 dep
chain to be built (qtbase, qtdeclarative, kf6-extra-cmake-modules,
plus the recipe's own deps). The 56 recipes are now
discoverable + scriptable; the recipe-by-recipe verification
+ patch validity check remains a per-recipe manual step
(open the patch, confirm the diff matches the inline sed
chain, edit [build].script to call cookbook_apply_patches,
re-cook, byte-compare stage.pkgar).
The 3 new commits since the postmortem was first written
(5325360b4 status reporter, fbc32a6d8 parallel cook pool)
advance the v6.0 hardening arc from 7/10 DONE to 9/10 DONE.
This update captures Sessions 10 and 11 with the actual
deliverables, commit hashes, and end-to-end verification
notes.
Updates:
- Session 10 row: ae749ffb2 (22 build-system files) +
5325360b4 (status reporter, src/cook/status.rs +
src/bin/repo.rs wiring).
- Session 11 row: fbc32a6d8 (src/cook/scheduler.rs +
src/cook.rs registration + --jobs=N flag in
src/bin/repo.rs + 7 dep_levels unit tests).
- Final state: 9 DONE (was 7), 1 OPEN (#10 scratch-rebuild).
The OPEN row is now explicitly flagged as
Qt6-engine-fix vs cookbook-improvement and noted as
appropriate for a separate session.
- Test coverage: 86/86 Python + 27/27 Rust (was 55/55 +
20 Rust). Added 6 status + 7 scheduler tests.
- Durability caveat: now notes 8 most recent commits cover
the v6.0 deliverables; remaining in git status is this
postmortem itself + the user's WIP.
- Replaced the 'What to commit (suggested)' block + the
'Files in v6.0 hardening arc (clean tree, ready to
commit)' table with the actual commit history table
(3 commits, durable in git) and a 'What remains
uncommitted' table listing only the postmortem itself +
user WIP paths the next commit will need to be careful
about.
When the user runs `repo cook A B C D`, the cookbook cooks the
transitive closure of those recipes strictly serially — even
recipes in the same dep level that have no inter-deps. On a
15-recipe KF6 batch this costs ~2 hours wall-clock when the
same batch could cook in ~45 minutes if level-0 recipes
ran in parallel.
Add `repo cook --jobs=N` to enable dep-aware level
parallelism. Default is 1 (serial — current behavior
preserved). The flag is only honored when the ratatui TUI
is off (CI=1 mode); the TUI has its own per-recipe
scheduling and is unchanged.
src/cook/scheduler.rs implements `dep_levels()`: walks the
already-dep-first `Vec<CookRecipe>` from
`get_build_deps_recursive`, computes
`levels[i] = 1 + max(level of any direct dep in this vec)`
or 0 if no deps in the vec. Grouping by level gives the
topological wavefront — recipes in level 0 are independent
and can cook concurrently; level 1 depends only on level 0;
etc.
src/bin/repo.rs: when jobs > 1 and !tui, replace the serial
`for recipe in recipes` loop with a level-driven parallel
loop using `std::thread::scope` (Rust 1.78+). For each
level: spawn up to `jobs` worker threads, each calling
`repo_inner()` with its own &mut StatusReporter, then
drain completed handles before advancing to the next level.
The drain-after-spawn pattern keeps live-worker count <= jobs
even for a 1000-recipe batch.
Cloning the references in scope is required for the
thread::scope closures (references are Copy, so a single
`let recipes_ref = &recipes;` works across all spawns). The
`cook_one` helper function takes all needed data as
parameters (no captures) so it can be called from both
serial and parallel paths. Test count: 20 -> 27 (7 new
dep_levels() unit tests covering empty / single / linear /
independent / diamond / dev_dependencies / unknown-dep).
Verified end-to-end with a 5-recipe batch:
$ CI=1 ./target/release/repo cook --jobs=4 \
redbear-statusnotifierwatcher redbear-traceroute \
redbear-udisks
[01/05] redbear-statusnotifierwatcher: starting
[02/05] redbear-traceroute: starting
[03/05] expat: starting
[01/05] redbear-statusnotifierwatcher: fetched (0s)
[02/05] redbear-traceroute: fetched (0s)
[02/05] redbear-traceroute: built (2s)
[02/05] redbear-traceroute: done (total 2s)
[03/05] expat: fetched (5s)
[01/05] redbear-statusnotifierwatcher: built (17s)
[01/05] redbear-statusnotifierwatcher: done (total 17s)
[04/05] dbus: starting <- level 1
[04/05] dbus: cached
[05/05] redbear-udisks: starting <- level 2
...
Level 0 ran 3 recipes in parallel; level 1 (dbus) and level 2
(redbear-udisks) advanced after level 0 finished. On a clean
rebuild (rm -rf target/ first), parallel was modestly faster
than serial on a 3-recipe batch (45s vs 48s) — the speedup is
bounded by the longest single build (17s for the heaviest
recipe). The 2-3x gain from the proposal is on a 15-recipe
KF6 batch where the longest build is 5-10 min, not a
3-recipe batch where it's 17s.
Caveat: the shared `build/qt-host-build` host toolchain
is not currently locked. A parallel cook that triggers two
qt-host-build recipes simultaneously could race. Mitigation
for v2: `flock` around qt-host-build invocations in
src/cook/script.rs. Not done in this commit because no
current test recipe triggers qt-host-build in the redbear-full
path, and the host-build path is host-cargo, not
cross-cargo, so the race window is narrow.
With this commit, 9 of 10 build-system improvements in
BUILD-SYSTEM-IMPROVEMENTS.md are DONE. The remaining #10
(cookbook scratch-rebuild system) is L-sized (1 week,
M risk) and a separate session.
When the cookbook runs without its ratatui TUI (e.g. `CI=1 repo cook
...` from a real terminal, SSH session, or backgrounded shell), the
only progress output is the per-recipe tail of the build script. The
user has no aggregate '5/15 done' view, no per-phase signal (fetch vs
build vs package), and no elapsed-time.
src/cook/status.rs adds a one-line StatusReporter that fills that
gap. Auto-enables when the TUI is off AND log capture is off AND
stderr is a TTY. Output format:
[05/15] kf6-kio: starting
[05/15] kf6-kio: fetched (3.2s)
[05/15] kf6-kio: built (4m 18s)
[05/15] kf6-kio: done (total 4m 23s)
Cached recipes emit `[NN/MM] recipe: cached` (no phase breakdown).
Writes to stderr via eprintln! so it never gets mixed with the
captured build-script log. The ratatui TUI in run_tui_cook() is
unchanged — this is the parallel status path for non-interactive
cooks.
Wiring: a &mut StatusReporter is created in main_inner's cook loop,
threaded through repo_inner() and the per-phase closures in
src/bin/repo.rs. Two phase emissions per recipe: `fetched` (after
handle_fetch) and `built` (after handle_cook, ONLY when the build
is not cached — cached cooks skip the 'built' emission to avoid
confusion). 6 unit tests cover format_elapsed boundaries, the
disabled no-op path, and phase tracking. Rust test count:
14 -> 20 (all pass in 3.2s).
Verified end-to-end with a real multi-recipe cook:
CI=1 ./target/release/repo cook redbear-statusnotifierwatcher \
redbear-traceroute \
redbear-udisks
[01/05] redbear-statusnotifierwatcher: starting
[01/05] redbear-statusnotifierwatcher: fetched (0s)
[01/05] redbear-statusnotifierwatcher: cached
[02/05] redbear-traceroute: starting
[02/05] redbear-traceroute: fetched (0s)
[02/05] redbear-traceroute: built (2s)
[02/05] redbear-traceroute: done (total 2s)
[03/05] expat: starting
...
Per build-system improvement #4. With this commit, 8 of 10
improvements in BUILD-SYSTEM-IMPROVEMENTS.md are DONE. Remaining:
#1 (parallel cook pool), #7A (QML gate), #10 (scratch-rebuild).
The v6.0 build-system hardening arc lands 5 of the 10 improvements
proposed in local/docs/BUILD-SYSTEM-IMPROVEMENTS.md. All scripts
have unit tests (62 -> 86, all pass in <1s) and the new 'lint-recipe'
Gitea Actions job runs on every PR.
Per-recipe audit & lint scripts (catch R1/R2 violations BEFORE cook):
* audit-patch-idempotency.py — verifies external patches in
local/patches/ still apply against the upstream pinned rev.
Caught 1 real bug on first run: libdrm/02-redox-dispatch.patch
hunk at xf86drm.c:321 no longer matches libdrm-2.4.125.
* audit-kf6-deps.py — fetches upstream, scans for
find_package(KF6Xxx REQUIRED), compares to recipe deps. Catches
missing + dead dependencies in every kf6-* and qt* recipe.
* classify-cook-failure.py — 17-rule cook-failure classifier.
10-30s diagnosis vs 5-10min manual. exit code is intentionally
inverted (0=novel failure, 1=known fix) for CI signal.
* lint-recipe.py — 7-rule recipe lint: R1-NO-PATCH-FILE,
R1-PATH-SOURCE, R2-INLINE-SED, R2-PATCHES-DIR-UNUSED,
NO-LEGACY-MAKE, R1-LEGACY-APPLY-PATCHES, DEP-NOT-FOUND.
1.1s for 171 recipes (down from 60s+ in v1 via recipe-index
precomputation). Strict mode promotes warnings to errors.
Build-system convenience:
* repair-cook.sh — incremental-build optimizer.
Equivalent to 'repo cook <pkg>' but with a fast-path that
skips configure when CMakeCache.txt is newer than source AND
external patches haven't changed. 30-60s vs 5-10min on KF6
recipes. make repair.<pkg> / make clean-repair.<pkg> targets.
* migrate-kf6-seds-to-patches.sh — migration skeleton for
converting 56 inline 'sed -i' chains across the KF6 recipes
to durable external patches in local/patches/<name>/.
Gitea Actions (host-execution, no Docker):
* .gitea/workflows/build-system.yml — 8-job pipeline:
unit-tests, lint-offline, lint-network (nightly),
lint-recipe (NEW), lint-docs, build-mini, build-full,
smoke (QEMU boot).
* .gitea/RUNNER-SETUP.md — one-time Manjaro/Arch host setup.
Build script hardening:
* build-redbear.sh — when a low-level source (relibc,
kernel, base, bootloader, installer) is newer than its pkgar,
clean build/ and sysroot/ across all recipes too. Low-level
package changes leave autotools packages (pcre2, gettext,
libiconv, ...) with stale configure/libtool scripts referencing
the old runtime, causing 'libtool version mismatch' and
'not a valid libtool object' errors. Cleaning forces
re-configuration; stage/ and source/ are preserved so the
cookbook skips unchanged packages that don't use autotools.
* Makefile — wire lint-cook-failure,
lint-cook-failure-explain, lint-recipe, lint-recipe.%,
lint-recipe.strict, lint-recipe.%.strict, repair.%,
clean-repair.%, test-lint-scripts[-quiet]. Replace the
legacy 'validate-patches' target with a deprecation notice
pointing at validate-sources.
Documentation:
* BUILD-SYSTEM-IMPROVEMENTS.md — mark #2 and #5 DONE; full
implementation notes; updated Make-targets table.
* BUILD-SYSTEM-V6-HARDENING-POSTMORTEM.md (NEW) — 226-line durable
record of the 8-session arc: 32 findings categorized, 5 P0
audit-script bugs fixed, 6 over-broad multi-pattern rules
discovered + fixed, test coverage 86/86 in <1s, 7/10
improvements DONE.
* SCRIPT-BEHAVIOR-MATRIX.md — apply-patches.sh row marked
LEGACY/ARCHIVED; build-redbear.sh row no longer claims to
call it.
* boot-logs/README.md (NEW) — frozen-evidence policy:
'do not edit' rule for REDBEAR-FULL-BOOT-*-RESULTS.md files.
* libdrm/02-redox-dispatch.patch.README (NEW) — 8-step regen
procedure for the broken hunk.
Cleanup:
* local/cache/README.md deleted (1-line placeholder).
* legacy 'make validate-patches' target removed.
Per build-system improvement #5: lint-recipe.py's first run on
the live tree surfaced 1 broken-patch reference (redbear-sessiond),
1 dangling cookbook_apply_patches call (tc), 19 sed -i calls in
sddm (warning — cookbook_apply_patches present, drop-x11.py
migration in progress), 4 sed -i calls in qt6-wayland-smoke
(uncovers the same bug class the libwayland fix prevented).
Improvement #9 from BUILD-SYSTEM-IMPROVEMENTS.md. Scans the tail of
a failed repo cook output and matches it against ~14 known failure
patterns documented in AGENTS.md 'COMPLEX FIX CHECKLIST
(v6.0-impl17)'. Each rule emits a structured fix with the relevant
build flags, paths, and AGENTS.md reference.
Usage:
repo cook kf6-kio 2>&1 | tee /tmp/build.log
classify-cook-failure.py /tmp/build.log
Cuts per-failure diagnosis from 5-10 min of manual pattern-matching to
10-30 seconds. Critical for new contributors. Pure read-only analysis,
no build side effects.
Also opportunistically references the new
audit-patch-idempotency.py from the patch-no-longer-applies rule,
tying the two improvements together.
Two S-sized improvements from BUILD-SYSTEM-IMPROVEMENTS.md:
1. local/scripts/audit-patch-idempotency.py (improvement #3):
Validates that every external patch in local/patches/ is
idempotent (--reverse --check succeeds) and reproducible
(re-clone + re-apply produces an identical tree). Catches the
patch idempotency class of bug at lint time, where it used to
surface as a 2+ hour cookbook failure during a cook. Found a
real bug on first run: local/patches/libdrm/02-redox-dispatch.patch
has a hunk at xf86drm.c:321 that no longer matches the upstream
libdrm-2.4.125.
2. src/cook/script.rs auto-link Qt sysroot dirs (improvement #8):
The cookbook's BUILD_PRESCRIPT now auto-detects if the per-recipe
sysroot has Qt6 (qtbase or qtdeclarative) and creates the canonical
/usr/{plugins,mkspecs,metatypes,modules} symlinks that KF6 recipes
need for cmake to find Qt6Config.cmake's INTERFACE_* paths. New
KF6 recipes that depend on qtbase no longer need to manually
call redbear_qt_link_sysroot_dirs in their build script. Recipes
that need more customization can still call the helper directly
via 'source $COOKBOOK_ROOT/local/scripts/lib/qt-sysroot.sh'.
Previously the script only parsed [package].dependencies, missing
the build-time-only consumers that the cookbook's
get_build_deps_recursive() picks up via [build].{dependencies,
dev_dependencies}. This caused 'rebuild-cascade.sh relibc' to report
'nothing to do' even though the cookbook correctly identifies
uutils, libpciaccess, relibc-tests, and other packages as relibc
build-dep consumers and rebuilds them under 'make live'.
The fix is to also walk the [build] section. The cookbook's own
parser uses the same convention.
The per-recipe sysroot and stage cache used mtime of the dep pkgar
files to detect when a rebuild was needed. Any mtime bump on relibc
or any leaf dep (including the pre-cook relibc in build-redbear.sh)
would cascade-rebuild every downstream per-recipe sysroot even when
the dep's content was bit-identical. The resulting transient sysroot
extractions produced 'C compiler cannot create executables' and
'configure error' failures that retried fine standalone.
Replace the mtime checks with a blake3 content-hash fingerprint of
the dep pkgar set:
- For the per-recipe sysroot: store the fingerprint in
<target>/sysroot/.tags/deps-fingerprint and rebuild only when the
computed fingerprint does not match.
- For the per-recipe stage: store two fingerprints at
<target>/.deps-fingerprint and <target>/.host-deps-fingerprint.
Rebuild stage only when (source changed) OR (deps content changed)
OR (host-deps content changed) OR (auto_deps.toml missing).
This eliminates the transient build failures in 'make live' / 'build-redbear.sh'
and aligns the cache invalidation signal with the actual content the
recipe depends on, not the arbitrary mtime of the dependency package.
TUI rewrite (the visible half of the change):
- 8 views instead of 6: Home, Search, Info, Install, Build,
Query, Remove, Updates — central VIEW_ORDER const in
tui/views/mod.rs and cycle_view() / parent_view() helpers
mean adding a 9th view is one line.
- Help overlay is now view-scoped: shows a Global section and
an In this view section that only lists keybindings the
current view actually honors.
- Esc goes back one view (parent_view map); q still quits.
- New ConfirmAction enum and full-screen confirm_banner
widget: pressing i / b / r / U now sets pending_confirm
and shows a banner; y confirms, n cancels.
- TUI build routed through the canonical fetch_aur_to_store
+ validate_git_target instead of an inline git clone. The
6 swallowed errors are now surfaced in build_log and
status_message.
- panic-catching wrapper around spawn_action so a panic in
the worker thread becomes a visible ActionUpdate instead
of a frozen spinner.
- TUI cancel architecture: Arc<Mutex<Option<Child>>> shared
between parent and worker drainer, Arc<AtomicBool> cancel
flag, c keypress in Install/Build views calls child.kill().
- Terminal too-small guard: < 14 rows or < 40 cols renders
a Terminal too small overlay instead of broken layout.
- Sticky error coloring in the status bar: success uses
theme.success, failure uses theme.error, neutral uses
status_style.
- PgUp / PgDn global scroll bindings for Install/Build log
views; scroll position tracked in install_log_scroll /
build_log_scroll u16 fields; install_log_joined /
build_log_joined pre-joined strings avoid repeated
Vec<String>::join calls during render.
- CubApp::new() returns Result<Self, CubError>; HOME missing
or store.init() failure is now a fatal error overlay
instead of a silent /tmp/.cub fallback.
AUR hardening:
- AUR client (reqwest::blocking::Client) gets 5s connect +
15s request timeouts.
- fetch_aur_to_store writes the recipe atomically: stage in
store.tmp_dir()/recipe-staging-<name>-<nanos>, then
fs::rename. TmpGuard drop cleans up the clone directory.
- validate_git_target rejects names with .., ://, leading -,
empty, or NUL bytes (was previously only catching leading -).
- redox-pkg dependency pinned to rev
52f7930f8e6dfbe85efd115b3848ea802e1a56f0 to match the
resolved Cargo.lock.
God-module split (main.rs 2063 -> 1723 lines):
- constants.rs: 10 path / URL constants.
- bur_helpers.rs: search_cached_bur, ensure_bur_package_dir,
sync_bur_repo, default_bur_repo_url, bur_repo_dir,
aur_repo_url, BurMatch struct.
- fs_helpers.rs: find_stage_dir, directory_has_entries,
copy_dir_recursive, remove_dir_if_exists,
current_unix_timestamp, join_strings, join_package_names,
empty_if_blank, yes_no.
- paths.rs: cub_temp_dir, validate_git_target.
Tests:
- 19 unit tests in main-side modules (was 5); 121 in the
lib (unchanged). Total 140/140 pass.
- New CubError variant tests, validate_git_target
happy-path + 4 attack vectors, cub_temp_dir unique-name
under concurrent calls, bur_repo_dir / aur_repo_url
composition, fs_helpers round-trips with tempfile
scratch dirs.
Policy:
- local/AGENTS.md gains a TUI CONVENTION section: single
binary, -i flag, no separate -tui crate, ratatui 0.30
+ termion 4.0.6, anti-pattern list. cub, redbear-info,
and redbear-netctl-console are listed as already
compliant.
- cubl (the cub lib-only consumer) recipe path updated
from -p cub-cli to -p cub.
Verified: cargo build --workspace and --no-default-features
and --features full and --features tui all clean; cargo
test --workspace 140/140 pass; cub --version cub 0.2.3;
cub --help 21 subcommands.
There is one cub, not three. The CLI, TUI, and library used to be
three separate workspace crates with awkward paths and three
independent installable artifacts. After the rewrite:
- single Cargo workspace at local/recipes/system/cub/source/cub/
with [lib] name = "cub" and [[bin]] name = "cub" in one
Cargo.toml
- 21 source files moved into cub/src/ (lib modules + main.rs +
tui/{app,mod,theme,widgets,views/{mod,build,home,info,install,
query,search}}.rs)
- 13 dead crate Cargo.toml / Cargo.lock / old lib.rs files removed
- cub-assessment + cubl + cub system recipe point at the new
package name
- workspace manifest collapsed to { members = ["cub"], version =
"0.2.3" } to match the active Red Bear OS branch
The TUI surface is preserved (ratatui 0.30 + termion 4.0.6,
single binary, -i flag) and the public lib API is unchanged
(cub::aur, cub::pkgbuild, cub::version, etc. all re-exported from
the new lib.rs).
Verified: cargo build --workspace passes with all four feature
combos (default, no-default-features, --features full, --features
tui); cub-assessment compiles against cub v0.2.3; cub --version
prints cub 0.2.3; cub --help lists 21 subcommands.
The v6.0 service file used type={scheme='input/evdev'} which caused
init to register the scheme under the wrong name. Restored the
original type={scheme='input'} so init correctly registers the
:input scheme and all downstream services (vesad, fbcond, fbbootlogd)
can find it.
rtcd: syscall::openat(0, '/scheme/sys/update_time_offset') used fd 0
(stdin) instead of the namespace fd, causing EACCES. Fixed by using
libredox::call::getns().
acpid: AmlPhysMemHandler captured pci_fd at construction as
Arc<Option<Fd>>, so after set_pci_fd() stored the fd, the handler
never saw it. Changed to Arc<RwLock<Option<Fd>>> shared between
AcpiContext and the handler.
Base fork commits: 770694d0 (rtcd), 84573feb (acpid)
A focused batch of small, real improvements from prior sessions. Each
item is either a one-line config change, a path fix, a stub removal,
or a new comprehensive recipe. No stubs added, no workarounds, no
fake fixes.
* local/recipes/libs/libdrm/recipe.toml: enable Intel GPU support
(-Dintel=enabled). Mesa's iris/crocus Gallium drivers need the
Intel backend compiled in. The AMD backend is already enabled.
* local/recipes/libs/libxkbcommon/recipe.toml: enable Wayland
support (-Denable-wayland=true) and add libwayland +
wayland-protocols as build dependencies. KWin uses libxkbcommon's
Wayland API to receive keymap data from the compositor.
Previously the recipe had Wayland disabled, blocking KWin.
* local/recipes/kde/kf6-kded6/recipe.toml: replace a wrapper-script
hack (which renamed kded6 to kded6.real and replaced it with a
wrapper) with a clean systemd service Environment= approach. The
wrapper script is removed (kf6-kded6/source/kded6-wrapper.sh
deleted). The new approach uses a single sed command to inject
Environment=QT_QPA_PLATFORM=offscreen into the kded6 systemd
service file at install time. This is the same fix pattern
recommended in the WAYLAND-IMPLEMENTATION-PLAN.md.
* local/patches/libdrm/02-ioctl-response-sizes.patch: fix the patch
header paths. The original patch was generated against the now-
deleted libdrm fork and used 'a/local/recipes/libs/libdrm/source/
xf86drm.c' style paths. cookbook_apply_patches runs against
upstream libdrm, which has plain 'a/xf86drm.c' paths. Without
this fix, git apply would warn about path mismatch. The hunk
contents are unchanged.
* recipes/libs/libpciaccess/recipe.toml: new comprehensive recipe
for libpciaccess 0.19. Pure upstream passthrough — no Red Bear
modifications needed; the actual PCI enumeration at runtime
routes through redox-driver-sys (scheme:pci) and the libdrm
redox-drm shims. Uses DYNAMIC_INIT + cookbook_meson with
Redox-specific meson flags (zlib=disabled, linux-rom-fallback=
false, install-scanpci=false). Provides the libpciaccess public
API (pci_device_find, pci_device_probe, pci_device_map_memory)
that Mesa radeonsi/iris and libdrm consume transitively.
* recipes/libs/pciaccess-stub: removed. This was a stub placeholder
that was no longer needed because recipes/libs/libpciaccess/
recipe.toml is the real implementation. Per the project's
ZERO TOLERANCE FOR STUBS policy (local/AGENTS.md), stubs must
be removed when real implementations exist.