Ontology Core as Authoritative State, NCL as Manifestation — Two-Phase Adoption

Accepted

ontoref
ADR-024 establishes the operations layer as the agent's only project-touching

Context

ADR-024 establishes the operations layer as the agent's only project-touching path. This ADR closes the remaining ambiguity ADR-024 left implicit: where does the authoritative project state actually live, and what is the role of the existing NCL filesystem (.ontology/, adrs/, reflection/).

Two competing readings have coexisted through the project's evolution:

Reading 1 (current de facto): the NCL filesystem IS the state. Rust code (ontoref-ontology, ontoref-reflection crates) parses NCL into typed structs for query. Mutations happen by editing files; git is the history.

Reading 2 (substrate plan original direction): NCL is intended to disappear (ADR-023 § decision, constraint legacy-crates-removed-at-g12). State lives in triples (G4) + commitments (G5); operations are recorded in oplog (G3). NCL was destined to become migration tool input only.

Neither reading is currently coherent with the project's lived axioms and the direction set by ADR-024. Reading 1 contradicts ADR-024 (an agent who edits .ontology/*.ncl with Edit bypasses operations entirely). Reading 2 dissolves .ontology/ which collides with the Self-Describing axiom and removes the human-legible substrate the project has invested years in.

The session of 2026-05-25 (see .coder/2026-05-25-implementation-final-stop-review_done.md) closed Reading 2 by adopting ADR-024: operations layer becomes the fin, substrate becomes the medio. This ADR closes the matching question on state location: the authoritative state lives in Rust (loaded into typed structs, committed via the substrate), and NCL is one of several manifestations rendered from that state. The plain-text legibility of .ontology/ is preserved; its semantic authority is moved to the Rust-side state.

The transition has a load-bearing constraint: it must be progressive. A big-bang switch to "NCL is read-only output" would block all legitimate human edits during the period when the operations catalog is incomplete (which is inherent to first adoption — the catalog grows dialectically with use). The decision adopts a two-phase model: Phase 1 NCL editable with parse-on-save reconciliation; Phase 2 NCL render-only when the catalog is mature.

The decision also collides with the existing axiom no-enforcement (.ontology/core.ncl, invariant=true): "There is no enforcement mechanism. Coherence is voluntary." Once operations are the only mutation path and parse-on-save can reject inconsistent edits, there IS enforcement — internal, structural, post-adoption. The axiom must evolve to reflect this distinction between voluntary adoption (preserved) and enforced internal coherence (new).

Decision

The authoritative project state lives in Rust, materialized over the verifiable substrate (ontoref-types + ontoref-triples + ontoref-oplog + ontoref-commit, G1-G10 verde). NCL files in .ontology/, adrs/, and reflection/ become one manifestation of that state — human-legible, git-versioned, but no longer the seat of semantic authority.

The transition is two-phase:

Phase 1 — C-progressive (during catalog construction)

