A typed check has ONE reader — the contract, the shape validator and the executor admit the same spellings or the corpus stops being checkable

Accepted

ontoref
THE ITEM THAT ASKED A DIFFERENT QUESTION. bl-007 was opened by an adopting project. lian-build

Context

THE ITEM THAT ASKED A DIFFERENT QUESTION. bl-007 was opened by an adopting project. lian-build wrote adr-003 and wanted to enforce «no Rust file in src/ may call Command::new("nickel") EXCEPT src/directives.rs». The `'Grep` variant is `pattern` + `paths` + `must_be_empty` and cannot say «except», so the rule shipped as `must_be_empty = false` plus a `check_hint` explaining it socially — auditable by a human, invisible to a second copy of the dispatch in another module.

The item named three ways out and asked which: (A) `paths_exclude` on `'Grep`, (B) a `'GrepFiltered` tag carrying include and exclude lists, (C) recommend `'NuCmd`, which works today and is opaque to static review.

MEASURED 2026-08-29, WHILE SIZING IT. `'Grep` does not have one reader. It has three, and they already admit different spellings:

adr-constraints.ncl:135 the Nickel contract requires `pattern` and `paths` checks.nu:232 the shape validator accepts `pattern` + (`paths` OR `path`) checks.nu:398 the executor reads `paths`, `path`, or `file`

THE CORPUS HAS PAID FOR THAT THREE TIMES, and each incident is recorded in the file where it happened rather than here:

- The `files` + `absent_ok` dialect: legal Nickel, unreadable to the executor. Four Hard constraints dispatched to a crash. - `path` versus `paths`: the shape validator blessed the singular, the executor read only the plural, so a check written the blessed way passed validation and then found nothing to search — reporting «I DID NOT LOOK» about a file sitting right there. - `FileExists` admitting `paths` while `run-fileexists` read `path` alone (bl-098). The contract PERMITTED a shape the runner could not execute, dispatch-check has no per-constraint guard, so the first bad record aborted the run and ALL 372 CONSTRAINTS WENT UNEVALUATED.

The third is the one that decides this ADR. `EachCheckWellFormed` was added to end exactly that failure and did not, because it was written PERMISSIVE where the executor was STRICT. A contract that admits more than its runtime can execute does not fail the check that is wrong; it fails every check in the corpus, and it does so silently until someone runs the whole set.

WHY THIS IS NOT bl-007's QUESTION. Whichever of A, B or C is chosen, it lands in three places that can disagree, and the failure mode above is reproduced by construction. The expressivity question is real and stays open; it is not what blocks.

Decision

A TYPED CHECK HAS ONE READER. The set of shapes the contract admits, the set the shape validator blesses, and the set the executor can run are ONE SET — either because one authority derives the others, or because their disagreement is mechanically detectable before it reaches a corpus run.

Which of the two is chosen is deliberately left open: derivation and differential detection both satisfy the rule, they have different costs, and nothing measured so far distinguishes them.

WHAT THIS DECIDES - No variant of the check ADT may gain, lose or rename an accepted field in one reader alone. - A shape the contract admits and the executor cannot run is a DEFECT IN THE CONTRACT, not in the ADR that wrote it. adr-019 was not malformed by the rule it was validated against; it was malformed only by an unwritten rule the executor held alone. - Every reader's accepted spellings are stated where a reader of ONE of them can see the others.

WHAT THIS DELIBERATELY DOES NOT DECIDE — and the ondaod below says why at length: whether `'Grep` gains `paths_exclude`, whether a `'GrepFiltered` variant is added, or whether path exclusion stays outside the typed surface in `'NuCmd`. That question sits on `formalization-vs-adoption`, which core.ncl names as a Spiral, and this ADR refuses to collapse it. What it does is make the choice CHEAP AND REVERSIBLE: once divergence is impossible or detected, adding expressivity is an ordinary change instead of a corpus-wide risk, and a reversible decision does not need a tension collapsed in order to be taken.

SCOPE OF THE RULE: the constraint check ADT (`'Cargo`, `'Grep`, `'NuCmd`, `'ApiCall`, `'FileExists`, and any variant added after this). It does not extend to other typed surfaces by implication — a second surface with the same shape adopts it and says what paid for the adoption.

Constraints

  • Hard Every shape the constraint contract admits can be executed by the runner, and every shape the runner accepts is admitted by the contract.
  • Soft A change to what a check variant accepts is reported across all three readers together, and a change landing in fewer than three is reported as a direction, not refused.

Alternatives considered

  • Answer bl-007 as asked: pick `paths_exclude`, `'GrepFiltered` or `'NuCmd`rejected: All three land in three readers that can disagree, so all three reproduce the measured failure. Choosing among them before that is a menu whose every option carries the same defect — the `lesser-evil menu` pattern the ondaod discipline names, where the criterion that generated every cost stops being a candidate for falsification. The expressivity question is real and is deferred, not answered.
  • Repair the `'Grep` spellings and add `paths_exclude`, without a rulerejected: Closes the instance and leaves the shape. The next variant added — by this project or by any consumer — reproduces it, and there would be nothing to point at. Three incidents already show the defect recurs across variants rather than living in one.
  • Make the Nickel contract the single authority and generate the executor from itrejected: Not rejected as a solution — rejected as a DECISION TO TAKE HERE. It is one of the two ways to satisfy the rule and it is the more expensive one; nothing measured so far distinguishes it from differential detection. Fixing it in this ADR would spend a choice this ADR does not need to make, and would bind consumers to a mechanism before anyone has paid for it.
  • Deprecate `'Grep` and route path-scoped rules to `'NuCmd`rejected: Trades a typed, statically reviewable claim for a shell command nobody can read without running it. `plane-habitability` names this as a plane transgression to be EVALUATED rather than taken by default, and the eighth reading of enforcement-vs-emergence shows the cost concretely: a check whose blindness reports as the subject's fault is the metric's worst column, and an opaque command is the shape most able to hide there.
  • A Hard constraint requiring every expressivity change to land in three readers at oncerejected: A Hard biconditional on a Spiral surface, which ondaod forbids and which the corpus would fail on day one — it starts divergent. It would also refuse the honest intermediate state of widening a contract and following with the executor in the same session, converting the rule into something to work around. Kept as the Soft direction-reporting constraint above.

Anti-patterns

  • A contract that admits more than its runtime can run — A validator is written to accept several spellings «to be lenient», while the executor implements one. The lenient side reads as generous and is the one that breaks the corpus: the record validates, dispatch reaches a field that is not there, and with no per-constraint guard the whole run aborts. Leniency in a contract is only leniency if the runtime is at least as lenient.
  • Widening the variant with the worst blindness record — A check variant is given more expressive power while the question of who reads it is still open. The new field inherits every existing disagreement and adds a surface for the next one, on the variant that has already produced gates which report red over correct code and gates that never executed at all.
  • Fixing the dialect and calling the class closed — The specific mismatch is repaired, the corpus goes green, and the shape is recorded nowhere. The next variant reproduces it and the previous repair is not evidence of anything, because it fixed a spelling rather than establishing who decides spellings. `EachCheckWellFormed` is the worked example: it was added to end this exact failure and did not, because it was itself written in only one of the three readers.

Related ADRs

ADR-072 · ADR-070 · ADR-088 · ADR-031

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.