Every artefact of a level is an occupant of a declared kind or a store listed as undeclared under a signed ceiling; edges between occupants are derived by parsers and read per plane

Accepted

ontoref
adr-103 declared five classes of governed reference (adr, backlog, qa, biop, mode). Everything else a

Context

adr-103 declared five classes of governed reference (adr, backlog, qa, biop, mode). Everything else a level holds is invisible to the protocol, and a read-only measurement of 2026-10-17 across DD-eca, ontoref, provisioning and website-htmx-rustelo shows the cost: nobody can say what exists, what is used or what is broken, so nothing can be optimised, retired or reused.

Measured in this level:

- Volume: 256 just recipes and 349 scripts in ontoref (237 under .ontoref/reflection), 41 modes with 314 cmd and verify lines. No declaration by hand can keep up with that. - kinds.ncl states «every other store under reflection/ is UNDECLARED, and `kinds gaps` names each one». kinds gaps has no code for it. 32 entries under .ontoref/reflection/ hold files no kind covers, among them reflection/biop/, a second biop store beside .ontoref/biop. - The SOW `baseline` field («ratchet for gradual adoption») is read by no module. - A reference to a superseded ADR or a retired QA entry resolves as valid: nothing carries the target's validity, which is pattern 3 of the measurement in all four projects. - The «orphan» and «unresolved» figures of the measurement were basename guesses. Of 27 unresolved recipe→script edges most exist elsewhere and resolve once the recipe's `cd` is read; a recipe no other recipe calls may be the entry point a person types. Whether an artefact matters depends on the plane it is measured on, as a symbolic link counts for space and not for access. - The edges ARE derivable without reading prose: `just --dump --dump-format json` gives recipe bodies as fragments with typed variables and attributes; Nushell's `ast --json --flatten` gives `use` and `source` targets and keeps a string literal as one token (237 scripts in 0.8 s); mode commands are shell words. - Under jj no pre-commit hook runs (custody qa custody-git-ops-under-jj, bl-154); `iter accept` runs the contracts `due-sow` selects at Commit for the touched paths.

core.ncl recorded on 2026-08-31 why a declared catalog died: the operation was authored as code and the declaration written afterwards as a mirror, so it could only ever shadow what it described. An inventory of recipes and scripts written by hand would be that mirror again.

Decision

EVERY ARTEFACT OF A LEVEL IS AN OCCUPANT OR AN UNDECLARED STORE, AND SAYS WHICH. A kind may declare how its occupants are read — `reader = 'Nickel` (the default: a store that exports), `'JustDump` (a justfile read through `just --dump --dump-format json`), `'Path` (files matched by the store glob, the path being the id) or `'Stem` (files matched by the store glob, the file stem being the id, never exported — a Nickel schema is contracts and functions, which `nickel export` refuses). This level declares, besides adr-103's kinds: `core` (the nodes of core.ncl — not `node`, which collides with the glossary term ontoref-node), `schema` (reflection/schemas/, 'Stem: qa entries cite schemas by bare name), `recipe`, `script` and `config`. The `biop` kind covers both of its directories, the project's overlay (.ontoref/biop/) and the shipped set (reflection/biop/), as biop.nu loads them. Every entry under .ontoref/reflection/ whose files are not all covered by some kind's store is listed in the project's `undeclared`, each with a why. `kinds gaps` names the uncovered entries and those not listed.

THE REGISTRY HAS TWO LAYERS. The base is the registry the protocol ships (reflection/kinds.ncl), or a whole registry a project declares in its own .ontoref/reflection/kinds.ncl. It declares kinds only. What a project HOLDS — its undeclared stores, the files a kind of it enumerates — is its inventory, .ontoref/kinds.ncl, outside the protocol tree, merged over the base by kind id. A project with no inventory is judged against no list.

THE UNDECLARED LIST HAS A CEILING, AND THE SIGNATURE HOLDS IT. The list may shrink freely; it may not grow. Since no module reads a SOW `baseline`, the ceiling is the set of entries written into a signed standing contract (sow-artifact-routes-v2 C8). A new store is declared as a kind, or enters through a signed amendment. What exists today is registered by being listed with its why; what is created from now on is refused until it is declared.

EDGES ARE DERIVED, NEVER DECLARED. Recipe → recipe and recipe → file from the dump; script → module and script → file from the Nushell parser; a kind's `commands` fields (modes: steps.cmd, steps.verify) tokenised as shell words; references from `offer.follow` (adr-104). `commands` is disjoint from `follow`: a command is not a reference a projection follows. The only things a person declares are what cannot be derived: a kind, its role, why a store is undeclared, and which recipes are exposed.

