Tier Coexistence as Permanent Design — Strictly Additive Stack, Offline-First, No Forced Migration
Accepted
Context
ADR-024 (operations as the agent's only project-touching path) and ADR-025 (ontology core as authoritative state, NCL as manifestation) introduce a new mutation discipline grounded in the verifiable substrate (ADR-023). ADR-025 also performs the axiom split that replaces `no-enforcement` with `voluntary-adoption` (invariant=true) and `internal-coherence-enforced` (invariant=true). The split is precise: external adoption remains opt-in; internal coherence is enforced only AFTER adoption of the operations tier.
Across the implementation of the operations-layer plan (gates O-pre → O7, session 2026-05-26), an observed ambiguity arose in operational decisions: the ontoref piloto self-host has migrated to operations as authoritative (tier-2, Phase 1), but every other onboarded project (mirador, provisioning, lian-build, vapora, stratumiops, kogral, libre-{daoshi,wuji,forge}, secretumvault, typedialog, jpl-website, personal-ontoref, jpl-ontoref, cloudatasave, build-in-layers, DD7pasos, librosys, forge-fleet, mina) still relies on NCL-authoritative state with file-edit mutation. Questions recurred during the session — "are we still going to support NCL?", "did we migrate to Rust?", "do the tiers have the same platform requirements?" — that surfaced an unstated design property: the protocol supports multiple authoritative-state modes simultaneously, by construction, and indefinitely.
The plan itself states this in three places without naming it as an architectural property worth its own ADR:
- §1: "Piloto: ontoref mismo (self-hosted). Phase 2 queda fuera de scope." - §7 R11: "Phase 1 → Phase 2 nunca llega: Aceptable resultado intermedio. No-transición es estado válido." - §8: "Expansión a proyectos no-ontoref … requieren cada uno su sesión de catálogo." (per-project decision, no global migration)
Without an ADR naming the property, future sessions risk interpreting the migration as transitional and removing the tier-0/1 paths once the piloto stabilises. Doing so would silently violate the `voluntary-adoption` axiom and break the 18 onboarded projects that chose minimal NCL as their adoption surface. The session that produced this ADR included a sub-bug — the daemon's `global_schemas_path()` did not expose the data-dir schemas, so consumer projects in tier-0/1 got WARN-logged `config_surface not loaded` errors. The fix (`global_schema_dirs()` returning multiple paths) was structurally correct because it preserved tier-0/1 operability — but it would have been incorrect (silent migration coercion) if the design were transitional. This ADR records the property so the fix-or-not decision is explicit at every future point.
Decision
The protocol supports three authoritative-state modes simultaneously and indefinitely, as the canonical operational surface:
tier-0 — minimal NCL adoption. Authoritative state: `.ontology/*.ncl`, `adrs/`, `reflection/` as files. Mutation: file edit (any editor). Stack: `nickel` binary + optional `ontoref` Nushell CLI. No daemon required for the project's own correctness; the daemon may serve the UI and describe queries if installed.
tier-1 — substrate adopted. Authoritative state: NCL files PLUS the substrate's commit layer + oplog. Mutation: file edit + reconcile (substrate ingests NCL on read). Stack: tier-0 + `ontoref-daemon` binary + G1-G10 substrate crates. Cryptographic: blake3 (commitment root). Cross-instance verifiability via state_root + witness, but no domain operations.
tier-2 — operations adopted. Authoritative state: Rust substrate (commit_layer + state_root). NCL is manifestation rendered from state. Mutation: dispatch_op exclusively (MCP, HTTP, CLI, or save-on-NCL reconciler in Phase 1). Stack: tier-1 + `ontoref-ops` + `ontoref-derive` + actor Ed25519 keypair. Cryptographic: blake3 + Ed25519 (witness signing). `internal-coherence-enforced` axiom applies HERE — operations are the only mutation path. Below tier-2 the axiom does not apply (the project hasn't adopted the operations tier; coherence remains voluntary).
The three modes coexist permanently. There is no scheduled transition. A project may stay at tier-0 indefinitely. A project may climb to tier-1 or tier-2 voluntarily, on its own schedule, project by project. The ontoref piloto runs at tier-2 / Phase 1; every other onboarded project chooses its own tier independently.
Within tier-2, the Phase 1 → Phase 2 transition (NCL render-only, save-reconciler off) is also opt-in per project and recorded in `.ontoref/config.ncl::ops.phase`. The plan documents that Phase 2 may never be reached — that outcome is valid by design.
Stack requirements are strictly additive: tier-N includes everything tier-(N-1) needs plus its own delta. No tier requires network access or external services for correctness. Optional features (`db`, `nats`, `ui`, `mcp`, `graphql`, P2P sync backends, S3/OCI blob backends) are orthogonal to tier — any project at any tier can opt into any feature independently. Network-using features (real sync, remote blobs) are trigger-based per ADR-027 / ADR-026 / D23: activated only when documented thresholds are exceeded.
The daemon binary serves all tiers from a single build. Per-project configuration declares the tier; the daemon dispatches against the declared mode. Migration of an individual project is a per-project deliberate act, not a protocol upgrade.
Constraints
- Hard The protocol MUST NOT include any mechanism that automatically migrates a project from one tier to another without an explicit per-project decision recorded in `.ontoref/config.ncl`. Migration aids (templates, documentation, CLI helpers) are permitted; automatic mutation of a project's tier is not.
- Hard The daemon's NICKEL_IMPORT_PATH MUST resolve canonical schema imports (`manifest`, `core`, `state`, `gate`, `ontoref-project`) for tier-0/1 projects. The resolution MUST include `$data_dir/ontology/schemas/` and `$config_dir/schemas/`.
- Hard The default build of `ontoref-daemon` (any features active) MUST operate correctness-wise without network access. Optional sync backends (SyncBackend impls beyond FilesystemSync) and blob backends (BlobBackend impls beyond LocalFilesystemBlobs) MAY require network when activated, but the default substrate MUST NOT.
- Hard Changes to ontoref-ops, ontoref-derive, ontoref-ontology-content, or the substrate crates MUST NOT remove a public API that tier-0/1 consumers rely on. Schema additions are permitted; schema removals require a new ADR documenting the deprecation path.
- Hard Tier transitions (upgrade tier-N → tier-N+1 OR downgrade tier-N → tier-N-1)
MUST refuse to execute when any protocol migration is pending. Specifically:
`ontoref migrate pending` MUST return an empty list before any change to
`.ontoref/config.ncl::ops.tier` is accepted by the daemon.
Enforcement layers:
- The future
transition_tieroperation (to be defined alongside ADR-033) declares this constraint as a synchronous pre-validator. - Until
transition_tieris implemented, the daemon’s ConfigWatcher enforces the same precondition: any mutation ofops.tierin.ontoref/config.ncltriggers a guard that consults the project’s migration store. When pending migrations are detected, the daemon emits a Block-severity notification, refuses to swap the config in its registry, and the project remains at its current tier value.
This is a forward declaration: the constraint exists at the protocol-semantic level (ADR-029) before its operational materialisation (ADR-033). The daemon-side enforcement closes the operational gap immediately so manual
.ontoref/config.ncledits cannot bypass the rule during the period between this amendment and ADR-033’s authorship. - The future
Alternatives considered
- Schedule a transition: all projects migrate to tier-2 by date X — rejected: Violates voluntary-adoption (invariant=true) by construction. Forcing migration would require superseding ADR-025 with a new axiom — and no empirical evidence motivates that change. The 18 onboarded projects chose tier-0/1 deliberately; revoking that choice is hostile to ecosystem participants. Also: there is no engineering need — the substrate G1-G10 already coexists with NCL ingestion and the cost of supporting both is dwarfed by the cost of forcing migration on consumer teams.
- Pick one mode (NCL-authoritative OR Rust-authoritative) and deprecate the other — rejected: Both modes have first-class users. Documentation-heavy projects need NCL editability with low ceremony. Multi-actor projects need cryptographic witnesses. Pruning either side narrows the protocol's value. The plan itself preserved this — Phase 2 is opt-in not mandatory, tier-2 is opt-in not mandatory. Codifying that choice as an ADR makes it stable.
- Leave the property implicit in plan §7 R11 and §8 without a dedicated ADR — rejected: The implicit-in-plan path produced confusion during the implementation session itself ('did we migrate?', 'why are we fixing NCL paths?'). If the design intent was unclear at the moment of implementation, it will be unclear at every future session. The cost of one ADR is small; the cost of repeated rediscovery is large.
- Define an even finer tier ladder (tier-0.5, tier-1.5, etc.) for fractional adoption — rejected: The three-tier model already maps to the three distinct value propositions (typed records / cryptographic state / verifiable ops). Sub-tiers would multiply the support matrix without adding qualitatively new modes. If a use case emerges that genuinely needs an intermediate tier, a future ADR can extend the ladder; pre-emptively splitting it is over-design.
- Force tier-2 only for new projects, leave existing 18 on tier-0/1 — rejected: Splits the ecosystem into 'old protocol' and 'new protocol' permanently — every cross-project feature has to handle the discontinuity. Permanent coexistence within a single protocol is cleaner: every project chooses its tier, every tier interoperates with the others (within voluntary-adoption limits), and the design admits no temporal cohort.
Anti-patterns
- Formalizing during the fire (or under perpetual firefighting) — Treating ontoref as something to build or update mid-incident, when latency-to-action dominates — or adopting it at all when the work is perpetual firefighting or throwaway (a spike you will delete). The types, flows, and definitions become pure tax: they stop you to understand something you will not revisit. The resolution is not 'never stop' but the distinction between stopping-to-build and stopping-to-read: introspection is time-shifted, not eliminated. You pay in the calm and spend the dividend in the fire — a `describe impact` / `describe constraints` read costs seconds and tells you which pipe not to cut. Harm occurs only when you try to formalize during the fire, or when no calm window ever exists.
- Tier-0 false certainty — form outruns truth, no witness to anchor — A well-typed NCL graph looks authoritative even when it is stale or aspirational; the crispness of the form lends unearned credibility to the content. A desired_state can read as a real rudder when it was only a wish. Below tier-2 this harm is undefended: a pretty stale graph misleads exactly like a stale README — worse, because the README does not feign rigor. The witness (tier-2) is the only thing that anchors certainty to something independently verifiable; without it, ontoref can manufacture false certainty.
- Ceremony capture — feeding the protocol instead of the work — Adoption degrades into satisfying the protocol — updating nodes, writing ADRs, contenting validators — rather than doing the work the protocol was meant to serve. The tail wags the dog. This is the Yang collapse of formalization-vs-adoption: maximal formalization imposed where it does not pay back.
- Premature formalization — freezing an unstable identity — Typing something whose identity is not yet stable freezes a guess. Declaring invariants over what is not yet invariant — a pre-product-market-fit product, a person in genuine transition — ossifies what should stay fluid and makes the inevitable change expensive. Form applied before the substance has settled is rigidity, not coherence.
Related ADRs
ADR-001 · ADR-020 · ADR-023 · ADR-024 · ADR-025 · ADR-026 · ADR-027 · ADR-028