A domain PROVIDES modes to the projects that bond it — the third rung, so a component's inspection method is inherited and overridden per workspace instead of re-invented in each one
Proposed
Context
MEASURED 2026-09-05, registering a client's infrastructure in a consumer spine (diegodelgado, bonded to the `provisioning` domain). The question was ordinary and operational: WHAT MAIL ACCOUNTS DOES THIS CLIENT HAVE AND HOW MUCH DO THEY CONSUME.
· No declaration anywhere holds the answer, and correctly so: mailboxes and aliases are created at runtime through an admin panel and live inside the mail pod's PVC. Writing the inventory into a `.ncl` would freeze a measurement — an occupancy figure with no measurement date is a claim that ages without saying so. The answer has to be a PROCEDURE that re-measures, which in this protocol is a mode.
· THE MODE HAS NOWHERE TO LIVE THAT MATCHES WHAT IT IS. `shared.nu::all-mode-files` globs exactly two rungs — the shipped `(tool-root)/reflection/modes/` and the project's own `.ontoref/reflection/modes/`. The mode is not the protocol's: it knows about docker-mailserver. It is not the project's either: every workspace that deploys that component needs the same procedure, and diegodelgado does not even deploy it — it CONSUMES it from another workspace.
· THE CATALOG ALREADY HAS THE INHERITANCE THIS NEEDS, FOR DATA. A provisioning catalog component declares `concerns` — tls · dns · certs · backup · observability · security — as DEFAULTS, and a workspace overrides them. The chain demonstrably works: `catalog/components/docker_mailserver/nickel/defaults.ncl` sets `backup | force = { kind = 'pending, reason = "BackupPolicy declared at workspace level (mail-stack BackupGroup)" }`, and libre-wuji's `backup-groups.ncl` declares exactly that `mail_stack` group. Default → override, end to end, already in production.
· AND NONE OF THE SIX CONCERNS IS THIS ONE. `ObservabilityImpl` (`schemas/lib/concerns.ncl:97`) is metrics · logs · traces · alerts — Prometheus telemetry. «Ask this component what it currently holds» is an operator-facing report, not a metrics surface. The concern that would host it does not exist, and inventing it as a seventh would change a schema ~60 components type against while still not making anything LOADABLE: concerns are data, modes are executable.
· WHAT THE ABSENCE COSTS, MEASURED IN THE TREE. libre-wuji carries `inspect-storage` — a bespoke workspace mode that interrogates Longhorn, a CATALOG COMPONENT. Every component in that workspace additionally hand-writes a `context = { how, why, supervision, updates }` block. That is the interrogation-and-management surface, written once per workspace per component, with nothing linking it back to the component it describes. `find catalog/components -type d -name modes` returns NOTHING — not because shipping modes from a component was considered and refused, but because no rung would read them.
· THE PRECEDENT IS BUILT AND IS CITED BY ITS OWN AUTHOR. Migration 0066 / adr-092 gave a biop two rungs — `reflection/biop/` shipped and inherited, `.ontoref/biop/` a local overlay winning per id — stated as «a copy is what witness-not-clone refuses». adr-108 applied the same cut to the reflection schema three days ago. This applies it to modes, and the resolution target is not new either: adr-102 already established ONE ROOT PER PROVIDED DOMAIN at `<provider_root>/.ontoref/domains/<id>/pages/`, reached by `domain-root <id>`, which adr-087 makes a Hard violation to reproduce rather than invoke.
· WHY THIS IS NOT «THE DOMAIN IS OUT OF DATE». It was worth stating because it was the first hypothesis and it was wrong: `provisioning/.ontoref/catalog/` being empty says nothing (the catalog is a project folder, not an ontoref one), and `observability = 'pending` is not a gap but a declared position governed by libre-wuji's adr-009 coverage FSM. The domain is not behind. It has no rung.
Decision
A DOMAIN PROVIDES MODES TO EVERY PROJECT THAT BONDS IT, AS A THIRD RUNG BETWEEN THE PROJECT AND THE PROTOCOL.
(1) THE RUNG. `all-mode-files` gains a middle rung, resolved per domain returned by `project-domains` for the current project:
<domain-root <id>>/.ontoref/domains/<id>/modes/*.ncl
The shape is adr-102's, verbatim, one directory over from `pages/`. Nothing new is invented about where a domain's surface lives; a second KIND of surface is read from the root that already holds the first.
(2) PRECEDENCE, AND IT IS THE WHOLE POINT. project > domain > protocol, deduplicated by mode id — which is the file's basename, as `all-mode-files` already does. The FIRST occurrence keeps the id. So a workspace overrides a domain-provided mode by writing one with the same filename, and inherits it by writing nothing. That is «use the component's method, or override it» expressed in the mechanism that already resolves modes, with no new vocabulary.
(3) ONTOREF DOES NOT LEARN THE WORD «CATALOG». The protocol resolves a DOMAIN's modes directory and stops there. Whether the provider populates it per catalog component, per service class, or by hand is the domain's business. Hardcoding `catalog/components/*/…` into the loader would write one domain's layout into the protocol — bl-048's defect, a location true on one machine promoted to a contract.
(4) `domain-root` IS INVOKED, NEVER REPRODUCED (adr-087). It shells out through the domain's own `schema_cmd` under the `ONTOREF_RESOLVING_DOMAIN` guard, which exists because reproducing it without the guard forks until killed. This rung therefore SKIPS ITSELF while that variable is set: resolving a domain must not require listing the modes whose listing is what asked for the resolution.
(5) A PROJECT THAT BONDS NO DOMAIN IS UNAFFECTED. `project-domains` returns an empty list, the middle rung contributes nothing, and the two-rung behaviour every existing project has today is bit-for-bit what it was. Nothing becomes required.
Constraints
- Hard The middle rung MUST reach the provider through `domain-root`, never through a path built in the loader
- Hard Domain modes MUST be appended after project modes and before root modes, so a project overrides a domain and a domain overrides the protocol
Alternatives considered
- Add a seventh concern (`inspection`) to ServiceConcerns — rejected: Changes a schema ~60 catalog components type against, needs its own migration in provisioning, and still would not make the procedure loadable — concerns are data read by templates, modes are executable and resolved by a different mechanism. It would also push docker_mailserver from 3/6 to 4/7 concerns 'pending, which libre-wuji's adr-009 threshold forbids.
- Glob `catalog/components/*/ontoref/modes/` from the loader — rejected: Writes the provisioning domain's directory layout into the protocol. Every other domain would either have to adopt that layout or be excluded by a special case, and the protocol would carry a word from one domain's vocabulary.
- Leave interrogation as a per-workspace concern — rejected: It is the status quo and its cost is measured above: one bespoke mode per component per workspace, none of them reachable from the component they describe, and a consumer project that cannot reach any of them at all.
- Have the domain's commands.nu expose the procedures instead of modes — rejected: `provisioning/commands.nu` already dispatches domain commands, so this would work — and it would put executable procedure outside the mode machinery that already gives guards, capability-typed `needs`, step DAGs, dry-run and run records. A procedure that cannot say `needs = ["node_reach"]` reports a missing VPN as a failed claim.
Anti-patterns
- Every workspace writes its own procedure for interrogating a shared catalog component — The component knows how to install and configure itself but not how to answer for itself, so each workspace that deploys it writes that procedure again, in its own file, with no reference back to the component. The copies drift independently and a defect found in one is fixed in one.