docs: add LOCAL-FORK-SUPREMACY-POLICY
Documents the project-wide rule that Red Bear OS local forks are the complete, authoritative source of truth for every component they cover. Key points: - A fork that delegates to upstream/crates.io is not a fork — it's a wrapper. - Missing fork functionality is implemented in the fork, not papered over. - Path deps + [patch.crates-io] everywhere; no version strings for forks. - Adapt to upstream at the same pace — never pin back. Adds a cross-reference from local/AGENTS.md DESIGN PRINCIPLE section.
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
# Local Fork Supremacy Policy
|
||||
|
||||
**Status:** ABSOLUTE — Do not violate.
|
||||
**Established:** 2026-07-10 (reinforced during `setrens` namespace debug session)
|
||||
**Scope:** All Red Bear OS components, all agents, all operators.
|
||||
|
||||
---
|
||||
|
||||
## Core Principle
|
||||
|
||||
**Red Bear OS local forks are the COMPLETE, AUTHORITATIVE source of truth.**
|
||||
|
||||
Everything else — upstream Redox, crates.io, third-party registries — exists ONLY
|
||||
as reference material and raw material that flows INTO our local forks. Our local
|
||||
forks are NOT thin wrappers, NOT compatibility shims, and NOT delegators that
|
||||
"just call upstream".
|
||||
|
||||
If a component is forked, it is forked COMPLETELY. Our fork contains every piece
|
||||
of functionality our system needs, implemented in our tree, maintained by us.
|
||||
|
||||
## The Fork Model
|
||||
|
||||
### What Red Bear OS Is
|
||||
|
||||
Red Bear OS is a **full fork** of Redox OS, based on frozen Redox snapshots
|
||||
(currently 0.1.0 at build-system commit `f55acba68`). We are NOT a downstream
|
||||
distributor of Redox. We are NOT a configuration overlay on top of Redox.
|
||||
|
||||
We took Redox as a starting point and are building our own complete operating
|
||||
system on top of it. The fork boundary is real: every component we depend on
|
||||
lives in our tree.
|
||||
|
||||
### The 9 Declared Submodules
|
||||
|
||||
| Submodule | Branch | Path |
|
||||
|------------------|-----------------------|-------------------------------|
|
||||
| base | `submodule/base` | `local/sources/base` |
|
||||
| bootloader | `submodule/bootloader`| `local/sources/bootloader` |
|
||||
| installer | `submodule/installer` | `local/sources/installer` |
|
||||
| kernel | `submodule/kernel` | `local/sources/kernel` |
|
||||
| libredox | `submodule/libredox` | `local/sources/libredox` |
|
||||
| redoxfs | `submodule/redoxfs` | `local/sources/redoxfs` |
|
||||
| relibc | `submodule/relibc` | `local/sources/relibc` |
|
||||
| syscall | `submodule/syscall` | `local/sources/syscall` |
|
||||
| userutils | `submodule/userutils` | `local/sources/userutils` |
|
||||
|
||||
**9 submodules. No more, no less.** The fork inventory is closed by policy.
|
||||
Adding a new submodule requires explicit operator justification (see
|
||||
`local/AGENTS.md` § BRANCH AND SUBMODULE POLICY).
|
||||
|
||||
### Tracked Trees (Not Submodules)
|
||||
|
||||
Some components live as full tracked trees under `local/sources/<name>/` instead
|
||||
of as submodules. They have the same fork-supremacy guarantee — the local tree is
|
||||
the source of truth. Examples include `libredox`, `redox-scheme`, and other
|
||||
smaller but critical components that don't have upstream worth tracking on a
|
||||
dedicated branch.
|
||||
|
||||
### Local Recipes (Original Red Bear Code)
|
||||
|
||||
Components with NO upstream origin (e.g. `tlc`, `redbear-sessiond`,
|
||||
`redbear-authd`, `redbear-greeter`, `cub`) live as local recipes under
|
||||
`local/recipes/<category>/<name>/source/`. These are 100% Red Bear original code
|
||||
and are the only authoritative implementation.
|
||||
|
||||
---
|
||||
|
||||
## The Five Rules of Local Fork Supremacy
|
||||
|
||||
### Rule 1: Local Fork First, Always
|
||||
|
||||
**If a component has a local fork, EVERY dependency on that component MUST route
|
||||
through the local fork. Period.**
|
||||
|
||||
- ✅ `path = "../../../../local/sources/<fork>"` in any Cargo.toml
|
||||
- ✅ `[patch.crates-io]` entry pointing at the local fork
|
||||
- ❌ Version-string dependency (`redox_syscall = "0.9"`)
|
||||
- ❌ Exact-pin dependency (`redox_syscall = "=0.9.0"`)
|
||||
- ❌ "Let's just use crates.io for now, we'll fix it later"
|
||||
- ❌ "Upstream has a newer version, let's skip our fork this once"
|
||||
|
||||
**Crates.io may resolve the SAME crate independently of our fork, creating two
|
||||
copies in the build graph and producing silent ABI mismatches.** This is not a
|
||||
hypothetical risk — it has caused real, hard-to-debug build failures in Red Bear
|
||||
history. The path dependency eliminates the ambiguity entirely.
|
||||
|
||||
### Rule 2: Local Fork Complete, Not Partial
|
||||
|
||||
**A local fork must implement everything Red Bear OS needs from that
|
||||
component. If the fork is missing functionality, implement it IN THE FORK.**
|
||||
|
||||
There is no acceptable shortcut where our fork "defers to upstream" or "passes
|
||||
through to crates.io" or "re-exports from the system version". A fork that
|
||||
delegates is not a fork — it is a wrapper.
|
||||
|
||||
When the kernel needs a syscall (`SYS_SETRENS`, `SYS_SETNS`, `SYS_MKNS`) and the
|
||||
upstream Redox kernel does not implement it, the Red Bear kernel fork MUST
|
||||
implement it. The relibc fork MUST route through it. The syscall crate fork
|
||||
MUST define the number. No layer says "we don't support this, use a different
|
||||
API". We adapt our entire stack to be complete.
|
||||
|
||||
Concrete example from this very session: the `setrens(0, 0)` call in
|
||||
`local/sources/base/init/src/main.rs:192` panicked on bare metal because the
|
||||
`SYS_SETRENS`/`SYS_SETNS`/`SYS_MKNS` namespace syscalls were not implemented in
|
||||
the kernel fork. The fix is not to make init tolerate the failure (a workaround)
|
||||
— the fix is to implement the missing functionality in the kernel fork properly.
|
||||
|
||||
### Rule 3: Adapt to Upstream, Never Pin Back
|
||||
|
||||
**When upstream Redox changes a dependency version, API, or ABI, Red Bear
|
||||
adapts — fully.**
|
||||
|
||||
- Upstream bumps `redox_syscall` to `0.9` → every Red Bear crate that touches
|
||||
`redox_syscall` moves to `0.9` — we fix our code, not theirs.
|
||||
- Upstream renames a type → our fork renames it too (or wraps it with a compat
|
||||
layer inside the fork).
|
||||
- Upstream drops a function we depended on → we either reimplement it or
|
||||
re-architect around it. We never pin back.
|
||||
|
||||
This is not just convenience. It is the long-term maintenance contract that
|
||||
keeps the fork healthy: as upstream evolves, our local forks track and adapt
|
||||
**at the same pace**. Pinning back creates "dead branches" of the fork that
|
||||
accumulate divergence and become impossible to rebase.
|
||||
|
||||
### Rule 4: Single Source of Truth Per Concept
|
||||
|
||||
**If two places in Red Bear OS can answer the same question, we have a bug.**
|
||||
|
||||
The local fork is the single source of truth for how Redox syscalls work, how
|
||||
relibc functions behave, how the kernel allocates memory. There is exactly one
|
||||
implementation, in exactly one tree, and every other tree imports from it.
|
||||
|
||||
This is why we use path dependencies and `[patch.crates-io]`. This is why we
|
||||
ban "stub" implementations that pretend to provide an interface. This is why
|
||||
we remove duplicate files (e.g. the `redox.patch` symlinks from before we
|
||||
switched to local forks — see `local/AGENTS.md` § LOCAL FORK MODEL).
|
||||
|
||||
If a feature appears in both the local fork and in some "in-tree copy", the
|
||||
"in-tree copy" is dead code and must be removed.
|
||||
|
||||
### Rule 5: Document the Fork Boundary
|
||||
|
||||
**Every local fork commit that diverges from upstream MUST include:**
|
||||
|
||||
1. A clear commit message explaining what changed and why.
|
||||
2. The upstream commit/tag the fork is based on (in the commit body).
|
||||
3. If the divergence is significant, an entry in `local/docs/` or
|
||||
`local/AGENTS.md` describing the architectural choice.
|
||||
|
||||
This is what makes the fork auditable. An operator looking at the fork history
|
||||
six months from now needs to be able to understand: what did we take from
|
||||
upstream, what did we change, and why.
|
||||
|
||||
---
|
||||
|
||||
## What "Complete" Looks Like (Concrete Checklist)
|
||||
|
||||
For every local fork at `local/sources/<name>/`:
|
||||
|
||||
- [ ] `Cargo.toml` (or `Makefile`) exists and declares all dependencies as
|
||||
path deps to other local forks, with `[patch.crates-io]` if needed.
|
||||
- [ ] `Cargo.toml` `version` field follows the fork versioning convention:
|
||||
`<upstream-version>+rb<redbear-branch>` (e.g. `0.6.0+rb0.3.0`).
|
||||
- [ ] `authors` field credits both upstream maintainers AND every Red Bear
|
||||
contributor who has commits on the fork.
|
||||
- [ ] All functions/types/syscalls that Red Bear OS uses are implemented in the
|
||||
fork — none fall through to "unimplemented" stubs.
|
||||
- [ ] No `unimplemented!()`, `todo!()`, or `unreachable!()` in code paths Red
|
||||
Bear exercises (warnings policy from `local/AGENTS.md` applies).
|
||||
- [ ] All cross-fork references use relative `path = "..."` dependencies, not
|
||||
absolute paths or crates.io versions.
|
||||
- [ ] Source tree is tracked in git and pushed to the `submodule/<name>` branch
|
||||
on `gitea.redbearos.org/vasilito/RedBear-OS`.
|
||||
|
||||
For the syscall crate specifically:
|
||||
|
||||
- [ ] Every syscall number used by relibc MUST be defined in
|
||||
`local/sources/syscall/src/number.rs`.
|
||||
- [ ] If relibc calls a syscall that is not yet defined, ADD the syscall number
|
||||
to the syscall crate AND implement it in the kernel fork. Both edits go
|
||||
in the same work session.
|
||||
|
||||
For the kernel fork specifically:
|
||||
|
||||
- [ ] Every syscall called from relibc/redox-rt MUST have a handler in
|
||||
`local/sources/kernel/src/syscall/mod.rs` (or a sub-module it dispatches
|
||||
to). Unhandled syscalls return `ENOSYS` — which is the correct behavior,
|
||||
not a bug. But if relibc ACTUALLY uses the syscall in its startup path,
|
||||
the kernel MUST implement it.
|
||||
- [ ] No panic/abort paths in kernel code. Errors propagate via `Result`.
|
||||
|
||||
For relibc/redox-rt specifically:
|
||||
|
||||
- [ ] Every public function used by Red Bear binaries (init, bootstrap, base
|
||||
daemons, libredox wrappers) MUST be implemented. If upstream removed or
|
||||
renamed a function, we reimplement or wrap — we do not delete the
|
||||
function from the API surface.
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns (Strictly Forbidden)
|
||||
|
||||
| Anti-Pattern | Why It's Forbidden |
|
||||
|-----------------------------------------------|------------------------------------------------------------------|
|
||||
| `redox_syscall = "0.9"` (version string) | Pulls from crates.io, creates dual-copy ABI mismatches |
|
||||
| `as any` / `@ts-ignore` / `unwrap()` on fork APIs | Hides real bugs; kernel fork must not have panic paths |
|
||||
| Commenting out a fork function to fix a build | Deletes functionality; the function exists for a reason |
|
||||
| "Let's wait for upstream to add this" | We are a fork. We adapt or reimplement, we don't wait |
|
||||
| Delegating fork work to crates.io | Two implementations of the same concept = guaranteed divergence |
|
||||
| Skipping a fork submodule because "it's not used" | Every declared submodule is used somewhere in the build graph |
|
||||
| Adding an `unimplemented!()` with `#[allow(dead_code)]` | This is a stub. Stubs are forbidden (see local/AGENTS.md) |
|
||||
|
||||
---
|
||||
|
||||
## Enforcement
|
||||
|
||||
1. **Build preflight** (`local/scripts/build-preflight.sh` + `verify-fork-versions.sh`):
|
||||
- Rejects builds that use version-string deps for any Cat 2 fork crate.
|
||||
- Rejects builds where fork version labels don't match upstream base + branch.
|
||||
2. **CI gates**:
|
||||
- `sync-versions.sh --check` verifies Cat 1/Cat 2 version compliance.
|
||||
- Fork version drift is a build-blocking error.
|
||||
3. **Operator review**:
|
||||
- Any new submodule addition requires operator sign-off and a documented
|
||||
necessity case (see `local/AGENTS.md` § BRANCH AND SUBMODULE POLICY).
|
||||
- Any removal of fork functionality requires explicit user request.
|
||||
|
||||
---
|
||||
|
||||
## When This Policy Was Last Verified
|
||||
|
||||
- **2026-07-10**: Reinforced during bare-metal boot debugging. The `setrens(0, 0)`
|
||||
panic in init's `main.rs:192` was traced to missing kernel-side namespace
|
||||
syscall implementations (`SYS_MKNS`, `SYS_SETNS`, `SYS_SETRENS`). The Red
|
||||
Bear fork policy requires implementing these in the kernel fork — not
|
||||
papering over them with `.expect()` → panic bypass.
|
||||
|
||||
## Related Documents
|
||||
|
||||
- `local/AGENTS.md` — full Red Bear agent guidance (branches, submodules, durability)
|
||||
- `local/docs/SOURCE-ARCHIVAL-POLICY.md` — how sources are frozen and archived
|
||||
- `local/docs/PATCH-GOVERNANCE.md` (referenced in `local/AGENTS.md`) — how patches
|
||||
are rebased and applied
|
||||
- `README.md` (project root) — high-level description including the fork model
|
||||
Reference in New Issue
Block a user