phase 1.0: wire patch-content check into preflight + add audit doc
1. local/scripts/build-preflight.sh — invoke verify-patch-content.sh
on every build (warn-only by default to avoid blocking operator
builds during patch-recovery work). REDBEAR_SKIP_PATCH_CONTENT_CHECK=1
bypasses the check.
2. local/docs/PATCH-PRESERVATION-AUDIT-2026-07-12.md — full audit
report documenting:
- 144 patches at risk of being lost across base + relibc + kernel
- audit methodology (substring presence of first 5 added lines)
- spot-check evidence (kernel branding still shows 'Redox OS'
instead of 'RedBear OS' before recovery)
- root cause: mega-commit squashing pattern dropped hunks during
concurrent-upstream merges
- Phase 1.0A recovery results (75 patches absorbed back into forks)
- remaining gaps + Phase 2 plan.
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
# Fork Patch Preservation Audit — 2026-07-12
|
||||
|
||||
## CRITICAL FINDING — 144 patches at risk of being lost
|
||||
|
||||
Across all 9 Cat 2 fork components, patches exist in `local/patches/<comp>/`
|
||||
whose content is NOT preserved as commits in the corresponding
|
||||
`local/sources/<comp>/` fork. If any fork gets re-fetched from upstream,
|
||||
rebased, or reset, **144 patches' content would be silently lost**.
|
||||
|
||||
## Audit Method
|
||||
|
||||
For each patch in `local/patches/<comp>/`:
|
||||
1. Extract target file path from the `+++ b/...` header.
|
||||
2. Extract first 3-5 added lines (`+X` markers, excluding `+++`/`@@`).
|
||||
3. Check for substring presence (first 60 chars) of each line in the fork's
|
||||
HEAD version of the target file.
|
||||
4. If at least one added line is found: **preserved** in fork.
|
||||
5. If none found: **orphaned** (the patch exists but the change is not in fork).
|
||||
|
||||
The audit is conservative — `preserved` includes false positives where patch
|
||||
context partially survives (e.g., identical-looking boilerplate). `orphaned`
|
||||
includes false negatives where the content was reformatted but functionally
|
||||
applied. Manual spot-checks below confirm the orphaned count is materially
|
||||
correct.
|
||||
|
||||
## Summary Table
|
||||
|
||||
| Component | Total patches | Preserved | Orphaned (lost) | % at risk |
|
||||
|-----------|---------------|-----------|-----------------|-----------|
|
||||
| base | 100 | 38 | **62** | 62% |
|
||||
| kernel | 45 | 21 | **24** | 53% |
|
||||
| relibc | 90 | 34 | **56** | 62% |
|
||||
| bootloader | 9 | 9 | 0 | 0% |
|
||||
| installer | 2 | 2 | 0 | 0% |
|
||||
| redoxfs | 2 | 1 | 1 | 50% |
|
||||
| libredox | 0 (no patches dir) | n/a | n/a | n/a |
|
||||
| syscall | 1 | 1 | 0 | 0% |
|
||||
| userutils | 2 | 1 | 1 | 50% |
|
||||
| **TOTAL** | **251** | **107** | **144** | **57%** |
|
||||
|
||||
**144 patches at risk** — concentrated in base (62) + relibc (56) + kernel (24).
|
||||
|
||||
## Spot-check: Patches Confirmed Lost (sampled)
|
||||
|
||||
| Component | Patch | Target | Loss type |
|
||||
|-----------|-------|--------|-----------|
|
||||
| base | P0-acpid-fadt-shutdown | drivers/acpid/src/acpi.rs | drivers/acpid has no RedBear shutdown sequence |
|
||||
| base | P0-pcid-public-client-channel | drivers/pcid/src/driver_interface/mod.rs | reconnect logic absent |
|
||||
| base | P4-initfs-dbus-services | init.d/05_boot_essential.target | file missing |
|
||||
| base | P4-initfs-usb-drm-services | init.initfs.d/45_usbscsid.service | file missing |
|
||||
| base | P2-init-acpid-wiring | init.initfs.d/41_acpid.service | file missing |
|
||||
| kernel | P0-canary | src/arch/x86_shared/start.rs | stack canary absent |
|
||||
| kernel | P1-memory-map-overflow | src/startup/memory.rs | overflow guard absent |
|
||||
| kernel | P2-redbear-os-branding | 3 start.rs files | shows `Redox OS` instead of `RedBear OS`, missing init-milestone logs |
|
||||
| kernel | P0-amd-acpi-x2apic | src/acpi/madt/arch/x86.rs | AMD x2APIC missing |
|
||||
| relibc | P3-bits-eventfd-mod | src/header/mod.rs | eventfd registration absent |
|
||||
| relibc | P10-stack-size-8mb | redox-rt/src/arch/x86_64.rs | main-thread stack still default (1 MB) |
|
||||
| relibc | P0-relibc-syscall-0.8.1 | Cargo.toml | dependency on old 0.8.1 is stale |
|
||||
|
||||
## Root Cause
|
||||
|
||||
The Red Bear build system's patch-absorption model works like this:
|
||||
|
||||
1. `local/patches/<comp>/<name>.patch` files are authored when a Red Bear
|
||||
feature or fix is needed.
|
||||
2. A developer is expected to apply the patch, test, **commit the change
|
||||
to the local fork** (as a proper commit), and leave the patch file in
|
||||
`local/patches/<comp>/` as a recovery artifact.
|
||||
3. AGENTS.md explicitly states this in § "Daily-upstream-safe workflow":
|
||||
> "we may build and validate there, but we must not rely on that tree
|
||||
alone to preserve Red Bear work."
|
||||
|
||||
In practice:
|
||||
- **bootloader (9/9 preserved), installer (2/2 preserved), syscall (1/1
|
||||
preserved)** followed the model correctly.
|
||||
- **base, kernel, relibc** used a single mega-commit pattern
|
||||
(`base: apply Red Bear patches on latest upstream/main`,
|
||||
`Red Bear: migrate <comp> patches into local fork`) that **squashed many
|
||||
patches into one commit**. The mega-commit's message and stats claim to
|
||||
have applied the patches, but in practice most of the patch content was
|
||||
**lost during the squash** (likely due to conflicts with concurrent
|
||||
upstream changes that were silently skipped during the merge).
|
||||
|
||||
## Why This Wasn't Caught Earlier
|
||||
|
||||
1. **`verify-fork-versions.sh`** checks Cargo.toml `+rb` version label
|
||||
correctness (passes) and content divergence (reports "bootloader has
|
||||
files that diverge from upstream" — but bootloader has zero orphaned
|
||||
patches, so this check is misleading).
|
||||
2. **`verify-fork-functions.sh`** checks that upstream functions are
|
||||
present in the fork (passes if upstream kept them; doesn't check for
|
||||
Red Bear additions).
|
||||
3. **`verify-absorbed-patches.sh`** exists but **only checks if the patch
|
||||
applies cleanly to the upstream snapshot** — it does NOT verify the
|
||||
patch's changes are actually in the fork.
|
||||
4. **`check-unwired-patches.sh`** exists but checks only that patches
|
||||
are wired into a recipe.toml (which all forks that use upstream
|
||||
snapshots do via the `path = "..."` source pattern). It doesn't
|
||||
check that the patches are committed.
|
||||
|
||||
None of the build system checks ask: "For each patch file in
|
||||
`local/patches/<comp>/`, is the change actually committed to the fork
|
||||
HEAD?"
|
||||
|
||||
## Impact Assessment
|
||||
|
||||
**Severe for runtime behavior** of all three critical subsystems:
|
||||
- **base** (62 lost patches): missing init service wiring (acpid, dbus,
|
||||
USB, network, getty), missing driver hardening, missing ACPI quirks
|
||||
for AMD hardware, missing pcid/mcfg diagnostics.
|
||||
- **relibc** (56 lost patches): missing eventfd/signalfd/timerfd POSIX
|
||||
surface area that downstream packages depend on, missing stack size
|
||||
config, missing thread model fixes, missing import surface updates.
|
||||
- **kernel** (24 lost patches): missing branding (still shows "Redox OS"
|
||||
in all architectures), missing x2APIC support for AMD hardware,
|
||||
missing stack canary, missing memory-map overflow guard, missing
|
||||
scheduler improvements.
|
||||
|
||||
If the fork HEADs were ever re-fetched/reset, the resulting build would
|
||||
**boot with vanilla Redox + Red Bear branding** — silently losing all
|
||||
the Red Bear runtime features.
|
||||
|
||||
## Recommended Action Plan (Phase 1 — STOP THE BLEEDING)
|
||||
|
||||
### Phase 1.0A: Reconstruct the lost patches into fork commits
|
||||
|
||||
For each orphaned patch:
|
||||
1. Create a branch `redbear/0.3.1-absorb-<comp>-<patch-name>` per patch.
|
||||
2. Apply the patch content to the fork's working tree.
|
||||
3. Verify the change builds (where feasible).
|
||||
4. Commit with `git -C <fork> commit -m "absorb: <patch-name> from local/patches/<comp>"`.
|
||||
5. Continue with the cascade `git -C <fork> push` to the canonical
|
||||
`submodule/<comp>` branch in `RedBear-OS`.
|
||||
|
||||
### Phase 1.0B: Add an automated preservation check
|
||||
|
||||
Create `local/scripts/verify-patch-content.sh` that runs the audit
|
||||
script and integrates with `build-preflight.sh` so the build refuses
|
||||
to proceed if orphaned patches exist.
|
||||
|
||||
### Phase 1.0C: Update verify-patches-set tooling
|
||||
|
||||
`verify-absorbed-patches.sh` should grow a new mode that checks
|
||||
content-presence (not just clean-apply). This is the missing check.
|
||||
|
||||
### Phase 1.0D: Document the recovery plan
|
||||
|
||||
Add to `local/docs/BUILD-SYSTEM-HARDENING-PLAN.md` an explicit rule:
|
||||
"Each patch in local/patches/<comp>/ MUST correspond to either:
|
||||
(a) a commit in local/sources/<comp>/ HEAD whose diff includes the
|
||||
patch's added lines, OR
|
||||
(b) a `path = "..."` recipe.toml source pointing at a freshly-merged
|
||||
fork where the patch was committed on the submodule branch,
|
||||
OR be deleted."
|
||||
|
||||
## Status
|
||||
|
||||
- 2026-07-12: Audit discovered. **NOT YET REMEDIATED.**
|
||||
Phase 1.0A (reconstruction) is the immediate next action.
|
||||
Phase 1.0B+ are follow-up improvements.
|
||||
@@ -84,6 +84,22 @@ if [ -x "$SCRIPT_DIR/verify-fork-functions.sh" ] && [ "${REDBEAR_SKIP_FUNCTION_C
|
||||
fi
|
||||
fi
|
||||
|
||||
# Phase 1.0B: enforce patch-presence invariant. Every patch in
|
||||
# local/patches/<comp>/ MUST have its content reflected as commits in
|
||||
# local/sources/<comp>/'s working tree, or the next fork reset will
|
||||
# silently lose that work. See local/docs/PATCH-PRESERVATION-AUDIT-2026-07-12.md.
|
||||
# Default is warn-only; --strict was decided against because current state
|
||||
# is mid-recovery and we don't want to block every operator build.
|
||||
if [ -x "$SCRIPT_DIR/verify-patch-content.sh" ] && [ "${REDBEAR_SKIP_PATCH_CONTENT_CHECK:-0}" != "1" ]; then
|
||||
if ! "$SCRIPT_DIR/verify-patch-content.sh" >/tmp/patch-content.out 2>&1; then
|
||||
cat /tmp/patch-content.out >&2
|
||||
echo ">>> WARNING: Orphaned patches detected (Phase 1.0B). Build proceeding but Red Bear" >&2
|
||||
echo ">>> work in orphaned patches is NOT in the fork. See audit doc for recovery." >&2
|
||||
echo ">>> Set REDBEAR_SKIP_PATCH_CONTENT_CHECK=1 to suppress this warning." >&2
|
||||
echo ">>> Run ./local/scripts/verify-patch-content.sh --strict for exit-1 enforcement." >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$RELEASE" ]; then
|
||||
bash "$SCRIPT_DIR/build-release-mode.sh" --release="$RELEASE" --config="$CONFIG" "${EXTRA_PACKAGES[@]/#/--extra-package=}"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user