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, andd807dd3(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.
7.5 KiB
Delicate Patching During a Version Bump (ABSOLUTE)
Status: ABSOLUTE — do not violate.
Established: 2026-08-03, after the libredox 0.1.19 bump silently dropped
committed fork work and left the upstream map stale.
Scope: every Cat 2 fork under local/sources/, every vendored recipe fork
under local/recipes/<name>/source/, and every patch in local/patches/.
Related: LOCAL-FORK-SUPREMACY-POLICY.md, PATCH-GOVERNANCE.md,
RELEASE-BUMP-WORKFLOW.md, VERSIONING.md,
local/AGENTS.md § "No-fake-version-label rule".
The core rule
A version bump is a REBASE, never a REPLACEMENT.
Moving a fork to a newer upstream means taking upstream's new commits onto the fork. It never means taking upstream's tree and re-adding whatever Red Bear work you happen to remember.
Everything below follows from that one sentence.
Why this exists — the libredox 0.1.19 incident
local/sources/libredox was moved from upstream 0.1.18 to 0.1.19 on a branch
named bump-0.1.19. Three separate policy failures compounded:
-
Wholesale replacement lost committed work. The bump rebuilt the fork from upstream 0.1.19 and re-applied only some Red Bear commits. Silently dropped:
- the
redox_syscall-gatedpub mod acpire-export ofAcpiVerb, F_DUPFD_CLOEXEC,- the
vasilitoentry inauthors, whichlocal/AGENTS.md§ "Fork authorship attribution" requires, repository = "https://gitea.redbearos.org/vasilito/RedBear-OS", which the Single-Repo Rule requires.
This is the exact failure mode
local/AGENTS.mddocuments for the deprecatedbump-fork.sh, which is why that tool is guarded. - the
-
A stray version-named branch.
bump-0.1.19violateslocal/AGENTS.md§ BRANCH AND SUBMODULE POLICY ("create a branch named after a version — this is a policy violation"). It also tripped the build's fork-branch gate, so nothing could be built at all. -
Bookkeeping was left behind.
local/fork-upstream-map.tomlstill said0.1.18whileCargo.tomlsaid0.1.19+rb0.3.2, soverify-fork-versions.shfailed the build with a fake-label violation.
The correct resolution was to merge upstream 0.1.19 into
submodule/libredox, which kept every Red Bear change and gained upstream's
multiple-fds work, then update the map.
Mandatory sequence for bumping a Cat 2 fork
Use local/scripts/bump-release.sh (see RELEASE-BUMP-WORKFLOW.md); it
automates most of this. When doing it by hand, all of it applies.
- Work on the canonical branch.
submodule/<name>, never a new branch. A version is not a branch name — it is aCargo.tomlfield. - Inventory the fork's own commits first.
Every one of those commits must still be represented afterwards.
git -C local/sources/<fork> log --oneline <upstream-tag>..HEAD - Rebase or merge onto the new upstream tag. Prefer
upgrade-forks.sh --to=<tag>. A conflict is information, not an obstacle — resolving it is how fork work gets carried forward. Taking upstream's side in a conflict is correct only when upstream rewrote that code ("Red Bear adapts to upstream"); it is never correct as a bulk shortcut. - Diff the result against the old fork tip, ignoring comments:
Anything that disappears here is lost work. Put it back.
git diff <old-fork-tip> HEAD -- <paths> | grep -E '^[+-]' \ | grep -vE '^[+-][+-]' | grep -vE '^[+-]\s*(//|$)' - Re-check the policy-mandated
Cargo.tomlfields, which wholesale replacement always clobbers because they come from upstream:authorsmust still list every Red Bear contributor,repositorymust still be the Gitea URL,versionmust be<new-upstream>+rb<branch>—+rb, never-rb.
- Update
local/fork-upstream-map.tomlin the same commit. A fork whoseCargo.tomland map disagree is a fake label and fails the build. - Account for divergence. After a bump,
verify-fork-versions.shcompares content against the upstream tag. Every diverging file must be explained by either a patch inlocal/patches/<fork>/or an entry in the script's per-fork declarative allowlist, with a comment naming the commit and why. Do not reach forREDBEAR_SKIP_FORK_VERIFY=1— that hides the very drift the check exists to catch. - Type-check before committing:
cargo check --target x86_64-unknown-redox --offline - Bump the parent gitlink and say in the message what moved and why.
- Delete the stray branch if one was created, after confirming its
content is reachable elsewhere (
git branch -a --contains <sha>).
Vendored recipe forks (local/recipes/<name>/source/)
Same principle, different machinery — see VERSIONING.md. recipe.toml is the
source of truth; source.tar, source/, and .redbear-src-version are derived
and must be re-derived by sync-recipe-source.sh, never hand-edited to match.
The failure mode here is the mirror image: the metadata moves and the build
input does not. A tar= bump that never propagates into the vendored
source/ silently keeps compiling the old version — that is how Qt shipped
6.11.0 while the recipe declared 6.11.1, and KDE built 6.10.0 against recipes
declaring 6.28.0.
PATCH-REJECT / SHIM-REJECT from the sync engine means re-port by hand onto
the new upstream. It never means freeze at the old version, and never means
apply with fuzz.
Patches during a bump
- Never delete a patch to make a bump apply. Rebase it.
(
PATCH-GOVERNANCE.md§ 1 — this rule exists because an agent once deleted 8 patches and 9 BINS entries and destroyed a day of driver work.) - A patch that no longer applies is a question, not a verdict. Run the
orphan-patch decision tree in
AGENTS.md§ "Orphan-Patch Supersession Decision Tree" before concluding anything: superseded, integrated elsewhere, or a genuine gap. - Never apply with fuzz to force a bump through. The build uses
--fuzz=0deliberately. - Re-validate after reordering, since patches carry line-number context:
remove
source/, re-fetch, rebuild.
Toolchain bumps are version bumps too
A compiler upgrade changes the inputs to every package even though no recipe version moves, and the content-hash cache does not track the toolchain — so a stale cache will happily serve objects built by the previous compiler.
- Rebuild with
--no-cacheafter a toolchain swap. - Expect default-language-standard changes. GCC 13→16 moved the default C
dialect from gnu17 to gnu23, where an empty parameter list means "no
arguments" instead of "unspecified" — see
PACKAGE-BUILD-QUIRKS.md. - Version-glob toolchain paths in build files.
mk/prefix.mkhardcoded13.2.0in arm, which silently no-ops after an upgrade and leaves a conflictinglimits.hbehind.
Red flags — stop and re-read this document
| Symptom | What it usually means |
|---|---|
ERROR: <fork> Cargo.toml declares version=X but the upstream map has it tracking Y |
Step 6 skipped |
ERROR: <fork> has files that diverge from upstream <tag> |
Step 7 skipped — divergence undocumented |
REFUSING TO BUILD — FORKS NOT ON CANONICAL BRANCHES |
A stray branch was created (step 1) |
A fork's authors lost its Red Bear entries |
Wholesale replacement (step 5) |
| A bump commit whose only change is the version field | Fake label — no real rebase happened |
Reaching for REDBEAR_SKIP_FORK_VERIFY=1 |
The check is right and the bump is wrong |