docs/architecture/forward-compat-invariants.md.The 8 patterns
FCI-001: Kernel traits upfront, implementations deferred
FCI-001: Kernel traits upfront, implementations deferred
nika-kernel at v0.90 for subsystems that ship later.
Default methods return Err(Unsupported) until real implementations land.MemoryStore, EmbeddingProvider, ToolExecutor,
WasmPluginHost, MetricsExporter + TracerProvider + AuditSink +
EventSink + BillingSink, Sandbox. Stubs in src/plugin/, src/infra/.Impact: v0.95 lights up the memory subsystem by adding nika-memory-oxigraph that implements
MemoryStore. Zero v0.90 code modification.FCI-002: `#[non_exhaustive]` on every public struct, enum, error variant
FCI-002: `#[non_exhaustive]` on every public struct, enum, error variant
#[non_exhaustive] so adding a field or variant
is always additive. new() constructors provide ergonomic construction
without exposing struct literal syntax.#[non_exhaustive] struct ships a new() constructor.FCI-003: Single version marker per on-disk artifact
FCI-003: Single version marker per on-disk artifact
nika: v1 (a
single version marker · the language name as key, the contract version
as value · supersedes the older K8s apiVersion: and schema: nika/workflow@X forms). Parsers reject any other value (v1.0 · v2 …)
with a clear error.v1 is the only value for the lifetime of the v1 contract: minor
additions are additive and never change it. There is no nika: v2 —
ever: pre-1.0 grammar changes happen inside v1 (the pre-1.0 stability
contract) · after engine 1.0.0 the grammar is additive only.
Non-workflow artifacts (package manifests, events) carry their own
schema version field.FCI-004: Extension namespaces (`nika.*` core + `x-*` community)
FCI-004: Extension namespaces (`nika.*` core + `x-*` community)
nika.* for core additions
(authoritative, versioned) and x-* for community overlays (no
stability guarantee, no collision with core). Scope note · in the
workflow language both are RESERVED, not current: unknown top-level
fields are rejected at v0.1, and the tool namespace set is closed at
nika: / mcp: (engine-specific tools route through mcp:). The
live x-* surface today is the pck registry.FCI-005: Reserved error code ranges
FCI-005: Reserved error code ranges
NIKA-<NAMESPACE>-<NNN> taxonomy ( registered
codes across namespaces ·
reference/error-codes). Namespaces own
001-099 ranges, codes are never repurposed. The ENGINE-INTERNAL
registry (nika_error::codes · NIKA-1000+ blocks per L1 effect
crate) is diagnostics machinery: reserved per crate, never
reassigned, and never leaked into workflow-visible errors.FCI-006: Sealed traits for core, open traits for extension
FCI-006: Sealed traits for core, open traits for extension
Provider, EventSink, Sandbox) are
sealed via a private supertrait: only Nika-owned crates can
implement them. Extension traits (e.g., MemoryStore) are open for
community crates to implement.FCI-007: Feature flags with stable defaults
FCI-007: Feature flags with stable defaults
feature flag, OFF by default. After 1-2 minor
cycles of stability, flips to default-ON. Never remove a default feature
(breaking change): deprecate and redirect.FCI-008: Public API discipline via CI
FCI-008: Public API discipline via CI
cargo public-api: detects API surface changes per crate.cargo semver-checks: verifies SemVer compatibility.cargo deny: enforces license + advisories + layer bans.
.public-api.json
snapshots are checked into the repo per crate.The 5 locked decisions (v0.80)
The 10 rules
cargo public-api +
cargo semver-checks catch violations before merge.- All public types carry
#[non_exhaustive]. - All public structs with
#[non_exhaustive]ship anew()constructor. - All
asynctrait methods usetrait_variant::makefor dynamic dispatch. - All core traits seal via private supertrait.
- All error types expose a
NIKA-XXXcode viaNikaError::code(). - All on-disk schemas carry
schema: "nika/<kind>@<major>". - All DTO fields reserve forward-compat extension slots (see FCI-010).
- All feature flags default to a stable subset: new caps ship OFF.
- All public API changes emit a
cargo public-apidiff in the PR. - All breaking changes require ADR Accepted before merge.