# 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//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: 1. **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`-gated `pub mod acpi` re-export of `AcpiVerb`, - `F_DUPFD_CLOEXEC`, - the `vasilito` entry in `authors`, which `local/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.md` documents for the deprecated `bump-fork.sh`, which is why that tool is guarded. 2. **A stray version-named branch.** `bump-0.1.19` violates `local/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. 3. **Bookkeeping was left behind.** `local/fork-upstream-map.toml` still said `0.1.18` while `Cargo.toml` said `0.1.19+rb0.3.2`, so `verify-fork-versions.sh` failed 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. 1. **Work on the canonical branch.** `submodule/`, never a new branch. A version is not a branch name — it is a `Cargo.toml` field. 2. **Inventory the fork's own commits first.** ```bash git -C local/sources/ log --oneline ..HEAD ``` Every one of those commits must still be represented afterwards. 3. **Rebase or merge onto the new upstream tag.** Prefer `upgrade-forks.sh --to=`. 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. 4. **Diff the result against the old fork tip, ignoring comments:** ```bash git diff HEAD -- | grep -E '^[+-]' \ | grep -vE '^[+-][+-]' | grep -vE '^[+-]\s*(//|$)' ``` Anything that disappears here is lost work. Put it back. 5. **Re-check the policy-mandated `Cargo.toml` fields**, which wholesale replacement always clobbers because they come from upstream: - `authors` must still list every Red Bear contributor, - `repository` must still be the Gitea URL, - `version` must be `+rb` — `+rb`, never `-rb`. 6. **Update `local/fork-upstream-map.toml` in the same commit.** A fork whose `Cargo.toml` and map disagree is a fake label and fails the build. 7. **Account for divergence.** After a bump, `verify-fork-versions.sh` compares content against the upstream tag. Every diverging file must be explained by either a patch in `local/patches//` or an entry in the script's per-fork declarative allowlist, **with a comment naming the commit and why**. Do not reach for `REDBEAR_SKIP_FORK_VERIFY=1` — that hides the very drift the check exists to catch. 8. **Type-check before committing:** ```bash cargo check --target x86_64-unknown-redox --offline ``` 9. **Bump the parent gitlink** and say in the message what moved and why. 10. **Delete the stray branch** if one was created, after confirming its content is reachable elsewhere (`git branch -a --contains `). ## Vendored recipe forks (`local/recipes//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=0` deliberately. - **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-cache` after 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.mk` hardcoded `13.2.0` in a `rm`, which silently no-ops after an upgrade and leaves a conflicting `limits.h` behind. ## Red flags — stop and re-read this document | Symptom | What it usually means | |---|---| | `ERROR: Cargo.toml declares version=X but the upstream map has it tracking Y` | Step 6 skipped | | `ERROR: has files that diverge from upstream ` | 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 |