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:
2026-07-10 16:08:50 +03:00
parent 7b6e83f14b
commit f3269b414c
2 changed files with 272 additions and 0 deletions
+244
View File
@@ -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