ADR-0021: the chaos roll is retired, and the condition that retired it was wrong
Some checks failed
ci / check (push) Failing after 4s

The condition is met and the tally was verified rather than recalled.
ADR-0017 D2 named window 3 as the decider; windows 2 and 3 each produced
exactly one override and each changed nothing. Every roll in window 3 was
cross-checked against the workplan that made it, because the last time
this project tallied chaos rolls from memory it was wrong and asserted the
wrong figure four times (F23). All twelve agree.

But meeting the condition is not evidence. P(an override changes nothing)
is 1/3, and each window had exactly one override, so the condition fires
on a 1/9 coincidence. ADR-0017 restated it to be REACHABLE and made it
weak in the process; reachability was checked and discriminating power was
not.

Worse, it measures the wrong subject. It asks whether an override changed
the tier; the question is whether changing the tier helped. Under it, a
die that always changed the tier could never be retired however useless
its changes were.

The real ground is stronger. Four overrides across roughly forty
declarations, and the mechanism's value has never once been demonstrated.
The one substantive intervention dropped CB-WP-0011 from a structural L to
S, and that work then needed CB-WP-0016 and CB-WP-0017 to fix defects a
human found by playing. Not offered as causation — a tier is process
weight, not a guarantee — but it is the only evidence we have about an
override's consequences and it points the wrong way.

And the purpose has no live evidence of need: 17 M, 11 S, 4 L across every
workplan, with the only two structural/declared mismatches being the
window-1 overrides themselves. Tier declaration has not ossified.

So: retired, with nothing replacing it. Adding a successor to guard
against ossification that has not occurred would invent a gate for an
instance we do not have. The revival trigger is stated: tiers collapsing
toward one value, or a pass declaring below its structural tier to dodge a
review.

InnerLoop loses the chaos paragraph, loop-lint loses the chaos-recorded
check — a check that outlives its rule becomes an obstruction — and its
self-test now asserts the opposite: a note with no roll must pass.
ChaosRollHistory is closed. Window 4 ends incomplete at four declarations,
the last of which is this one, rolled because the rule was still in force.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-08 15:51:52 +02:00
parent b0001b5760
commit 05d1a9aada
4 changed files with 183 additions and 46 deletions

View file

@ -0,0 +1,127 @@
# ADR-0021: the chaos roll is retired — and the condition that retired it was wrong
status: accepted
date: 2026-08-08
decided by: agent, under the standing loop authorization
tier: M (changes how the loop constrains its own operation — ADR-0006 D5).
chaos d8 = 7 → no override. **Declaration 4 of chaos window 4, and the
last chaos roll this project will make.**
references: [ADR-0006](ADR-0006-the-inner-loop.md),
[ADR-0017](ADR-0017-chaos-window-2-verdict.md),
[`specs/ChaosRollHistory.md`](../specs/ChaosRollHistory.md),
[`specs/InnerLoop.md`](../specs/InnerLoop.md) §Loop tiers
## D1 — the condition is met, and the tally was verified rather than recalled
ADR-0017 D2: *retire if a full window's overrides all change nothing, and
the window contains at least one override* — met in **two consecutive
windows**. D2a: *"Window 2 counts as the first. Window 3 decides."*
| window | declarations | overrides | all changed nothing? |
|---|---:|---:|---|
| 2 | 12 | 1 (CB-WP-0020, d8=8, drew S over structural S) | **yes** |
| 3 | 12 | 1 (CB-WP-0038, d8=8, drew L over structural L) | **yes** |
**Two consecutive. The condition is met.**
**Every roll was cross-checked against the workplan that made it**, not
read off the history table — because the last time this project tallied
chaos rolls from memory it was wrong, and asserted the wrong figure four
times (F23). All twelve of window 3 agree.
## D2 — and meeting it is not evidence
**P(an override changes nothing) = 1/3.** Each of windows 2 and 3 had
**exactly one** override. So:
> **P(the condition fires | one override per window) = 1/3 × 1/3 = 1/9.**
**An 11% coincidence is not a finding.** The condition, restated in
ADR-0017 D2 to be *reachable*, became reachable by becoming weak — and
that trade was made without computing this number. Reachability was
checked; discriminating power was not.
## D3 — worse: it measures the wrong subject
**The condition asks whether an override changed the tier. The question
is whether changing the tier helped.**
Those are different subjects, which is the family
[`Taxonomy.md`](../specs/Taxonomy.md) §2.2 exists to name. An override that
changes nothing tells you the die landed on the tier you already had; it
says nothing about whether the mechanism is worth running. Under this
condition a die that *always* changed the tier — maximally active — could
never be retired, however useless its changes were.
## D4 — the real ground for retirement, which is stronger
Four overrides across roughly forty declarations:
| override | effect | benefit |
|---|---|---|
| CB-WP-0011 | structural **L → declared S** | **none recorded** |
| CB-WP-0017 | structural **S → declared M** | none recorded |
| CB-WP-0020 | drew S over structural S | changed nothing |
| CB-WP-0038 | drew L over structural L | changed nothing |
> **The mechanism has acted four times in the project's life and its value
> has never once been demonstrated.**
**And the one substantive intervention is not encouraging.** CB-WP-0011
was the inspectable-table pass; the roll dropped a structural **L** to
**S**, and the resulting work needed **two follow-up passes** — CB-WP-0016
(the drag was broken) and CB-WP-0017 (the page was wrong about which moves
exist) — to fix defects a human found by playing.
**That is not proof of causation and is not offered as such.** A tier is
process weight, not a guarantee, and both defects were the kind INTENT
says only human verification finds. **But it is the only evidence we have
about an override's consequences, and it points the wrong way.**
## D5 — the purpose has no live evidence of need
The roll exists so tier declaration cannot ossify. Measured across every
workplan:
**17 M · 11 S · 4 L**
**That is a real spread**, and only two passes in the project's history
declared a tier different from their structural one — **both of them the
window-1 overrides.** Left to itself, tier declaration has varied on the
structure, which is what it is supposed to do.
## D6 — retired, and what replaces it
**The chaos roll is retired.** No roll, no window, no `chaos` line in a
workplan's tier block. Existing records keep theirs — they are history.
**Nothing replaces it.** Adding a successor mechanism to guard against
ossification that has not occurred would be inventing a gate for an
instance we do not have (GameDesign §2).
**What stays** is the structural derivation and its trigger table, which
is the part that was doing the work.
**The trigger for revisiting**: if declared tiers collapse toward a single
value, or if a pass is found to have declared a tier below its structural
one to avoid a review, ossification is real and this decision was wrong.
## Consequences
- `InnerLoop.md` §Loop tiers loses the chaos paragraph; `loop-lint`'s
`chaos-recorded` check must go with it, or it fails every future
workplan.
- `ChaosRollHistory.md` becomes a **closed log**, and says so.
- **Window 4 ends at four declarations**, incomplete, which is correct: the
condition was met at the end of window 3 and waiting out a window to
satisfy a symmetry nobody requires would be ceremony.
## What was rejected
| rejected | why |
|---|---|
| retire on the strength of the condition | it fires on a 1/9 coincidence and measures the wrong subject (D2, D3) |
| keep the roll because the condition is weak | the condition being weak is not an argument for the mechanism; D4 and D5 are, and they point at retirement |
| keep it as a diagnostic, unrolled | a roll nobody acts on is a ritual with a log file |
| replace it with a value-measuring condition | it would need overrides to measure, and retirement stops producing them. **The honest sequence is retire, then watch D6's trigger** |
| wait for window 4 to close | ceremony; the condition was met at the end of window 3 |

