A routed operation resolves by PRESENCE, not by a declared strategy — and every derived resolution is reported under a name that says it was derived

Accepted

ontoref
ADR-018 gives mode resolution a rule: a mode declares a `strategy` ('Delegate / 'Override /

Context

ADR-018 gives mode resolution a rule: a mode declares a `strategy` ('Delegate / 'Override / 'Merge / 'Compose), and where a level ≥2 declares none, the absence is read as implicit Delegate with a Soft warning. That rule is right for modes, and its rightness depends on a property modes have: a mode file present at a level is AMBIGUOUS — it may be the workflow this level runs, or a copy inherited and unmodified — so the declaration alone cannot say, and the strategy field is what says.

MEASURED 2026-08-14, while building `biop resolve` against `mode resolve` as its precedent.

· THE BIOP ENVELOPE HAS NO `strategy` FIELD AT ALL. `reflection/schemas/biop.ncl` declares `Biop = { nucleus | Nucleus, routes_from, routes_to }` and the record contract is CLOSED — verified by execution: an extra field is an export error, and so is one smuggled into `nucleus` through the wrapper. The field was deferred deliberately (see the schema header): a strategy resolves a disagreement, no second tissue had yet produced one, and shipping a dead field to twelve consumers is the `catalog/` failure this project already measured — materialised and EMPTY in 10 of 11 consumers.

· SO "ABSENT" DOES NOT MEAN WHAT IT MEANS FOR A MODE. For a mode, absent means the author did not say. For a biop it means THERE IS NOWHERE TO SAY IT. Carrying ADR-018's reading over would make every declaration at a level ≥2 delegate upward, and since only the Base level would ever answer, NO CONSUMER PROJECT COULD IMPLEMENT A BIOP LOCALLY — the mechanism would be inert in the eleven places it exists to reach, while reporting a perfectly ordinary resolution.

· THE CHAIN ITSELF CANNOT CURRENTLY READ A DECLARED LEVEL. Found in the same session and filed as bl-076: `manifest-path-at` probes `<root>/.ontoref/ontology/manifest.ncl` while `derive-level-chain` starts at `adr-root`, which already returns `<project>/.ontoref`. The lookup double-prefixes, finds nothing, and `load-level-info-at` returns its no-manifest fallback. `manifest.ncl:6` declares `level = { index = 'Base, name = "ontoref-base" }`; the chain reports `name: [".ontoref"]`, a `path basename`. On this tree the fallback and the declaration AGREE, which is why it went unnoticed. Traversal is therefore verified at depth 1 only, and this ADR must not pretend otherwise.

Decision

For routed operations, PRESENCE ANSWERS: a biop file present at a level resolves there and traversal stops; absence delegates to the parent; and when no level in the chain declares it, the protocol's shipped set (`reflection/biop/`) answers as the terminal rung.

Every resolution produced this way is REPORTED UNDER A NAME THAT MARKS IT AS DERIVED — `presence-Override`, `absence-Delegate`, `shipped-inherited` — never under ADR-018's declared names. A reader must be able to tell, without reading the file, whether a resolution was stated by an author or inferred by a resolver.

`unresolved` is a distinct outcome from a refusal, and says so: an id that no rung declares reports «nothing would run», not «this was rejected».

This rule is SCOPED TO ARTIFACT CLASSES WITH NO STRATEGY FIELD. It does not amend ADR-018 and does not apply to modes. If `strategy` later lands on the envelope — its declared trigger is a second tissue disagreeing about resolution — a declared strategy takes precedence over presence, and the derived names stop appearing for the ids that declare one.

Constraints

  • Hard A resolution the resolver inferred is reported under a name that marks it as inferred, never under a declared strategy name.
  • Hard When no level in the chain declares an id, the shipped set answers; the resolver never tells a consumer to copy a file in order to resolve.
  • Soft An id no rung declares is reported as unresolved, distinctly from a claim that was evaluated and rejected.

Alternatives considered

  • Carry ADR-018's rule over unchanged: absent strategy at level ≥2 means implicit Delegate.rejected: No consumer could ever implement a biop locally — every declaration would delegate upward and only Base would answer. The mechanism would be inert in eleven projects while reporting an ordinary resolution, which is worse than an error.
  • Add `strategy` to the envelope now, so resolution is always declared.rejected: A strategy field resolves a disagreement, and no second tissue has produced one. Shipping it now means twelve consumers inherit a field with no case behind it — the empty `catalog/` skeleton this project already materialised in 10 of 11 of them. The field is deferred WITH a declared trigger rather than omitted.
  • Report derived resolutions using ADR-018's names (`Override`, `Delegate`).rejected: It would make an inference indistinguishable from a declaration. The whole reason the field could be deferred safely is that its absence stays visible; identical names would hide exactly the debt that justifies the deferral.
  • Fix bl-076 first and resolve strictly through declared levels.rejected: bl-076 governs ADR-018/045 semantics for modes, `validate modes` and co-tenancy scope; fixing it from inside this work would change level resolution for surfaces this decision does not own. Filed with its measurement and its undecided fix shape instead, and the coverage limit is declared here rather than hidden.

Anti-patterns

  • Applying one class's resolution rule to another because both traverse the same chain — Two artifact classes share a level chain, so the rule written for one is assumed to govern the other. The tell is a rule whose justification depends on a field the second class does not have — here, «absent strategy means Delegate» applied where there is nowhere to declare a strategy. The result resolves plausibly and runs nothing.
  • A resolver presenting its own inference in the vocabulary of a declaration — A resolution derived from file presence, directory layout or ordering is reported using the names reserved for authored declarations. The reader cannot distinguish what someone decided from what the tool guessed, and a deferred field stops being visible as debt.

Related ADRs

ADR-018 · ADR-045 · ADR-087 · ADR-089 · ADR-049

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.