ADR-028
Forward-compat API-type reservation (amended — feature scheduling dropped)
accepted · 2026-04-16 · L0 L0.5 · cites 14
0ADR-028: Forward-compat reservation policy — seams now, crates later #
Context #
The swarm-3 SOTA audit (rust-architect + rust-pro + web-researcher, 2026-04-16,
HEAD 5e810a94a) closed a recurring strategic question:
"Puisqu'on refait Nika from scratch, est-ce con de différer Cortex memory à v0.95 et WASM plugin à v0.100 ? Faut-il tout planifier dès v0.90 ?"
The temptation is real: if every crate is rewritten clean, why not fully bake the
v0.95 Cortex orchestrator (nika-memory + 8 satellites) and v0.100 WASM plugin
host (nika-wasm-host + nika-sandbox) right now, so nothing ever needs to
change? This ADR closes the question.
Decision #
Two-tier forward-compat policy. Seams are mandatory at v0.90; crate implementations are deferable to their named v-target.
Tier 1 — SEAMS (mandatory, ship at v0.90) #
A seam is the smallest API surface that fixes the shape of a future feature without committing to its implementation. Seams must land before v0.90:
- Seam mechanism · Where in Nika today
- `#[non_exhaustive]` on every public enum + struct · All L0 + L0.5 crates (INV-019, ADR-007)
- Reserved trait stubs in kernel (`MemoryStore`, `WasmPluginHost`, `Sandbox`, `EmbeddingProvider`, `ToolExecutor`, `ObservabilitySink`) · `crates/nika-kernel/src/` (Wave 2 S1-B)
- Reserved sub-enums in event log (`EventKind::Memory` v0.95, `EventKind::Plugin` + `Wasm` v0.100) · Q4 + Q6 of `l0-l05-architecture-decisions.md`
- Reserved fields on Infer{Request,Response} + MemoryFrame · Wave 2 S1-B + S1-C
- `pub fn new()` constructors blocking struct-literal syntax · Every `#[non_exhaustive]` struct (Q-pattern)
- `Extension { ns, name, payload }` escape hatch on `EventKind` · Q6 (Pattern B sub-enums)
Seams are cheap to add now, ruinous to retrofit later (Hyrum's Law: every
observable behaviour gets depended on). They cost a few lines of #[non_exhaustive]
and Reserved { version } placeholders. Their absence creates breaking changes
at every v-tick.
Tier 2 — CRATE IMPLEMENTATIONS (deferable to named v-target) #
A crate implementation is the actual code that satisfies a seam. Crate implementations are deferred until at least one of:
- A user signal demands the feature (real workflow, not speculation).
- The upstream design space stabilises (e.g. WASM Component Model Preview 3).
- A sibling Diamond crate would benefit immediately (≥2 consumers, Q2 rule).
Specific deferrals locked by this ADR:
v0.95 Cortex memory — KEEP-DEFER #
- Seams already shipped:
MemoryStore,EmbeddingProvider,MemoryDirectiveonInferRequest,MemoryFrameRefonInferResponse,EventKind::Memoryreserved sub-enum. - Crates deferred:
nika-memory(L2 orchestrator) + 8 satellites (nika-memory-{hnsw,bm25,rrf,fsrs,rdfs-reasoner,temporal,graph-algos,autodesc}). - Rationale: 8 stub crates × 15k LOC budget each = ~120k LOC of empty hulls
with zero user signal.
fsrs(forgetting curve) andrdfs-reasonerdesign spaces are open in 2026 — premature freeze guarantees a v0.95 refactor. - v0.90 deliverable: 2-3 property tests proving
MemoryStorecan host a realistic HNSW backend (no implementation, just the trait shape proof).
v0.100 WASM plugin host — KEEP-DEFER #
- Seams already shipped:
WasmPluginHost,Sandbox,EventKind::Plugin+Wasmreserved. - Crates deferred:
nika-wasm-host+nika-sandbox(L3). - Rationale: WebAssembly Component Model Preview 3 (async, streams) is still in flux upstream as of 2026-04. wasmtime tracks; wasmer + wasmi lag. Stubbing before Preview 3 stabilises = rebreak guaranteed.
- Escape hatch: if a user demands plugins before v0.100, a Lua/Starlark/wasmi
L1 crate ships under
unstable-*(FCI-007 distribution flag).
What "tout v0.100" actually means #
The user proposal — "since we rewrite from scratch, plan everything for v0.100" — is half right: every seam must be planned for v0.100 (already done). But implementations follow YAGNI (Beck) + premature-abstraction caution (Metz): abstraction without ≥3 concrete usages is debt, not investment.
SOTA evidence (cross-project survey, swarm-3) #
- Project · Future-feature pattern · Lesson
- Bevy (ECS, ~50 crates) · No reservation; deliberate breakage every 3 months. · "Break early, break often" — community accepts. Not Nika's contract (stable forward-compat · real semver toward 1.0 per ADR-002 · amended D-2026-06-20-N1).
- tokio (~10 crates) · `#[non_exhaustive]` + feature flags. `tokio-uring`, `tokio-console` = **separate crates**, not core refactors. · Isolate innovation in opt-in satellites.
- Apollo Router (~40 crates) · Tower `Layer<S>` extension points + handshake-negotiated protocols (Federation 2). · Extension points = traits + negotiation, not frozen enums.
- rust-analyzer (~70 crates) · Salsa query traits (open) + process boundaries (`proc-macro-srv`). · Process boundary buys forward-compat for free.
- wasmtime · Component Model arrived as **sister crate** (`wasmtime-component`), not a core rewrite. · New major feature = new crate, not surgery.
Pattern dominant: zero surveyed projects pre-stub crates for v+2 milestones.
All five rely on the same three levers Nika already uses (#[non_exhaustive],
features, satellite crates).
Consequences #
Positive:
- v0.90 ships with a stable kernel API surface — Cortex and WASM can land later as additive admissions without touching admitted L0/L0.5 crates.
- Zero "stub crate debt" at v0.90 (no empty 8-satellite scaffolding).
- Upstream churn (Component Model Preview 3, fsrs design) does not block Nika.
- Any departure from a reserved seam (e.g. adding a non-additive field to
MemoryStore) is a breaking change — caught bycargo public-api+cargo semver-checks(planned, P0 follow-up).
Negative:
- Reserved seams that turn out to be wrong (e.g.
MemoryStoreshape unsuitable for real backends) require a breaking trait change at v0.95. Mitigation: property tests in v0.90 (P0 follow-up) prove the seam can host a realistic backend before lock. - Documentation cost: every reserved seam needs a sentence in
forward-compat-invariants.mdexplaining the v-target and the escape hatch.
Neutral:
- Crate count target stays at 40-42 for v0.90, +9 Cortex at v0.95, +2 WASM at v0.100 = ~51-53 final. No change to ROADMAP.
Escape hatch #
Reserved kernel traits and reserved event sub-enums remain modifiable until
their named v-target. A reserved seam is not a stable contract — it is a
shape commitment. The day Cortex lands at v0.95, MemoryStore's final shape
is locked; until then, breaking changes to the trait are allowed and tracked
as affects_layers: [L0.5] ADR amendments.
This must be documented in docs/architecture/forward-compat-invariants.md
under a new §reserved-mutability section (P1 follow-up).
Related decisions #
- ADR-002 Real semver toward 1.0 (amended D-2026-06-20-N1) — context for the release model that demands reservation.
- ADR-006 Layered kernel ISP traits — defines what trait stubs reserve.
- ADR-007 Forward-compat invariants —
#[non_exhaustive]+::new()discipline. - ADR-014 Sealed kernel traits — prevents downstream impl, makes seam mutability safer.
- ADR-020 WASM plugin boundary — names the v0.100 deferral target.
- ADR-024 SOTA Rust patterns — survey context for this ADR.
🦋
read at v0.107.0 · the decision record ships with the engine