Skip to contentWolf-Rayet

Decision records

ADR-0074 Three checks were owed, one of them was already done, and the claim had been carried forward twice

Accepted2026-09-06Phase 4, reopened

#Context

ADR-0071 and ADR-0073 each ended by naming a check the repository owed, and ADR-0073 grouped them: *"each is a rule or a list the repository states in prose and reads nowhere. They should be taken as one unit rather than accumulated further."* This is that unit.

The first thing it found was that one of the three was not owed.

#The correction, first, because it is about how the other two came to be named

tools/battery.js's lists were called unreachable in ADR-0071's Consequences — *"the first being tools/battery.js's own lists"* — and again in ADR-0073's, as one of three. They are reachable and they are joined. All seven of that file's exports are imported by tools/check-battery.js, whose six assertions join every one of them in both directions:

  • 58 scripts in the repository, all classified — 23 in the battery, 12 excluded each with a reason, 23 fixture generators. A script missing from the battery has to appear in one of the other two or the check fails.
  • 210 artifacts across 35 components, and every file in the three per-component directories attributed to a component or declared as belonging to a tier.
  • 36 must-fail fixtures, each named for a component or for a decision.
  • Every check resolving to a root script, and every check run by the workflow it names, across 5 of 5 workflows.

The item was written when MUST_FAIL still lived inside cli.js and was read by nothing but the CLI. ADR-0064's follow-up extracted that list, along with the scene roster and the themed set, and cli.js now holds no data at all. The item was discharged and the claim was carried forward.

It was then restated twice without being re-derived, which is the same error this session had already made once — a structural claim about a CI job's upload step, written from a sed range that stopped one step short of the end. Both times the fix is the same and it is not "be more careful": a claim about what a file contains is derived in the turn that states it, or it is not stated. Recorded here rather than quietly dropped, because an owed-work list that accumulates entries nobody re-checks is the same defect as a roster whose build column agrees with the package by habit.

#D8: persistence, declared rather than inferred

ADR-0046 states its rule on a property so that a fourth persistent Scope would inherit it by being persistent rather than by being named:

A component that remains rendered across a change of route declares level 1 and no other, and may not hold a demand.

Nothing read it. It was applied by hand in three contracts and survived in the repository as one comment on a row in discipline.test.js. ADR-0073 found what that costs: the tier cannot tell a shell from a region, because region, page-header and data-table declare the same [1] for §3's entirely different reason — that a Scope distributes an allocation and does not hold one. Two arguments, one declaration, and no way to tell which one a component made.

So the reason is declared. persists joins interactive, vouches and acknowledges as an axis every component carries, and tokens:verify asserts ADR-0046's rule against it.

One-directional, by design. Persistence implies one rung; one rung implies nothing about persistence. Asserting the converse would refuse region for obeying §3.

With the positive half beside it (ADR-0041). Every assertion above is satisfied by an empty set — if no component declared persists: true the rule would hold vacuously and the check would be green about nothing. So a run in which nothing persists is a failure, and the components that do are printed by name on every run rather than counted, in the shape ADR-0051 gave acknowledgement's named exception.

It is the eighth dimension of the identity test, and it is the one that makes ADR-0073's Scope finding checkable: the shell panels and region now differ in the tier rather than only in an argument.

#The identity test's axes are classified rather than stated

Six roster rows have been removed on the identity test and several kept, so what it contains is load-bearing. It has been mis-described twice: silently extended by ADR-0059, and called *"every dimension the component tier has"* by ADR-0057 when it named four of thirteen — a sentence five records then carried. ADR-0071 recovered vouches and acknowledges from the tier and found that presence, identical to pagination on all five dimensions the test then had, was kept only by an axis nobody had listed.

A list that load-bearing may not live in prose. component.config.json now carries an identity classification in the shape tools/check-battery.js uses for scripts:

  • 14 axes are declared on every component.
  • 6 are dimensions of identity — layer, levels, interactive, vouches, acknowledges, persists.
  • 8 are excluded, each with a reason — selector, variants, mark, geometry, text, space, motion, apcaFloors.
  • The two together must account for all fourteen, and may not overlap. An axis declared on every component and classified as neither fails the run, so a new universal axis cannot be added without someone deciding which it is — which is exactly the failure this replaces.

variants is excluded with the reason spelled out, because ADR-0071 predicted the specific way it would be recovered: by the reasoning that recovered vouches and acknowledges. An exclusion nobody wrote a reason for is one the next audit re-argues from scratch.

And the check reports what it cannot see. Governed copy and disclosure are dimensions with no tier axis — they live in the contracts — so a check reading this file alone would report the test as shorter than it is. It prints them on every run instead, in ADR-0034's manner: a step reports only what its job measured, and says what it did not.

#Both checks were observed red before being trusted green

ADR-0041's rule, and four separate breakages rather than two, because each check has two directions:

  • alert flipped to persists: true against its three rungs — refused, naming the rule and what a persistent surface holding the ration would cost.
  • Every shell flipped to persists: false — refused, on the vacuous-truth half: *"a set that emptied is a check that stopped having a subject."*
  • space removed from the classification — refused as an axis nobody classified.
  • surface added to the dimensions, which only 27 of 35 components declare — refused, because the test compares two rows and an axis only some rows carry cannot be one of its dimensions.

#Rejected options

Derive the identity dimensions from the tier instead of classifying them. ADR-0071 rejected this and the reason still holds: two of the eight are contract-side and a check reading component.config.json alone would report six and be wrong in a new way. The classification is the honest middle — the tier half is total and checked, the contract half is named and disclaimed on every run.

Make persists two-directional, so that a single-rung component must justify itself. Tempting, because it would force region, page-header and data-table to say which argument they are making. Rejected because the rule ADR-0046 states is one-directional and inventing its converse here would make this check assert something no record decided. What the declaration already achieves is the distinction ADR-0073 wanted: those three declare false and say why in their note.

Infer persistence from the layer, since only Scopes persist so far. It would need no new axis. Rejected because *so far* is the whole problem: menu is a Scope on section D that will persist, region is a Scope that does not, and a rule stated on a property may not be checked against a proxy for it.

Write the two checks and drop the third quietly, since a claim that turned out to be false costs nothing to delete. Rejected on §12's own terms: the rejected options are the evidence of judgment, and so is a wrong claim about the repository that the repository itself corrected.

#Consequences

Roster 49, built 35, both unchanged. The battery stands at 23 and §10 at 27 — no check kind was added, two existing checks grew, which is the outcome ADR-0021 predicted for this enforcement surface: *"the enforcement surface does not grow with the roster."*

persists is declared on all 35 components and true on 3 — shell-header, shell-left-panel, shell-right-panel. menu will be the fourth and will inherit the rule by declaring the property, which is what ADR-0046 asked for.

The owed list is empty. It was three; one was already done, two are built. Nothing is carried forward on it, and the next record that adds to it should derive the state of the others in the same turn.

No token, scene or baseline moved. The config's hash moved, so every component receipt did — same values, different provenance.

#Measured

  • check-battery.js imports all 7 of battery.js's exports; its six assertions report 58 scripts classified (23 + 12 + 23), 210 artifacts across 35 components, 36 must-fail fixtures *(disk, this pass)*.
  • persists declared on 35 of 35, true on 3. ADR-0046's rule asserted against it; the coverage line prints the three by name.
  • identity: 14 universal axes, 6 dimensions, 8 excluded with a reason, union total and disjoint *(darwin-arm64, this pass)*.
  • Four observed refusals, one per direction of each check, each restored green.
  • pnpm battery — 23 of 23, the verdict this pass is measured by.