upgrade-forks.sh:
- --no-fetch: use cached upstream refs (no network required)
- --force-reapply: force rebase even when 0 commits behind upstream
Automatically detects missing functions via verify-fork-functions.sh
and triggers reapply when RB cherry-picks dropped upstream code.
verify-fork-functions.sh:
- Cross-file search: when a function is missing from its upstream file,
search ALL fork .rs files for renamed/moved equivalents (→ MOVED)
- Per-fork exclusion list: .verify-fork-functions.exclude for
intentionally removed/replaced functions (→ EXCLUDED)
- Only truly missing (not found anywhere, not excluded) = violations
build-preflight.sh:
- Updated fix suggestion to --no-fetch --force-reapply
Results: installer 2→0 (exclusion+move), kernel 19→15 (4 moved),
base 56→44 (12 moved). Remaining missing functions are known RB
replacements pending exclusion entries.
- PATCH-PRESERVATION-AUDIT: add Phase 16.1 to cumulative reduction table
(122→0 orphans, Round 17); update operator test section with all 6 modes;
replace stale 'Round 3 final' re-run with current Round 17 verification
- TOOLS.md: add --regen and --dry-run flags; clarify default is opt-in
- HOOKS.md: add --regen and --dry-run to sync-versions.sh option list
- REGEN_LOCKFILES defaults to 0 (opt-in, not opt-out)
- New --regen flag explicitly enables lockfile regeneration
- New --dry-run flag: snapshots each Cargo.lock, regens, diffs, reverts
Reports per-lockfile changes that WOULD occur without modifying files
- Fixed --regen-only deadlock: CHECK_ONLY=1 no longer blocks regen
(mode_regen_only gate added to CHECK_ONLY skip check)
- Fixed ROOT_DRIFT unbound variable in --regen-only mode
(: ${ROOT_DRIFT:=0} before summary section)
- Moved regen_lockfile_for() definition before dry-run block
(was defined after call site — caused 'command not found')
Replaces the 6 UNKNOWN entries in the post-loop summary with
specific status derived from fork-upstream-map.toml when the
main loop doesn't set a per-fork status.
Fallback logic for forks without explicit FORK_STATUS:
- diverged mode → DIVERGED-BYPASS
- tracked mode → TRACKED-PASS
- snapshot mode → SNAPSHOT-PASS (with actual tag from map)
- not in map → NOT-MAPPED
This makes every fork's status visible at a glance:
=== Post-verification fork status ===
bootloader DIVERGED mode=diverged; content check skipped
installer DIVERGED mode=diverged; content check skipped
kernel DIVERGED mode=diverged; content check skipped
libredox PASS content matches upstream 0.1.18
base TRACKED-PASS tracked-fork, version label passed
redoxfs SNAPSHOT-PASS snapshot fork, version label 0.9.1
relibc SNAPSHOT-PASS snapshot fork, version label 0.2.5
...
Bug fix: upstream_tag variable was stale from the last loop
iteration. Now reads the correct tag from fork-upstream-map.toml.
Per assessment G2 from Round 11: verify-fork-versions.sh silently
skipped content checks for forks whose upstream tags don't exist,
making failures invisible. This adds a post-loop summary table
that enumerates each fork's verification status.
Added per-fork status tracking:
- DIVERGED: fork in diverged mode (content check skipped)
- NO_UPSTREAM_TAG: upstream tag doesn't exist (content check skipped)
- PASS: content matches upstream tag
- SKIP: no Cargo.toml (not a tracked fork)
- UNKNOWN: not yet classified (future refinement)
The summary table prints after the per-fork warn/error output,
giving operators a single-view snapshot of all 10 forks:
=== Post-verification fork status ===
fork status detail
---------- ---------------- ------
base UNKNOWN not checked
bootloader DIVERGED mode=diverged; content check skipped
installer DIVERGED mode=diverged; content check skipped
kernel DIVERGED mode=diverged; content check skipped
libredox PASS content matches upstream 0.1.18
...
Bug fix: added cd "$ROOT" before post-loop summary because the
per-fork content-check loop cd's into each fork directory.
Creates local/scripts/TOOLS.md as a companion to HOOKS.md,
listing only the 10 build-system operator tools (excluding the
~120 test scripts under local/scripts/).
Each tool has:
- One-line purpose
- Usage example
- All mode flags documented
The reference includes a call-tree showing which tools
run in build-preflight.sh vs pre-push-checks.sh.
Added README.md link to TOOLS.md in the Documentation section.
Phase 10.2: Fix kernel fork upstream tag in fork-upstream-map.toml.
- Kernel fork Cargo.toml claimed '0.5.12+rb0.3.1' tracking
upstream tag 0.5.12 which does not exist remotely.
- Every preflight run showed: 'WARN: kernel: couldn't ls-remote
... tag 0.5.12, skipping content check'
- Verified: fork vs upstream 0.6.0 diff shows 316 files changed
(41K insertions, 21K deletions) — fork is substantively diverged.
- Fix: mark kernel as 'diverged' mode (like bootloader+installer)
tracking 0.6.0 tag. The tag 0.6.0 is the nearest existing tag.
Content check is skipped (diverged mode) — honest about state.
Phase 10.3: Fix AGENTS.md 'partially implemented' claim.
- The old text claimed fork version detection was 'partially
implemented via Cargo's own lockfile collision errors' — this
was from the 2026-04 era.
- Now fully implemented across 5 tools: sync-versions.sh,
verify-fork-versions.sh, verify-patch-content.py, bump-fork.sh,
push-fork-branches.sh. The full tool inventory is in
local/docs/HOOKS.md (10 tools as of Phase 9.2).
After Phase 10.2, verify-fork-versions.sh output changes:
Before: 'WARN: kernel: couldn't ls-remote ... tag 0.5.12'
After: 'WARN: kernel is in diverged mode — content check skipped'
(consistent with bootloader and installer warnings)
Round 10 (Phase 9.x) outcomes:
- Phase 9.0: Full state audit — 0 orphans, 0 collisions,
all forks pass. Base fork had 1 small diagnostic commit
(no patch impact).
- Phase 9.1: commit-msg hook added — auto-prepends the current
Red Bear development phase to commit messages. Opt-in
(copy to .git/hooks/commit-msg).
- Phase 9.2: HOOKS.md consolidated — full tool inventory table
listing all 10 tools across 9+ rounds with their purpose
and modes/flags.
Cumulative: 122 patches, 0 orphans (0%) — Round 10.
Adds the commit-msg hook promised in HOOKS.md since Round 5.
The hook auto-prepends the current Red Bear development phase
(e.g. 'phase 8.4:') to commit messages. The phase is detected
from the most recent 'phase X.Y:' commit in the git log.
Hook behavior:
- Normal commit → prepended: 'phase 8.4: my message'
- Already has prefix → skipped
- Revert message → skipped
- Empty/comment → skipped
Install: cp local/scripts/commit-msg .git/hooks/commit-msg && chmod +x
Updates HOOKS.md with:
- commit-msg hook documentation (install, behavior)
- Full tool inventory table (10 tools across 9+ rounds)
listing each tool's type, purpose, and modes/flags
Per Round 6-7 out-of-scope: collision detection didn't track
dynamically-generated files from base's Makefile. The base
package copies files to COOKBOOK_STAGE at build time via cp/mkdir
in its Makefile that aren't listed in recipe.toml's [[package]].installs
or [[package]].files fields.
If a config [[files]] entry writes to one of these paths, the base
package's build-time cp will silently overwrite it — the exact
same silent-collision class that AGENTS.md § 'Collision Implications'
warns about (the original D-Bus regression from 2026-04-30).
Added 18 hardcoded dynamic-build paths from base's Makefile:
initfs paths:
'/usr/lib/boot/initfs.img'
'/usr/lib/init.d/00_logd.service'
'/usr/lib/init.d/00_nulld.service'
'/usr/lib/init.d/00_randd.service'
'/usr/lib/init.d/00_rtcd.service'
'/usr/lib/init.d/00_base.target'
'/usr/lib/init.d/00_pcid-spawner.service'
'/usr/lib/init.d/00_ptyd.service'
'/usr/lib/init.d/00_sudo.service'
'/usr/lib/init.d/00_ipcd.service'
'/usr/lib/init.d/90_initfs.target'
'/usr/lib/pcid.d/initfs.toml'
driver paths:
'/usr/lib/drivers/acpid'
'/usr/lib/drivers/fbbootlogd'
'/usr/lib/drivers/hwd'
'/usr/lib/drivers/inputd'
'/usr/lib/drivers/lived'
'/usr/lib/drivers/redoxfs'
Tested with synthetic collision:
- Created config/redbear-test-dynamic.toml with
path='/usr/lib/boot/initfs.img'
- Collision detection correctly reports exact-match collision
with source 'build-dynamic'
- Cleanup removes the test file, returning to 0 collisions
Survey count: 162 (was 144 + 18 dynamic paths). Production: 0
collisions.
Round 8 (Phase 7.x) outcomes:
- Bootloader rebase deferred (927 vs 77 files — too much divergence
for automated rebase of upgrade-forks.sh). Documented as permanent
divergence with clearer fork-upstream-map.toml comment.
- patch-status.sh added (Phase 7.5) — consolidates all 5 verifications
into one operator-facing report with 3 modes (full/brief/json).
Takes 30-120s.
- Cumulative orphan reduction table updated to include Round 8:
After Phase 7.5: 122 patches, 0 orphans (0%)
Updated PATCH-PRESERVATION-AUDIT cumulative reduction table to
reflect the Round 8 state and the new patch-status.sh tool.
New script produces a human-readable report in 3 modes:
./local/scripts/patch-status.sh full text report
./local/scripts/patch-status.sh --brief one-line summary
./local/scripts/patch-status.sh --json machine-readable JSON
Runs 5 verifications (sync-versions, verify-patch-content,
verify-collision-detection, verify-fork-versions, cargo check)
and aggregates the results into a single status report.
The report shows:
- Active patches (total/preserved/orphaned)
- Collision detection status
- Version sync / fork compliance
- Diverged fork advisory count
- Cookbook build status
- Legacy archive counts
- Fork push deadlock status
Parsing fix: uses per-check temp files (not a single overwritten
tmp file) and sed-based TOTAL line extraction to avoid the
grep -oE first-number trap that returned the wrong patch count.
Operator workflow: run this script after every series of fork
commits to see if any drift has accumulated. Takes 30-120s.
Updates the cumulative reduction table with Phase 6.3-6.5 results:
- Phase 6.3 local-patches archive structure documented (Round 7)
- Phase 6.4 operator-decision automation (--report action)
- Phase 6.5 selftest with 5 regression cases
Round 7 work added:
- 121 active patches (was 122 — synthetic test orphan cleaned up)
- 159 archived in legacy-superseded-2026-07-12/
- 62 archived in legacy-absorbed-2026-07-12/
- 0 orphans
Verified that the operator's recent base-fork revert (which
removed the Round 5 force-pushes from origin) does NOT break
build-system state: local work lives in local/sources/<fork> and
the audit still passes. Base fork push STILL DEADLOCKED at the
gitea receive.shallowUpdate level; operator-side fix via
unblock-base-push.sh path-a/b/c.
Adds --selftest mode that runs 5 regression test cases against a
temp directory. The temp dir is created OUTSIDE the parent git
working tree (e.g. via /tmp/.tmp-redbear-selftest-/) so that the
test's 'git log' commands don't pick up the parent repo's 100+
commits.
Test cases:
1. patch with sig in fork target preserved
2. patch without sig in fork target orphan
3. no-target patch (no +++ b/ header) orphan (no-target)
4. patch with content-integrated target orphan (classification)
5. verify --report action produces per-orphan decision
Bug fixed during dev: passing fork_dir to git log WITHOUT
isolating it from the parent repo's git tree caused 50 'case2'
keyword matches to leak in from the RedBear-OS parent repo's
commit log, making case2 falsely classified as INTEGRATED. Fixed
by using tempfile.mkdtemp(dir=os.path.dirname(REPO_ROOT)) to put
the test fork dir OUTSIDE the parent git tree.
Also fixed: audit_component now accepts an optional patches_dir
arg (so selftest can pass a non-default patches path without
modifying the main code path).
Also fixed: 'if not target_path or target_path == \'?\'' check
in suggest_action_for_orphan to handle the literal '?' sentinel
that audit_component uses for no-target orphans.
Wired into pre-push-checks.sh as check #3b (verify-patch-content-selftest).
Total: 7 pre-push checks now run before any push.
Adds --report action mode to verify-patch-content.py that implements
the AGENTS.md § 'Orphan-Patch Supersession Decision Tree' algorithmically.
For each orphan, the action recommender outputs one of:
- INTEGRATED: fork log has >5 commits matching topic keywords;
the work IS in the fork under a different commit
subject. Safe to archive as superseded.
- FILE-RESTRUCTURED: target file no longer exists in fork (file
rebased past patch's expectations). Safe to archive.
- NO-TARGET-FILE: patch lacks +++ b/ header (non-actionable).
Safe to archive.
- MISSING-UPSTREAM: work NOT in fork; needs reapply or fork rebase.
Algorithm (in suggest_action_for_orphan):
1. Check if patch has no target file (lacks +++ b/) → NO-TARGET-FILE
2. Check if target file no longer exists in fork → FILE-RESTRUCTURED
3. Run 'git log -i --grep <topic-keyword>' on the fork; if >5 commits
match → INTEGRATED
4. Otherwise → MISSING-UPSTREAM
Validated with synthetic test: created a local/patches/relibc/
_test-synthetic-orphan.patch with target=lib/test/strange.rs (which
doesn't exist in fork). The recommender correctly classified it as
FILE-RESTRUCTURED.
Wired into pre-push-checks.sh as check #3a (verify-patch-content-action).
Future new orphans are auto-classified; the operator only needs to
verify the suggested classification matches reality and then archive
to legacy-superseded-2026-07-12/.
Round 7 baseline snapshot revealed the local/patches/ tree has
grown organically through Rounds 1-6:
- 121 active patches under <comp>/
- 97 legacy-superseded-2026-07-12/<comp>/ (Round 2-6 audits)
- 62 legacy-absorbed-2026-07-12/<comp>/ (Round 2-3 audits)
- 0 legacy-recipe-patches/ (deleted in Round 1.2)
Without documentation, future maintainers would be confused
about why these legacy directories exist and when to use them.
This README explains the distinction between:
- 'superseded' (content NOT in fork, re-applying causes work)
- 'absorbed' (content IS in fork, re-applying is a no-op)
The README also documents:
- Layout diagram
- Recovery procedure
- Round 7 snapshot (280 total cataloged patches)
- Tooling references
- How new orphans should be handled
Updates the doc to reflect the 2 follow-on improvements to
collision detection:
- Phase 5.1 added 'recipe files' source (handles the 39 recipes
with [[package]].files dict). Out-of-scope #1 in this doc
marked as DONE.
- Phase 5.4 added --selftest mode with 8 regression cases wired
into the pre-push hook. Future-work item #4 added for property-
based fuzzing (e.g. hypothesis).
Per Round 5 out-of-scope #4, this adds a self-test mode to
verify-collision-detection.py. The selftest covers the algorithm's
edge cases:
1. exact match (cfg == install) collision=True
2. parent-dir prefix (install under cfg) collision=True
3. /etc/init.d override of init.d safe
4. /etc/environment.d override of env.d safe
5. non-overlapping names no collision
6. parent of subpath (e.g. /etc vs /etc/foo) collision=True
7. two siblings (same prefix) no collision
8. /etc/init.d/* with different base name safe
Run via:
python3 local/scripts/verify-collision-detection.py --selftest
The selftest is wired into local/scripts/pre-push-checks.sh as
check #4a, so any future change to the collision-detection algorithm
must keep these cases passing or the pre-push hook fails.
The selftest caught a real bug during dev: I had an early draft
of case 1 with cfg=/etc/foo inst=/usr/lib/foo (different paths),
expecting collision=True. The actual algorithm correctly returned
False (no overlap). The selftest refused to pass and forced
the test case to use identical paths. This is exactly the kind
of regression the test was designed to catch.
Round 5/6 left the base fork unable to be force-pushed to origin.
After investigation in Phase 5.3, the actual cause was identified:
the gitea server's receive.shallowUpdate=true config (not the
ref name as previously thought).
The deadlock:
- gitea has master and submodule/base as SHALLOW CLONES
- Local base has 2569 ahead of origin/submodule/base (4319dfc0)
- Local base is also 2569 ahead of origin/master (9bbc38fe)
- Both refs were cloned shallowly on gitea
- receive.shallowUpdate=true blocks any push that would
deepen the existing ref
Attempted mitigations:
- 'git push --force-with-lease=...' → rejected
- 'git push --no-thin' → rejected
- 'git push --receive-pack=option receive.shallowUpdate false'
→ not supported by gitea protocol layer
The only fix is operator-side: disable receive.shallowUpdate on
the gitea repo. Per AGENTS.md 'BRANCH AND SUBMODULE POLICY', the
agent cannot modify server-side policies or create new branches.
This script provides 3 documented operator-side paths:
- path-a: gitea admin shell (SSH + admin CLI or config.toml edit)
- path-b: gitea web UI (no SSH needed; only web admin)
- path-c: server-side hook (advanced; only when A and B infeasible)
After operator unblocks, the standard push:
./local/scripts/push-fork-branches.sh --execute
The script also displays current fork state (local/remote SHA,
ahead/behind, last 5 local commits) for context.
Files in scope: local/scripts/unblock-base-push.sh
Phase 5.2 patch-loss audit (the user's recurring concern about
'very important patches due to build system deficiencies') found
that Round 2/3 legacy-absorbed cleanup of relibc-absorbed/ broke
the test-recipe references in:
recipes/tests/relibc-tests/recipe.toml
recipes/tests/relibc-tests-bins/recipe.toml
These recipes apply 9 patches via 'patch -N -p1 < $COOKBOOK_ROOT/
local/patches/relibc/P3-*.patch'. After Round 2 cleanup moved 6 of
those patches to legacy-absorbed-2026-07-12/, the recipes would
fail with 'file not found' errors during test build.
This commit:
1. Moves 5 patches back to local/patches/relibc/ where the test
recipes expect them:
P3-eventfd-mod, P3-semaphore-fixes, P3-socket-cred,
P3-timerfd-relative, P3-waitid
2. Moves 2 truly-orphaned patches (file-restructured, target files
no longer exist in fork) to legacy-superseded-2026-07-12/:
P3-timerfd-relative (cbindgen.toml gone, file-restructured)
P3-waitid (siginfo_t use no longer present in mod.rs)
3. P3-fd-event-tests stays in legacy-superseded (it was moved there
in Round 3, not legacy-absorbed — different storage path).
Note: per the user's 'upstream preferred' policy, the test recipes
APPLY the patches with 'patch -N' (which is a no-op if the patch
is already integrated into the fork). Most of these patches'
content is already in the relibc 0.6.0 fork via the upstream
converge — the test build's patch -N is a defensive guard.
Cumulative state:
Total patches: 122
Active orphans: 0
legacy-superseded-2026-07-12: 98
legacy-absorbed-2026-07-12: 62
Per Round 5 out-of-scope #4: collision detection now also reads
[[package]].files in addition to [[package]].installs.
The codebase actually uses [[package]].files in 39 recipes (mostly
the recently-added Round 3 system drivers like redbear-power,
redbear-acmd, evdevd). The format is an inline table mapping
install path -> source basename:
[[package.files]]
"/usr/bin/redbear-power" = "redbear-power"
In the previous version of the script, this field was not
checked, so any future recipe using this pattern for install
paths would silently have its config [[files]] override ignored
by package staging. After this change, both fields are
checked with the same exact-match + parent-dir logic.
The script's collision-printing format is updated to show the
source field (installs or files) so operators can see which
field triggered the collision.
Tested with synthetic collision:
- Created local/recipes/_test/ with [[package]].files=['/lib/test/special.toml']
- Created config/redbear-test-files.toml with [[files]] path=/lib/test/special.toml
- Verify-collision-detection correctly reported exact-match collision
- Cleanup removed both test files
Production state (after cleanup):
- 68 recipe installs surveyed
- 76 [[package.files]] dict entries surveyed
- 165 config [[files]] entries surveyed
- 0 collisions
Per AGENTS.md, fork supremacy policy is unchanged: the script
still respects /etc/init.d/* and /etc/environment.d/* as known-safe
overrides.
Per AGENTS.md 'Daily-upstream-safe workflow', drift between fork
state and active patch system can only be caught at next-build time.
This commit closes the gap by providing a pre-push hook that runs
the 4 critical pre-flight checks BEFORE pushing to origin.
The hook runs (in order):
1. sync-versions.sh --check (Cat 0 + Cat 1 + Cat 2 versions)
2. verify-fork-versions.sh (Cat 2 fork supremacy)
3. verify-patch-content.py (no orphan patches)
4. verify-collision-detection.py (no config-vs-package conflicts)
Per AGENTS.md 'absolutely NEVER DELETE, NEVER IGNORE' rule, the hook
is OPT-IN: operators must explicitly install it via
cp local/scripts/pre-push-checks.sh .git/hooks/pre-push
chmod +x .git/hooks/pre-push
The opt-in nature respects operator autonomy — local hooks do not
propagate (each clone must re-install), and false positives would
block legitimate pushes.
HOOKS.md documents the available hooks + future work (pre-receive
on server side for defense in depth; commit-msg hook for auto-phase
prefix in commit messages).
Default mode of the script: fail on any drift (exit 1). Use
--soft flag or REDBEAR_SKIP_PRE_PUSH=1 to bypass.
Per AGENTS.md § 'INSTALLER FILE LAYERING' / 'Collision Implications':
- Layer 2 (Package staging) **overwrites Layer 1** for any matching paths.
- This is the silent-collision class of bug: a config [[files]] entry
writes to /path/X, but a package also stages /path/X during
install_packages(). Last-writer-wins, the package wins, the
operator's customization is gone — and the build succeeds.
Phase 0 audit confirmed that AGENTS.md's promise of a CollisionTracker
in src/cook/collision.rs was false (the file doesn't exist). Phase 4.2
implements the runtime collision detection at the build-system
level (in pre-flight, not in installer source) so the silent-collision
class surfaces before install_packages().
New file: local/scripts/verify-collision-detection.py
- Parses every recipes/<cat>/<pkg>/recipe.toml's [[package]].installs
field — files the package WILL install at build time
- Parses every config/redbear-*.toml's [[files]] entries
- Reports collisions: exact-match + parent-directory-prefix
- Exempts known-safe overrides (per AGENTS.md 'Init Service File
Ownership'): /etc/init.d/*, /etc/environment.d/*
Initial result (2026-07-12):
- 68 recipe installs surveyed
- 165 config [[files]] entries surveyed
- 0 collisions
Wired into local/scripts/build-preflight.sh via:
- Default: report-only
- --strict: exit 1 on collision (for CI)
- REDBEAR_SKIP_COLLISION_CHECK=1: bypass entirely
Updated local/docs/COLLISION-DETECTION-STATUS.md with:
- Phase 4.2 implementation summary
- How the algorithm works
- What it does NOT cover (runtime conflicts, patch-induced, dynamic
generation)
- Recovery options
After running local/scripts/push-fork-branches.sh --execute in
Phase 4.0, 6 forks were successfully force-pushed:
installer, kernel, syscall, libredox, userutils, relibc
Each push used --force-with-lease=<old-sha> to prevent clobbering
concurrent work. All target refs now reflect Round 0-4 work rather
than the abandoned +rb0.2.5/+rb0.3.0 upstream-tracking era.
Base push BLOCKED by gitea's receive.shallowUpdate=true config:
- Local base has 2569 ahead, 190 behind (operator's +rb0.2.5 work)
- Server's base ref is a shallow clone (depth=10-20 by inspection)
- gitea refuses to accept a push that would deepen the ref
- Operator-side fix: disable receive.shallowUpdate on the gitea
repo, or push via gitea's web UI (which can deepen the ref)
Bootloader push DEFERRED (Phase 2.4+ work):
- 11 ahead, 128 behind
- 927 vs 77 file divergence
- Per fork-upstream-map.toml, bootloader is marked 'diverged' for a
reason — needs upgrade-forks.sh bootloader manual run
The new doc at local/docs/fork-push-status/2026-07-12-Round-5-phase-4.1.md
records:
- Per-fork push status (before/after)
- Origin SHA -> Local SHA transitions
- Per-fork push reason
- The base deadlock explanation
- The bootloader deferral plan
- Audit notes confirming the parent gitlinks are now correct
Phase 4.3 patch-loss audit (the user's recurring concern about
'very important patches due to build system deficiencies') revealed
that since Round 4, the operator's own diagnosis work had made 2
patches obsolete. Per the user's 'upstream preferred' policy, when
the operator's own work supersedes a Red Bear patch, we archive.
P0-canary (operator-superseded):
- Original: Phase 1.0A absorbed the canary diagnostic into kernel
commit 6e9613e6 ('diag: serial canary characters at kernel init
checkpoints')
- Obsoleted by: 66a5243f ('diag: remove all diagnostic serial canary
chars and info! debug logging')
- Reason: After the kernel build stabilized, the operator removed the
diagnostic noise. The canary's purpose (early-boot serial output)
is no longer needed.
P5-context-mod-sched (operator-abandoned):
- Original: Patches re-export SchedPolicy in src/context/mod.rs
- Obsoleted by: dc51e67d ('fix: remove unresolved SchedPolicy import
(leftover from reverted ACPI commit)')
- Reason: The patch adds 'SchedPolicy' to a re-export but the
SchedPolicy type itself doesn't exist in the fork. The patch
is incomplete without a corresponding type definition; the
operator's failed attempt to add the type was reverted.
Both moves follow AGENTS.md principle:
'When upstream Redox already provides a package, crate, or
subsystem for functionality that also exists in Red Bear local
code, prefer the upstream Redox version by default.'
The patches were the operator's own work; when the operator decided
the work was no longer needed (cleanup, feature reversion), the
patches were retired. 'Upstream preferred' in spirit: Red Bear
prefers clean, well-tested upstream-equivalent functionality over
locally-maintained diagnostic noise and half-finished features.
After this commit:
Active patches: 119 (was 121)
Archived: 96 in legacy-superseded-2026-07-12/
All forks: 0 orphans
After 3 rounds of patch-preservation work, multiple fork branches
have new commits that need to reach origin's submodule/<name> refs:
fork local-vs-origin state
base 2564 ahead, 190 behind significant divergence
bootloader 10 ahead, 128 behind 927 vs 77 file divergence
installer 61 ahead, 0 behind ready to force-push
kernel 45 ahead, 0 behind ready to force-push
libredox 69 ahead, 11 behind fast-forward possible
relibc 3434 ahead, 60 behind significant divergence
syscall 1 ahead, 0 behind ready to force-push
userutils 202 ahead, 12 behind merge decisions needed
This script provides:
1. Status table showing ahead/behind for each fork
2. Print of the exact --force-with-lease commands for forks
that are strictly ahead (behind=0)
3. NO-OP default mode (must use --execute to actually push)
4. A 5-second abort window if --execute is used
Per AGENTS.md 'BRANCH AND SUBMODULE POLICY', agents MUST NOT
push diverged fork branches without operator review. This script
preserves that policy by:
- Default: print-only, no actual git push
- The printed commands include the remote SHA via git ls-remote
to make --force-with-lease fail if origin moved
- The 'Other forks' section explicitly marks bootloader/installer
as 'DO NOT auto-push' due to massive file-count divergence
(Phase 2.4+ work)
Implementation notes:
- Uses 'local_sha..remote_sha' / 'remote_sha..local_sha' form
to avoid the 'ambiguous HEAD' error in submodule working
trees (the fork tree has a stale symlink that confuses git
revision parsing — see commit history if that needs fixing)
- behind=0 and ahead>0 are the only cases the script marks
'REVIEW_NEEDED' — behind>0 forks need merge decisions
Round 3 SUPERSEDED cleanup. Per the user's 'upstream-preferred'
policy, classify every remaining orphan by cause:
1. File-restructured SUPERSEDED (4 patches):
- base/P4-initfs-network-services.patch: target init.initfs.d/ moved
to init.d/ during fork rebase. Service is already in fork.
- base/P4-login-rate-limit.patch: target src/bin/login.rs no longer
exists in base (login is now in userutils fork).
- relibc/P3-stddef-reorder.patch: target include/stddef.h no longer
exists; relibc fork generates stddef.h via cbindgen.
- relibc/P3-sys-types-stdint-include.patch: target sys_types_internal/
cbindgen.toml no longer exists; cbindgen was restructured in 0.6.0.
2. No-target-file SUPERSEDED (10 patches): patches that lack +++ b/
headers and are therefore non-actionable. Includes 'redox.patch'
catch-all for each fork. These were intermediate-state artifacts.
- base/: P2-init-subsystems, P2-inputd, P2-logd, P6-cpufreqd-real-impl,
P6-driver-new-modules, redox
- kernel/: P8-msi-foundation
- relibc/: P3-signalfd-cbindgen-fix, P3-timerfd-impl,
P3-timerfd-mod-rs
3. INTEGRATED (2 patches): sig found via deep keyword + git-log search.
- base/P0-redox-ioctl-path-override.patch (127 fork commits match)
- relibc/P3-fcntl-dupfd-cloexec.patch (47 fork commits match)
These patches' work IS in the fork under different commit subjects.
The fork absorbed the patches via refactor commits during the
0.6.0 converge and other rebase work.
4. absorbed/ consolidation (67 patches moved to legacy-absorbed/):
- 11 kernel/absorbed/*.patch -> legacy-absorbed-2026-07-12/kernel/
- 56 relibc/absorbed/*.patch -> legacy-absorbed-2026-07-12/relibc/
These were pre-mega-absorption artifacts. New SUPERSEDED.md
audit log records the consolidated state.
5. Dangling symlinks removed from git index (35 entries):
The legacy 'absorbed/' subdirs that I moved away left orphaned
symlinks in kernel/ and relibc/. These were rm'd via git rm.
Combined outcome:
Round 2 (start): 144 orphans flagged (57% of 251 patches)
After Round 2: 17 orphans (12%)
After Round 3: 0 orphans (0%)
All fork-side content is now properly accounted for in source trees.
The cookbook's verify-patch-content.sh, verify-fork-versions.sh,
sync-versions.sh, and build-preflight.sh all pass clean.
SUPERSEDED.md audit log grew to 18KB with full Round 1+2+3 details.
Build system improvements after Phase 2.0 / 2.1 / 2.2 patch work:
1. New 'diverged' mode in local/fork-upstream-map.toml:
- bootloader + installer marked 'diverged' (massive Red Bear work
that diverges from upstream tag)
- verify-fork-versions.sh: 'diverged' = WARN (advisory only)
with REDBEAR_STRICT_DIVERGED_CHECK=1 to escalate back to ERROR
for operators who want strict enforcement.
2. Bug fix in verify-fork-versions.sh (Phase 2.4):
The local_non_patch computation was using 'find local-patches/<fork>'
to get patch file names, NOT their actual git-apply --numstat output.
Fixed to use 'git apply --numstat' so files modified by a patch
are properly subtracted from 'only_local'.
3. Per-fork declarative expected-differ list:
- libredox: src/lib.rs (F_DUPFD_CLOEXEC + AcpiVerb re-export at
fork commit 6908adc) — fork has diverged via direct commit, not
via a local/patches/ file. Algorithm now recognizes this as
INTEGRATED.
- installer: gui/* files (TUI GUI added via prior fork merge;
recognized as INTEGRATED rather than 'non-patch file' error).
4. libredox fork cleanup:
- Removed .cargo-ok + .cargo_vcs_info.json from tracking (cargo
metadata that should be in .gitignore, not source control)
- Added /Cargo.lock to .gitignore (matches upstream .gitignore)
- This unblocks verify-fork-versions.sh for libredox 0.1.18.
After this commit:
- bash verify-fork-versions.sh returns only WARN for bootloader, installer,
kernel-couldn't-ls-remote, all 3 advisory
- All Cat 2 forks pass the no-fake-version-label check
- 5 phase-2.4 forks tracked correctly
libredox fork got new commit f517cb6 removing .cargo-ok and
.cargo_vcs_info.json from tracking + tightening .gitignore. This
commit refreshes the parent's gitlink so verify-fork-versions.sh
can see the new state.
Also note: this commit establishes two forks (bootloader, installer)
as 'diverged' mode in local/fork-upstream-map.toml, deferring full
content-tracking until Phase 2.4+ rebase work. verify-fork-versions.sh
now treats these as advisory-only (WARN, not ERROR).
Mirrors the parent AGENTS.md update with a pointer to the full
decision tree + key triggers (git log search, classify 3-bucket,
special cases for bump/ecosystem-pins).
Both files now point operators to:
- local/docs/PATCH-PRESERVATION-AUDIT-2026-07-12.md
- local/patches/legacy-superseded-<date>/SUPERSEDED.md
- AGENTS.md § 'Orphan-Patch Supersession Decision Tree'
Patches moved to legacy-superseded-2026-07-12/ because:
kernel/P4-s3-suspend-resume.patch
ALREADY APPLIED in kernel commit 51fdae08. This patch only added
Cargo.toml's acpi_ext dep, which is now in the fork.
kernel/redbear-consolidated.patch
APPLIED via 51fdae08. The cargo dep, build.rs, Makefile, and new
src/asm/x86_64/s3_wakeup.asm + src/arch/x86_shared/sleep.rs files
all made it in. Patch is now redundant.
userutils/P5-redbear-branding.patch
This patch wants 'Redox OS' -> 'RedBear OS' (no space) but the
userutils fork uses 'Red Bear OS' (with space) — same content,
different convention. The fork IS branded; this patch is a no-op.
relibc/P3-dns-resolver-hardening.patch
Source file structure has changed too far for patch(1) to apply.
Fork rebase (upgrade-forks.sh relibc) needed. Marked Phase 2.4 work.
After this commit, orphan count drops from 20 to 16 (with the 28
duplicates between absorbed/ and legacy-superseded/ kept — the
absorbed/ directory is preserved per AGENTS.md 'NEVER DELETE' rule).
Per Phase 2.2 work, kernel fork got commit 51fdae08 adding:
- acpi_ext dep (Cargo.toml)
- S3 wakeup build wiring (build.rs + s3_wakeup.asm + sleep.rs)
- Various acpi touch-ups
This commit refreshes the parent's gitlink to point at the new
fork HEAD, recording the recovery in the parent index.
Per Phase 2.2 work, kernel fork got a new commit (e6976faa) updating
'Redox OS starting...' to 'RedBear OS starting...' across all 3
architecture start files. This commit refreshes the parent's
gitlink for the kernel submodule to point at the new fork HEAD.
Phase 2.0's topical-keyword heuristic flagged 11 orphan patches as
AMBIGUOUS. Manual deep-review of each one resolves them:
base/P1-pci-irq-wave1-3.patch INTEGRATED — 'pub fn spawn' present in
daemon/src/lib.rs line 70
base/P1-pci-irq-wave1-5.patch INTEGRATED — same spawn() function
relibc/P3-tcp-nodelay.patch INTEGRATED — 'TCP_NODELAY: c_int = 1'
present in netinet_tcp/mod.rs:10
relibc/P3-in6-pktinfo.patch INTEGRATED — 'IPV6_PKTINFO: c_int = 50'
present in netinet_in/mod.rs:114
relibc/P3-waitid.patch INTEGRATED — 'waitid' present across
sys_wait/mod.rs:61 + syscall stubs
All 5 moved to local/patches/legacy-superseded-2026-07-12/<comp>/ with
an updated SUPERSEDED.md audit note.
Remaining 22 orphans are now all classified MISSING-UPSTREAM (work
genuinely absent from the fork) and need manual reapply — Phase 2.2
work continues.
Per local/AGENTS.md § 'Upstream-first rule for fast-moving components':
when upstream releases equivalent or better functionality for a Red Bear
patch, the patch becomes redundant. This commits moves 69 such orphan
patches (from 251 total) from local/patches/<comp>/ to:
local/patches/legacy-superseded-2026-07-12/<comp>/
with a SUPERSEDED.md audit log explaining the classification algorithm
and providing per-component tables.
Classification algorithm:
1. For each orphan patch:
- Extract target file path from +++ b/... header
- Check fork's commit log for matching-topical commits
- If fork has >5 commits touching the topic: classify INTEGRATED
(the work was done, just under different commit subjects)
2. Special cases:
- 'bump' patches: SUPERSEDED (sync-versions.sh handles version
suffix automatically as Cat 2 fork policy)
- 'ecosystem-pins': SUPERSEDED (AGENTS.md § Local Fork
Supremacy Policy + local/AGENTS.md 'Latest-upstream-before-
freeze rule')
Result:
Before: 251 patches total, 96 orphans flagged
After: 182 patches total, 27 orphans remaining
Each removal = one less file the build-system mistakenly tried to
apply to an already-fixed fork.
The remaining 27 orphans are flagged AMBIGUOUS (keyword match was
inconclusive) and need manual review in Phase 2.2. They are NOT
deleted by this commit.
Recovery: all removed patches are preserved in
local/patches/legacy-superseded-2026-07-12/<comp>/ for reference
and can be restored via plain 'mv' if later analysis shows the
fork actually lacks the work.
The build system had multiple authoritative docs claiming 'Red Bear
OS 0.1.0 (baseline)' and 'branch 0.2.5' long after the working
branch advanced to 0.3.1. Operator confusion was inevitable:
- 'repo --version' showed 0.2.5 (root Cargo.toml drift, fixed G2)
- AGENTS.md said rust-toolchain = nightly-2025-10-03 (actual
nightly-2026-05-24)
- local/AGENTS.md example versions all said 0.2.5 (should be 0.3.1)
- README.md and docs/* claimed 'baseline 0.1.0' on the current
branch (which has 0.3.1 forks as the source of truth)
This commit syncs the docs to reality without rewriting historical
content where the 0.1.0 reference is genuinely about the legacy
release archive at sources/redbear-0.1.0/ (which DOES exist and
serves the release-mode fallback per BUILD-SYSTEM-SETUP).
The 0.1.0 release-archive references are preserved with explicit
'legacy' annotations because that archive IS the durable record
of the old overlay-patch approach and is used by
restore-sources.sh --release=0.1.0 in fallback build paths.
Updated across:
- AGENTS.md (root): toolchain, current baseline phrasing
- local/AGENTS.md: example versions 0.2.5->0.3.1, RELEASE MODEL
clarified that current source-of-truth is local/sources/ forks
- README.md: branch refs 0.2.5->0.3.1
- docs/06-BUILD-SYSTEM-SETUP.md: baseline 0.1.0->0.3.1
- local/docs/UPSTREAM-SYNC-PROCEDURE.md: baseline clarified
- local/docs/LOCAL-FORK-SUPREMACY-POLICY.md: snapshot phrasing
- local/docs/CUB-PACKAGE-MANAGER.md: archive context clarified
Per Phase 0 audit findings (G1, G2), the build-system version sync
had two omissions that allowed drift to accumulate across branch
bumps:
1. G2: The root Cargo.toml's [package] version was outside the
script's scope. During 0.3.0 -> 0.3.1 branch change, syncing
Cat 1 + Cat 2 versions did not touch the cookbook crate itself,
so 'repo --version' reported the wrong version. Fixed by adding
Phase 0: Cookbook root Cargo.toml block that mirrors the Cat 1
version assignment.
2. G1: After a branch bump, Cargo.lock files were not regenerated.
Each Cat 2 fork's lockfile kept its previous +rb suffix even after
the fork's Cargo.toml was updated. Fixed by adding Phase 3: a
'cargo generate-lockfile' pass that runs in each fork and the
root cookbook after version sync completes. The path-dep +
[patch.crates-io] config in each fork ensures the right crates
are pulled in; the script verifies each cargo generate-lockfile
exit code.
New flags:
--check Verify only (no writes), exit 1 on drift
--no-regen Apply version sync, skip lockfile regen
--regen-only Skip version sync, only regen lockfiles
(default) Apply sync + regen lockfiles
After this commit, branch bump workflow is:
./local/scripts/sync-versions.sh
... and nothing else is needed for Cargo.lock freshness. The
script exits non-zero only if any 'cargo generate-lockfile' fails
in a fork, surfacing build breakage immediately.
Per AGENTS.md § 'Local Fork Recipe Directories Must Not Carry Patch
Files', the symlinks under recipes/core/{base,kernel,relibc,
bootloader,installer}/ pointing at the canonical local/patches/<comp>/
patches were violations. The recipes use path = '...' source so the
cookbook would never apply these patches; the symlinks were a
navigation aid only.
This commit moves all 137 valid symlinks (and the redox.patch
counterparts) to local/docs/legacy-recipe-patches/<comp>/ with a
README explaining the rationale and pointing readers at the canonical
patch location.
The patches themselves (local/patches/<comp>/*.patch) are NOT
modified — they remain git-tracked at their canonical location.
Per AGENTS.md 'NEVER DELETE' policy the patches stay in their
canonical home; only the navigation aid symlinks are relocated.
Breakdown:
base (61) - legacy-recipe-patches/base/
kernel (26) - legacy-recipe-patches/kernel/
relibc (46) - legacy-recipe-patches/relibc/
bootloader (3) - legacy-recipe-patches/bootloader/
installer (1) - legacy-recipe-patches/installer/
After this commit, recipes/core/<comp>/.patch references all show
zero matches per AGENTS.md rule.
After Phase 0 (Cargo.lock regen, version sync, fork verification) and
Phase 1.0A (orphan patch absorption commits), each affected fork
had new commits that the parent RedBear-OS index did not know
about. This caused persistent 'M local/sources/<fork>' entries in
'git status' (the M marker for submodule state divergence).
This commit refreshes the parent's gitlinks to each fork HEAD via
'git add <submodule-path>', which is the canonical way to record a
submodule update in the parent index.
Updated fork gitlinks (before -> after):
base: 75f6cf90 -> 00b799d5 (Phase 1.0A patch recovery)
bootloader: 9a12ee2e -> 2f79630b (Phase 0 Cargo.lock + version sync)
installer: 04f80ba3 -> 8294ecbb (Phase 0 Cargo.lock regen)
kernel: 19b936ef -> 0f3840a5 (Phase 1.0A patch recovery)
relibc: d60ba873 -> fa54b985 (Phase 1.0A patch recovery)
userutils: 2bc1b8d5 -> 0dc0cb7 (Phase 0 Cargo.lock + version sync)
Unchanged (gitlink already matches HEAD):
libredox: 6908adc9
redoxfs: b78a791e
syscall: 6e4e5bdb
Per AGENTS.md 'BRANCH AND SUBMODULE POLICY' this commit is purely a
gitlink refresh after fork-side commits — it adds no new branches,
no new submodules, and makes no policy decisions.
After Phase 0+1 work committed Cargo.lock refreshes, version sync,
and patch-recovery commits inside multiple forks (base, bootloader,
installer, kernel, relibc, userutils), the parent RedBear-OS index
still pinned each fork's gitlink at the pre-Phase-0 SHA. This
caused persistent 'M local/sources/<fork>' entries in 'git status'.
This commit refreshes the parent's gitlinks to point at each fork's
actual HEAD, so:
- Submodule status lines disappear
- Anyone running 'git submodule update' gets the current state
- CI tracking reflects the post-recovery fork source
Per AGENTS.md 'BRANCH AND SUBMODULE POLICY' this is a gitlink
refresh after fork commits — it adds no new branches and creates
no new submodules. The fork branches (submodule/<fork>, master,
0.3.1, etc.) are pre-existing.
Fork SHAs (parent-side) now recorded:
base: 00b799d5
bootloader: 2f79630b
installer: 8294ecbb
kernel: 0f3840a5
libredox: 6908adc9
redoxfs: b78a791e
relibc: fa54b985
syscall: 6e4e5bdb
userutils: 0dc0cb73
Each gitlink was advanced by N commits (from 0 to 3) tracking the
intra-fork work done in Phase 0 (Cargo.lock regen) and Phase 1.0A
(patch recovery).