View file

@ -1,5 +1,15 @@
# Chaos roll — window records
> **CLOSED 2026-08-08.** The mechanism is retired
> ([ADR-0021](../decisions/ADR-0021-the-chaos-roll-is-retired.md)). This
> file is history and takes no new entries.
>
> **Four windows, four overrides, no demonstrated benefit.** The
> retirement condition was met — and was itself wrong: it fires on a 1/9
> coincidence, and it measured whether an override *changed the tier*
> rather than whether the change *helped*. The real ground was D4's
> tally, not the condition.
One entry per chaos window. Split out of `InnerLoopReference.md` when that
file crossed the loadability limit: this is a **log that grows**, and a log
inside a reference eventually crowds out the reference.
@ -99,9 +109,17 @@ not binding on what was actually built.
| 1 | CB-WP-0041 | d8 = 5 | — |
| 2 | CB-WP-0042 | d8 = 4 | — |
| 3 | ADR-0020 | d8 = 7 | — |
| 4 | ADR-0021 | d8 = 7 | — |
**Window 3's verdict remains owed** and is now two windows behind. One
override in twelve, changing nothing, is the second consecutive window
meeting ADR-0017 D2's retirement condition — *"a full window's overrides
all change nothing"* — and the condition asks for two consecutive such
windows. **It should be evaluated, not restated again.**
**Window 4 ends here, incomplete, at four declarations.** The condition
was met at the end of window 3; waiting out a fourth window to satisfy a
symmetry nobody requires would have been ceremony. **Declaration 4 is the
last chaos roll this project made** — the one that declared its
retirement, which was rolled because the rule was still in force.
**Window 3's verdict, delivered 2026-08-08** in
[ADR-0021](../decisions/ADR-0021-the-chaos-roll-is-retired.md): the
condition was met, and **the condition was defective**. Every roll in this
window was cross-checked against the workplan that made it before the
verdict was written, because the last tally taken from memory was wrong
four times over (F23).

View file