NCL files remain manually editable. Each save to .ontology/*.ncl, adrs/*.ncl, or reflection/*.ncl triggers a reconciliation:

parse(file) → diff vs current state → emit ops to align → apply

If parse fails or diff requires an op not in the catalog, the save fails with a structured message naming the missing operation. Each failure teaches the dialectical step needed to extend the catalog.

Render from state to NCL happens after any state mutation (whether triggered by a save or by a direct ops invocation), so the NCL files always reflect the post-mutation state.

Phase 1 is the operating mode during the build-out of ADR-024's operations catalog. It coexists with the existing CLI surface; ops invocations via MCP, ops invocations via save-triggered reconciliation, and (during this phase) legacy CLI mutations via the existing ontoref CLI commands all converge on the same state.

Phase 2 — C-pure (post-catalog-maturity)

NCL files become render-only. Direct edits are silently overwritten by the next render. All mutations flow through declared operations.

Transition from Phase 1 to Phase 2 is a deliberate decision recorded as a configuration flag (.ontoref/config.ncl::ops.phase = 'Pure) plus a new ADR (likely ADR-026) documenting the maturity assessment.

Reconciliation between this decision and the existing no-enforcement axiom requires the axiom to evolve. no-enforcement is split into two axioms that preserve voluntary adoption while making explicit the enforcement that lives inside the post-adoption operation runtime:

voluntary-adoption (invariant=true) — Projects adopt ontoref by choice; the protocol never imposes itself. Adoption is opt-in per tier (tier-0 minimal NCL, tier-1 substrate, tier-2 operations).

internal-coherence-enforced (invariant=true) — Once a project adopts the operations tier, mutation of authoritative state happens exclusively through declared domain operations. The agent (human or AI) cannot bypass.

Both new axioms are added to .ontology/core.ncl as part of the pre-flight to the operations-layer implementation plan; the existing no-enforcement node is removed. This is enforced as a Hard constraint of this ADR.

Invocation surfaces (the API answer):

Operations are invoked exclusively through typed API surfaces. NCL files are NEVER the invocation channel; they are the catalog (declaration of what operations exist) and the manifestation (render of resulting state). Three API surfaces, all built on the existing ontoref-daemon authentication (ADR-005):

1. MCP (Model Context Protocol) — agents call ops via tools/call <op_id>; the tool list is dynamically populated from ontoref-ops::registry (inventory::collect of OperationEntry). Auth via Bearer session token. Existing 34 query tools coexist.

2. HTTP/REST — POST /ops/{id} with JSON body matching the op's declared inputs; response includes the witness. Auth via Bearer. Suitable for scripts, CI/CD, GraphQL bridge, and remote clients.

3. CLI — ./ontoref ops <id> --json '{...}' (alternatively positional args resolved by the op's input schema). Same dispatch path as MCP and HTTP under the hood.

All three surfaces converge on ontoref-ops::dispatch(op_id, inputs) which loads the precondition slice, validates, executes, and emits the witness. In Phase 1, save-to-NCL is a fourth implicit invocation channel that goes through parse → diff → emit_ops → dispatch. In Phase 2, save-to-NCL has no effect on state.

Crate consequences:

- ontoref-ontology stays — its role shifts from "NCL parser" to "NCL ↔ state bridge" (parse-to-ops in Phase 1; render-from-state in both phases). - ontoref-reflection stays — modes execute against state queries (via ontoref-query L5) rather than parsing reflection/modes/*.ncl on each call. - ontoref-ops (new crate, plan's D2) is the operation runtime; it composes ontoref-ontology (for render/parse), ontoref-triples (for state), and ontoref-commit (for witnesses). - ontoref-daemon adapts to expose POST /ops/{id} routes and integrates ops into the MCP tool list; no rewrite required. - The legacy-crates-removed-at-g12 constraint in ADR-023 is invalidated by this ADR and is to be removed when ADR-023 transitions to Accepted (post O3 of the operations-layer plan).

Constraints

  • Hard .ontology/core.ncl MUST replace the no-enforcement node by two new axiom nodes: voluntary-adoption and internal-coherence-enforced (both invariant=true). The no-enforcement id MUST NOT remain in the nodes array.
  • Hard Rendering state → NCL MUST be byte-stable: render(state, file_path) produces identical bytes on repeated invocations for the same state, and parsing the rendered file MUST yield a state equal to the source state.
  • Hard .ontoref/config.ncl MUST declare an ops.phase field with value 'Progressive | 'Pure once ADR-025 is Accepted. Default value 'Progressive applies until a follow-up ADR records the Phase 2 transition.
  • Hard This ADR engages and synthesizes the named tensions ontology-vs-reflection, formalization-vs-adoption, and the to-be-split no-enforcement axiom; the synthesis state and direction of motion MUST be discoverable in the decision and rationale of this ADR.

Alternatives considered

  • Reading 1: keep NCL as authoritative state, ADR-024 enforcement applied only when operations existrejected: This is the current state. The H7 diagnosis demonstrates it does not work: agents bypass NCL because direct file edit is structurally cheaper than consulting via MCP. The ADR-024 commitment to operations-as-only-path is incoherent if the underlying state is still file-edit-accessible. The reading collapses ADR-024 into a recommendation rather than a constitutive constraint.
  • Reading 2: dissolve NCL completely per the original substrate planrejected: Closing the human-legible substrate breaks Self-Describing's lived form (the directories ARE the description, not an optional rendering). It also abandons years of investment in NCL schemas, contracts, and onboarding tooling. The H7 dialectic showed the problem was not NCL-the-substrate but the missing acting-binding to NCL — the substrate is fine; what was missing was the mutation discipline. Dissolving NCL is over-reach.
  • C-pure from day one (state in Rust, NCL render-only immediately)rejected: Big-bang transition during catalog incompleteness blocks legitimate work. Every mutation that has not yet been catalog-modeled is a hard block on the editor. Reasonable extensions (a new tension name in a new ontology node, a new ADR with a yet-unmodeled constraint type) become catalog-extension dialectics before they can be expressed. The friction kills adoption velocity precisely when adoption velocity is needed to mature the catalog.
  • C-progressive permanent (parse-on-save without ever reaching pure render-only)rejected: Stops at the half-way point. The internal-coherence-enforced axiom is only partially satisfied — humans can still mutate via save, ops are not the unique path. The cryptographic verifiability that ADR-024 promised (witness-of-mutation cannot be fabricated) is undermined by the existence of an alternative mutation channel. The model is functional but incomplete; Phase 2 must be the eventual target even if its trigger is far in the future.
  • Keep no-enforcement axiom unchanged; treat ADR-024 + ADR-025 as conscious axiom violations documented per caserejected: Encourages the protocol to drift from its declared axioms while the axioms remain frozen as aspiration. Future readers of core.ncl would see no-enforcement as truth while the runtime structurally enforces. The protocol's self-description must remain accurate; an axiom that no longer describes the runtime is not an axiom, it is a wish. The split makes the lived position legible.

Related ADRs

ADR-001 · ADR-007 · ADR-009 · ADR-018 · ADR-020 · ADR-023 · ADR-024

Was this useful? Rate it
Got something to add? Tell me what you think, what you'd suggest, or whether we should keep exploring this topic.
· reads

We use cookies to help this site function, understand service usage, and support marketing efforts. Cookie Policy for more info.