# Cargo Patch Propagation — Fork Dependency Policy **Created:** 2026-07-02 **Status:** Active policy **Scope:** All Rust recipes that depend on forked packages ## The Rule Every recipe that uses a forked package **MUST** depend on the local fork — never on the upstream/crates.io version. ``` Upstream package (gitlab.redox-os.org / crates.io) │ ▼ Our local fork (local/sources//) │ (patches applied, version bumps tracked) ▼ ALL downstream recipes depend on THIS fork ``` ### Forked packages (as of 2026-07-02) | Package | Local fork | Version | Symlink | |---------|-----------|---------|---------| | `redox_syscall` | `local/sources/syscall/` | 0.8.1 | `recipes/core/base/syscall` | | `libredox` | `local/sources/libredox/` | 0.1.18 | `recipes/core/base/libredox` | ### Lifecycle: when upstream bumps version 1. **Update fork first** — `git fetch upstream && git rebase upstream/master` in `local/sources//` 2. **Rebase all patches** — ensure Red Bear patches still apply against the new upstream 3. **Rebuild prefix** — `touch && make prefix` if the fork affects the cross-toolchain sysroot 4. **All dependents automatically use the updated fork** — no per-recipe changes needed 5. **Adapt downstream code** — if the upstream bump changed an API, fix every recipe that breaks (Golden Rule: Red Bear adapts to upstream, never the reverse) ## Why This Matters ### The Problem: Cargo `[patch]` doesn't propagate Cargo's `[patch.crates-io]` only applies from the **root package** being compiled. When a recipe depends on a base workspace member (like `daemon`) via path dependency, the base workspace's own `[patch]` entries are **silently ignored**. This means: 1. Recipe `redox-drm` depends on `daemon` via `path = ".../base/source/daemon"` 2. `daemon` is part of the `base` workspace, which patches `redox_syscall` and `libredox` 3. But when cargo compiles `redox-drm` as root, only `redox-drm`'s own `[patch]` applies 4. `libredox` from crates.io **vendors its own copy** of `redox_syscall` internally 5. Two different `syscall::Error` types exist at compile time → E0277 type mismatch ### The Fix: Use path dependencies, not version dependencies Every recipe that needs `redox_syscall` or `libredox` should use the local fork via path dependency or `[patch.crates-io]` entry. The path is always the same relative depth from `local/recipes///source/Cargo.toml`: ``` ../../../../../recipes/core/base/syscall → local fork of redox_syscall ../../../../../recipes/core/base/libredox → local fork of libredox ``` ## How to Make a Recipe Depend on the Fork ### Option A: Direct path dependency (preferred for direct deps) ```toml [dependencies] redox_syscall = { path = "../../../../../recipes/core/base/syscall", features = ["std"] } libredox = { path = "../../../../../recipes/core/base/libredox" } ``` ### Option B: Version dep + patch redirect (when transitive deps also need redirection) ```toml [dependencies] redox_syscall = { version = "0.8", features = ["std"] } libredox = "0.1" daemon = { path = "../../../../../recipes/core/base/source/daemon" } [patch.crates-io] redox_syscall = { path = "../../../../../recipes/core/base/syscall" } libredox = { path = "../../../../../recipes/core/base/libredox" } ``` Use Option B when the recipe depends on ANY base workspace member via path (`daemon`, `scheme-utils`, etc.). The `[patch]` entries redirect ALL transitive `redox_syscall` and `libredox` dependencies (from `redox-scheme`, etc.) to the local fork. ### Symlinks required The paths above resolve through symlinks that must exist: ```bash recipes/core/base/syscall → ../../../local/sources/syscall recipes/core/base/libredox → ../../../local/sources/libredox ``` These symlinks bridge the base workspace's relative path resolution. Without them, the base workspace's `path = "../syscall"` resolves to a non-existent directory. ## Validation Run the validation gate to check all recipes: ```bash ./target/release/repo validate-cargo-deps ``` This scans every `Cargo.toml` in `recipes/` and `local/recipes/`, detects: - Path dependencies on base workspace members without matching `[patch]` entries - Version dependencies on forked packages that should use the local fork - Missing or broken symlinks Exit code 0 = all OK, exit code 1 = warnings found. ## Recipes Currently Violating This Policy As of 2026-07-02, the following recipes pull `redox_syscall` or `libredox` from crates.io instead of the local fork. These are policy violations that should be fixed incrementally: ### `redox_syscall` from crates.io (25 recipes) Recipes using version 0.8 (straightforward conversion to fork): - `drivers/ehcid`, `drivers/linux-kpi`, `drivers/redbear-btusb` - `drivers/redox-driver-sys` - `system/driver-manager`, `system/evdevd`, `system/firmware-loader` - `system/hwrngd`, `system/iommu` - `system/redbear-btctl`, `system/redbear-hwutils` - `system/redbear-sessiond`, `system/redbear-traceroute` - `system/redbear-wifictl`, `system/thermald`, `system/udev-shim` - `tui/tlc` Recipes using version 0.7 (need API adaptation to 0.8): - `drivers/virtio-inputd`, `system/devfsd`, `system/diskd` Recipes using version 0.4 (need significant API adaptation to 0.8): - `system/redbear-accessibility`, `system/redbear-ime`, `system/redbear-keymapd` - `gpu/redox-drm` (uses `syscall04` for legacy PCI config — separate concern) ### `libredox` from crates.io (21 recipes) All recipes using `libredox = "0.1"` or `libredox = "0.1.x"` from crates.io should switch to the local fork at `local/sources/libredox/` (version 0.1.18). ## Migration Plan 1. **Phase 1 (done):** Fix `redox-drm` — the only recipe with a path dep on `daemon` 2. **Phase 2:** Convert all 0.8.x recipes to use the local fork (straightforward) 3. **Phase 3:** Adapt 0.7.x recipes to 0.8.x API and switch to fork 4. **Phase 4:** Adapt 0.4.x recipes to 0.8.x API and switch to fork Each conversion is: change `version = "0.x"` to `path = "..."`, cook, fix any API breaks. ## Related - `local/AGENTS.md` § "GOLDEN RULE — Red Bear adapts to upstream, never the reverse" - `local/AGENTS.md` § "LOCAL FORK MODEL (CORE COMPONENTS)" - `local/sources/base/Cargo.toml` lines 133-157 — the base workspace `[patch]` section that ONLY applies within the base workspace