@ -148,34 +148,21 @@ are never skipped for code-producing work.
| **M** | Survey and ADR merged into one document; review optional | Touches a canonical interface, adds/updates an external dependency, **or changes whether or how the loop constrains its own operation** — budgets, gates, review requirements, or these tier rules (v1.6, ADR-0006 D5) |
| **S** | One provenance paragraph in the commit message | Everything else (utilities, fixes, refactors inside a boundary) |
**The chaos roll.** After deriving the structural tier, roll **d8**
(`shuf -i 1-8 -n 1`). On an **8**, the tier is instead picked uniformly at
random (`shuf -e S M L -n 1`), overriding the structural derivation — up or
down.
**The chaos roll is retired** ([ADR-0021](../decisions/ADR-0021-the-chaos-roll-is-retired.md),
2026-08-08). The tier is the structural derivation above, and nothing
overrides it.
> **Window 3, opened 2026-08-07 at d8**, running to 12 declarations.
> It ran for four windows and acted four times. Its retirement condition
> was met — and **the condition was wrong**: it fires on a 1/9 coincidence
> and it measured whether an override *changed the tier*, not whether the
> change *helped*. The real ground was that **no override ever
> demonstrated a benefit**, and the one substantive intervention dropped a
> structural L to S on a pass that then needed two follow-ups.
>
> **Retirement condition (ADR-0017 D2):** retire the roll if **a full
> window's overrides all change nothing**, met in **two consecutive
> windows**. A window with **no** overrides is *inconclusive* and advances
> nothing. **Window 2 counts as the first.**
>
> Both windows' full records, the rate history, and why the old
> "twice running" condition was replaced:
> [`specs/ChaosRollHistory.md`](ChaosRollHistory.md), and
> `specs/InnerLoopReference.md` §Chaos roll — calibration.
>
> Record the roll every time, including when it changes nothing
> (`tier: L (structural L, chaos 8)`). Why the rate fell, why n=2 makes
> that the weakest part of the decision, and the dead rolls:
> [`specs/ChaosRollHistory.md`](ChaosRollHistory.md), and
> `specs/InnerLoopReference.md` §Chaos roll — calibration.
Chaos limits: a rolled-down tier relaxes *process* weight only. Invariants
(zero foreign types in canonical interfaces, determinism, passing
conformance suites) bind at every tier, and a rolled-down pass touching a
canonical interface still requires the interface change to be flagged in
the commit for retrospective review.
> The log is closed at [`ChaosRollHistory.md`](ChaosRollHistory.md).
> **The trigger for reviving it** is ossification: declared tiers
> collapsing toward one value, or a pass declaring below its structural
> tier to dodge a review.
### Step 1 — Research

View file

@ -191,7 +191,13 @@ def check_workplan_lifecycle(root=REPO):
def check_survey_tier_and_chaos(root=REPO):
"""§Loop tiers — tier declared, and the chaos roll recorded every time."""
"""§Loop tiers — the tier is declared.
**The chaos half is retired** (ADR-0021, 2026-08-08). Requiring the
roll would fail every future research note: a check that outlives its
rule stops being a control and becomes an obstruction. The name is
kept because `gates.toml` and the self-tests refer to it.
"""
out = []
base = os.path.join(root, "research")
if not os.path.isdir(base):
@ -203,12 +209,6 @@ def check_survey_tier_and_chaos(root=REPO):
text = open(os.path.join(root, rel)).read()
if not re.search(r"^tier:\s*[SML]\b", text, re.M):
out.append(Finding("tier-declared", rel, "no `tier:` declaration"))
elif "chaos" not in text.lower():
out.append(
Finding("chaos-recorded", rel,
"tier declared without the chaos roll; the rule requires "
"recording it even when it changes nothing")
)
return out
@ -644,15 +644,20 @@ def self_test():
check("evidence-unmeasured detects a table verdict, not prose",
len(f) == 1, f"{len(f)} finding(s), expected exactly 1")
# tier/chaos: missing tier trips; tier without chaos trips.
with open(os.path.join(tmp, "research", "A.md"), "w") as fh:
fh.write("# survey\nno tier here\n")
with open(os.path.join(tmp, "research", "B.md"), "w") as fh:
fh.write("tier: L (structural L)\n")
# tier: a missing declaration trips. The chaos half is retired
# (ADR-0021), so a note without a roll must now PASS — the
# control that used to require one would fail every new note.
os.makedirs(os.path.join(tmp, "research"), exist_ok=True)
with open(os.path.join(tmp, "research", "CB-RES-9999-x.md"), "w") as fh:
fh.write("---\nid: CB-RES-9999\n---\n\nno tier here\n")
f = check_survey_tier_and_chaos(tmp)
rules = sorted(x.rule for x in f)
check("tier/chaos detects both omissions",
rules == ["chaos-recorded", "tier-declared"], f"{rules}")
check("tier: a survey with no tier declaration is caught",
len(f) == 1 and f[0].rule == "tier-declared", f"{[x.rule for x in f]}")
with open(os.path.join(tmp, "research", "CB-RES-9999-x.md"), "w") as fh:
fh.write("---\nid: CB-RES-9999\ntier: M\n---\n\nno chaos line\n")
check("tier: a survey with no chaos roll now PASSES",
not check_survey_tier_and_chaos(tmp),
"ADR-0021 retired the roll; requiring it would fail every new note")
# self-test: a tool without the flag trips.
with open(os.path.join(tmp, "tools", "silent.py"), "w") as fh: