clay-borg/decisions/ADR-0018-a-number-that-does-not-move.md
tegwick d7279483f6
Some checks failed
ci / check (push) Failing after 4s
CB-WP-0030: a control for the wrong-subject family
Seven claims in this project have been wrong the same way — the arithmetic
right, the subject wrong. Four reached a document or another repo.

ADR-0018 states the rule: a measured claim names the variable it depends
on and shows the measurement moving with it. GameDesign §1.4 adds it as a
fourth admissibility clause; the existing three are untouched.

Honest yield is 3 clear + 1 partial of 7. It misses a count never made and
a claim that is not a number, and D3 names those so nobody stops looking.
The tempting fix — closing facts-check's 62 untagged literals — would have
caught 1 of 7 and is deferred as separate work rather than bundled.

loop-lint gains check_sensitivity_stated over arithmetic register rows,
mutation-proven red on F17 and green when restored, with four self-test
controls. It checks presence, never adequacy, and says so in its output.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 11:35:41 +02:00

126 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ADR-0018: a number that does not move when its subject moves is measuring something else
status: accepted
date: 2026-08-07
decided by: agent, under the standing loop authorization
tier: M (structural M — adds an admissibility requirement, changing how
the loop constrains its own operation; ADR-0006 D5). chaos d8 = 7 → no
override. **Declaration 1 of chaos window 3.**
references: [CB-WP-0030](../workplans/CB-WP-0030-a-number-that-does-not-move.md),
CB-EV-0019 §1 (where the family was named),
[GameDesign.md](../specs/GameDesign.md) §1,
[RetrospectiveAnalysis.md](../specs/RetrospectiveAnalysis.md) §4.1,
[ADR-0017](ADR-0017-chaos-window-2-verdict.md) (instance 7)
## Context
Seven claims in this project have been wrong in the same way: the
arithmetic was right and the **subject** was not. Four reached a document
or another repository.
## D1 — what this does not do, stated first
**`facts-check`'s untagged surface is not this family's control.** It
reports **62** untagged literal copies across 7 registry keys and fails on
none. That is a real gap, and closing it would have caught **at most one**
of the seven:
- Instance 1's denominator **was** a registry fact and **was correct**
`am4a_target` = 3,798 is right; AM-4a simply does not cover the code
being priced.
- Instances 2, 4, 5 and 6 were **fresh measurements** with no registry key
at all.
> **A tag proves a number came from somewhere. It cannot prove the
> somewhere was the right place.**
The untagged surface is worth closing on its own merits and is **named as
separate work**. Bundling it here would let this decision claim a victory
it did not win — which is the family's own error, applied to itself.
## D2 — the rule
> **A measured claim must name the variable it depends on, and show the
> measurement moving with it.**
>
> **A number that does not move when its subject moves is measuring
> something else.**
This is not new machinery. It is the generalisation of two things this
project already built, each in response to one instance:
- `RetrospectiveAnalysis` §4.1's **policy panel** — born from instance 5,
which is why a single policy's win rate may not be reported as a
difficulty;
- `attack-value.rs`'s **one-varying-number sweep** — built for F17, which
varies ATTACK's rank and holds everything else fixed.
**Both were built after an instance. The rule requires them before.**
## D3 — the yield, instance by instance, including the misses
| # | the claim | would D2 have caught it? |
|---|---|---|
| 1 | `csv` against AM-4a's headroom | **yes** — vary which crates are in scope and the budget moves; that *is* the error |
| 2 | *"12 in the file"* | **no** — but §1.2's row-level rule, born from it, does |
| 3 | SOLVE's wrong condition | **no** — not a number |
| 4 | 112161 µs per node | **yes** — cost must rise with branching; it fell, and the tell was already printed |
| 5 | too easy at 56 seats | **yes** — vary the policy and the rate collapses to 0% |
| 6 | 2 Focus/Blame conflicts | **partly** — varying seat count would scale a real conflict; it is the weakest row |
| 7 | window 2 had zero overrides | **no** — a count that was never made has no sensitivity to state |
**Three clear, one partial, three missed.**
**The two it catches outright are the two that travelled furthest**, and
both were bound for `ground-game`: instance 4 sat in a survey, instance 5
was queued for a repo blocked on that exact number. A control that catches
the consequential half is worth having even at 3/7.
**What it misses is stated so nobody stops looking.** Instance 7 needs
counting, not sensitivity. Instance 3 was not a number. **No syntactic gate
detects a wrong subject in general** — it is a semantic error, and
claiming otherwise would build a gate that cannot cash out (ADR-0006 D3,
ADR-0017 D2).
## D4 — where it binds
- **Register findings whose claim is arithmetic** — the same scope
GameDesign §1.2 already uses.
- **Anything leaving the repo.** Four of the seven were outbound or nearly
so, and the recipient caught one we did not.
**Not** every number in every artifact. A rule that binds everywhere is
obeyed nowhere, and this project has 62 untagged literals as evidence of
what a broad, unenforced requirement produces.
## D5 — half of it is mechanical, and only half
`loop-lint` can check that a sensitivity line **is present**. It cannot
check that the sensitivity is the **right one**, because that is the same
judgement the rule exists to force.
> **The lint checks presence, not adequacy, and must say so in its own
> output** — otherwise a green lint reads as *"the claim was verified"*,
> and the control becomes a way of not looking.
That is the honest split: the mechanical half stops the requirement being
forgotten; the judgement half stays with the author and the reviewer, who
between them caught five of the seven.
## Consequences
- `GameDesign.md` §1 gains a fourth clause. The existing three are
untouched — each was bought by an earlier instance.
- `loop-lint` gains a check over the register's arithmetic rows.
- Existing rows are brought under it or exempted **with a reason**.
- The untagged-facts surface is recorded as separate, unclosed work.
## What was rejected
| rejected | why |
|---|---|
| closing the untagged-facts surface as *the* control | 1 of 7; a tag reproduces a wrong subject faithfully |
| requiring a regenerating command on every claim | catches transcription, and six of seven were not transcription errors |
| a rule binding every number everywhere | obeyed nowhere; the 62 untagged literals are the evidence |
| a lint that claims to verify the claim | it can only see presence, and pretending otherwise trains the eye to stop at green |
| stating a yield of "the family is closed" | the family's own error, applied to itself |