Level Hierarchy and Mode Resolution Strategy — Observable Boundary Traversal
Accepted
Context
ADR-012 introduced a domain extension system that enables project-specific specialization of ontoref. In practice this created a three-level hierarchy: (1) ontoref base — generic protocol and reflection operations; (2) project domain — specialization of ontoref for a specific project type (provisioning, personal, ...); (3) domain instance — a concrete project derived from a domain (a workspace, an infra, a team ontoref). The hierarchy was not formalized: no level declares its identity, no mode declares whether its implementation is complete or delegates to the level above, and no mechanism exists to determine which level answered a given operation. The result is implicit traversal — a caller invoking 'build-docs' has no way to know whether the operation ran at level 1, 2, or 3, whether it merged contributions from multiple levels, or whether it silently fell through to the base because the domain had not implemented it yet. The discussion that produced this ADR also identified that resolution strategies cannot be uniform: the correct strategy depends on the mode, the project context, and the adoption phase. A domain in early adoption may Delegate all documentation modes to the base; the same domain at maturity Overrides them with project-specific implementations. The transition between strategies must itself be observable — crossing a level boundary is an architectural event, not an implementation detail.
Decision
Formalize the three-level hierarchy with four mechanisms: (1) Level identity declaration — each ontoref instance declares level.index (1=base, 2=domain, 3=instance), level.name, and level.parent (name of the level above; absent at level 1) in manifest.ncl. This makes level identity explicit and queryable. (2) Per-mode resolution strategy — each reflection mode declares a strategy field using one of four values: 'Override (implementation is complete at this level, traversal stops), 'Delegate (no implementation here, traverse to parent), 'Merge (accumulate fields bottom-up across all levels; lower level wins on conflicts), 'Compose (declare explicit partial inheritance via an extends field naming specific steps or fields to inherit from the parent). Strategy is required at level 2+; absent at level 1 (base is always Override by definition). Implicit absence at level 2+ is treated as 'Delegate with a Soft validation warning. (3) FSM-bound strategy transitions — when a mode's strategy is expected to change as the project matures, declare a state dimension in state.ncl whose current_state tracks the strategy value. The transition from 'Delegate to 'Override (or any other pair) is then an observable FSM event: tracked in state.ncl, visible in ontoref describe state, and triggerable by the same transition conditions as any other dimension. Strategy changes are architectural events, not silent refactors. (4) Observable traversal via ontoref mode resolve — the command 'ontoref mode resolve <id>' reports which level answered the operation, the strategy applied, the source file, and the reason (declared strategy or FSM state). No mode resolution is ever silent.
Constraints
- Hard Any ontoref instance at level 2 or 3 must declare level.index, level.name, and level.parent in manifest.ncl
- Soft Every reflection mode at level 2+ must declare strategy explicitly; implicit absence is a Soft violation
- Hard No Delegate chain may terminate without an Override at some level — every mode invocation must resolve to a concrete implementation
- Soft If state.ncl declares a dimension for a mode's strategy, its current_state must match the strategy field declared in the mode NCL
- Hard A mode with strategy = 'Compose must declare an extends field; every step or field reference in extends must exist in the named parent mode
Alternatives considered
- Implicit inheritance — current state, no declaration required — rejected: Makes level traversal invisible. Coupling is undiagnosable. Adoption phase is unknown. Boundary crossing is unobservable. This is the state that produced the architectural confusion this ADR resolves.
- Global strategy per level — one strategy applies to all modes at a given level — rejected: Domains specialize incrementally. A global Override for level 2 blocks early adoption (all modes must be implemented before any work). A global Delegate for level 2 makes domains invisible (nothing is ever implemented). Per-mode strategy reflects the real adoption curve.
- Override-only — levels either implement fully or do not appear — rejected: Eliminates phased adoption. A domain cannot exist in the system until it has implemented every mode — a steep entry cost that contradicts ADR-001 (voluntary coherence, no enforcement). Delegate and Compose are specifically needed for incremental adoption.
- Separate level.ncl file instead of level field in manifest.ncl — rejected: manifest.ncl already holds structural self-description (requirements, capabilities, registry topology, layers). Level identity is structural metadata of the same kind. A separate file adds coordination overhead (two files to keep in sync, two files for describe to read) for no additional expressiveness.