Files
RedBear-OS/local/docs/FORK-BUMP-PATCHING-POLICY.md
T
vasilito 538b1745d4 policy: document delicate patching during version and toolchain bumps
New local/docs/FORK-BUMP-PATCHING-POLICY.md, wired into AGENTS.md,
local/AGENTS.md and docs/README.md. Core rule: a version bump is a
REBASE, never a REPLACEMENT.

Written from the libredox 0.1.19 incident, which hit three failures at
once -- wholesale replacement that dropped the acpi re-export,
F_DUPFD_CLOEXEC, the mandated authors entry and the Single-Repo
repository URL; a version-named branch that also tripped the build's
fork-branch gate; and a stale fork-upstream-map.toml that failed
verify-fork-versions.sh with a fake-label violation. Covers the vendored
recipe-fork mirror image (metadata moves, build input does not -- how Qt
shipped 6.11.0 against a 6.11.1 recipe), patch handling during a bump,
and toolchain bumps as version bumps.

Also fixes the two bookkeeping gaps that failed this build:

- local/fork-upstream-map.toml: libredox 0.1.18 -> 0.1.19, never updated
  when the fork was bumped.
- verify-fork-versions.sh: record redoxfs's legitimate divergence in the
  declarative allowlist the script already provides for libredox, with
  the originating commit for each file -- a936d00 (Red Bear, Vec from
  alloc for no_std bootloader builds) for filesystem.rs and record.rs,
  852a971 (Red Bear, RecvFd EOPNOTSUPP) for mount/redox/mod.rs, and
  d807dd3 (upstream symlink fix imported ahead of the 0.9.1 tag) for
  mount/redox/scheme.rs. Documented rather than bypassed with
  REDBEAR_SKIP_FORK_VERIFY.
2026-08-03 13:12:47 +03:00

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:

  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/<name>, 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.
    git -C local/sources/<fork> log --oneline <upstream-tag>..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=<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.
  4. Diff the result against the old fork tip, ignoring comments:
    git diff <old-fork-tip> HEAD -- <paths> | 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 <new-upstream>+rb<branch>+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/<fork>/ 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:
    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 <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=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: <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