AN EDGE IS READ ON ITS PLANE. A reference (a route, adr-133). An invocation: the token after source, nu, bash, sh or just, a `use`, a recipe dependency — a target that exists nowhere is fail. A mention: any other token — a missing target is unknown, because `test -f x` probing an absence is legitimate. A path resolves against the referencing file, the project root and a preceding `cd` in the same command; found only by basename elsewhere it is unknown, with the place it was found. A token that depends on a shell expansion is unknown — except ONTOREF_PROJECT_ROOT, the one expansion with a declared value (the project root, adr-116), which is substituted before tokenising. A shebang recipe is one script, so its `cd` holds for the lines after it; a plain recipe runs each line in its own shell.

A MODE STEP THAT DOES NOT RUN FROM THE ROOT STATES WHERE IT RUNS. A step whose commands belong to a member or a subtree writes it into the command — `cd ${ONTOREF_PROJECT_ROOT:?}/<dir> && …` — never only in a precondition's prose: the directory is what makes its targets resolvable, and prose is not read.

AN OCCUPANT'S VERDICT IS ITS STANDING ON THOSE PLANES. pass when something invokes it or it is exposed (a public recipe with group and doc; the governed-justfiles `exposed` declaration takes precedence when it exists). unknown, with its cause, when neither — never fail: nobody can know from the graph that an entry point is dead. Every reference also carries the TARGET's validity — the last becoming response, else an ADR status Superseded or Deprecated, else Active — so a live reference to a retired occupant is visible.

THE AUDIT REPORTS; CONTRACTS REFUSE. `kinds audit` derives every edge, gives every reference, invocation and occupant its verdict, cause and plane, and writes nothing. Refusing is done by standing SOW contracts at Commit: no broken edge in the level (C13), no store outside the ceiling (C8), one resolver and one dump reader (C4, C9). Under jj these run at `iter accept`; that is the gate, not a client hook.

ONE READER PER SOURCE. `just --dump` is read in just.nu only (extracted from describe.nu, extended by the governed-justfiles work); the Nushell parser and the shell tokeniser live with the resolver in kinds.nu.

A LEVEL AUDITS ITSELF. This decision covers the spine level (root justfile, .ontoref/). code/, outreach/ and desktop/ are other levels with their own governance and carry their own SOW. Rust sources are not read.

Delivered by sow-artifact-routes-v2, successor of sow-artifact-routes (C5–C13). The constraints below invoke that SOW's signed contracts by id; they do not restate them.

Constraints

  • Hard core.ncl nodes are the kind core; every entry under .ontoref/reflection/ is covered by a declared kind or listed as undeclared with a why; the list stays within the signed ceiling
  • Hard Recipe edges come from just --dump (read in just.nu only), script edges from the Nushell parser, and configuration is an occupant whose standing is who reads it
  • Hard A command token is an invocation after source, nu, bash, sh or just and a mention otherwise; a missing invocation target fails, a missing mention is unknown; a path resolves with a preceding cd and reads unknown when found only by basename
  • Hard kinds audit gives every reference its verdict, cause and the target's validity, and leaves the audited tree byte-identical
  • Hard kinds audit over this level reports no fail; unknown is reported and never refused

Alternatives considered

  • Leave recipes, scripts and configuration out of the registry — rejected: They are most of the volume and most of the breakage measured; excluding them leaves the problem the measurement found where it was. The objection to their orphan figures was to an ungraded verdict, which planes answer.
  • Declare every recipe and script by hand — rejected: Volume rules it out, and it reproduces the catalog failure: a mirror of code written after the code.
  • Detect edges with regular expressions over files — rejected: A regex cannot tell `use x.nu` from the string "use x.nu", nor a recipe variable from a path. The dump and the parser can, at negligible cost.
  • Fail every artefact nothing invokes — rejected: An entry point a person types is invoked by nothing in the graph. It would make the gate refuse correct trees.
  • Carry the ceiling in the SOW `baseline` field — rejected: No module reads it; a ceiling nobody enforces is the declared-not-built defect this ADR records in kinds gaps.
  • A pre-commit hook as the gate — rejected: Under jj it does not run (bl-154). Gates are defined once and run at the moment custody owns; for jj that moment is accept, through the SOW's Commit contracts.

Anti-patterns

  • An inventory of mechanisms declared by hand — A list of recipes, scripts or their callers written beside the code. It is right on the day it is written and shadows the code from then on — the catalog failure core.ncl recorded on 2026-08-31.
  • Declaring an artefact dead from one plane — Reading «nothing invokes it» or «not found at that path» as dead or broken, without the exposure plane, the cd context or the basename-elsewhere case. It produced the measurement's false orphan and unresolved figures.
  • A ceiling stored in the file it limits — Reading the maximum of undeclared stores from kinds.ncl lets the edit that adds a store raise the ceiling in the same change.
  • An audit that mutates what it audits — Archiving, rewriting or deleting from inside the audit. Judgement and act merge, and a wrong finding becomes a wrong change with no one having decided it.

Related ADRs

ADR-133 · ADR-103 · ADR-104 · ADR-072 · ADR-127 · ADR-129 · ADR-074

Perspectives
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