# 2026-07-31 — retrospective: does hardening converge? CB-WP-0003 T10. Pass produced `tools/loop-lint.py`, `specs/SessionShape.md`, the rule audit, the live cost budget, the retarget test, dated price rates, M-D1-LNK, provisional expiry, and InnerLoop **v1.2**. ## The question this task was given > Is the set of mechanisms now complete, or is each new pass still finding > a new class? If this pass produces an eighth instance in a fourth class, > the honest conclusion is that class-by-class hardening does not > converge, and the loop should optimize for *cheap detection and > correction* rather than for prevention — which is a different design. **It produced both. The answer is: hardening does not converge.** ## The ledger | # | class | instances | found by | |---|---|---|---| | 1 | **HDN** harness-does-nothing | **5** | executable assertions | | 2 | **TA** trusted arithmetic | **3** | re-derivation | | 3 | **SSB** same-sample blind spot | **1** | an assertion running over *all* data | | 4 | **DFD** duplicated-fact drift *(new)* | **2** | reading the copy against its source | Ten instances, four classes. Three workplans in, **every pass has produced at least one instance of a class the previous pass had not seen.** ### New this pass - **HDN #5 (latent).** `tools/rule-coverage.py` would have exited 0 reporting `0/0` if its spec regex ever stopped matching — a silent pass in the tool that reports our headline AM-1 number. Never fired; found only because T02 required it to carry a `--self-test` and writing one meant asking what failure it should detect. - **TA #3.** *"0 of 330 tool calls were batched"* — 330 was the count of single-call responses, not the total. Carried unverified from CB-WP-0001's retrospective into CB-WP-0003 T04. Real figure: 7.8–8.6% of responses batched. - **DFD #1–2 (new class).** `specs/MetricsAndScenarios.md` §1a inlined a copy of the price sheet; T11 changed the real sheet and the copy went stale **within the same hour**. Separately, the acceptance figure $92.87/$0.66 had to be chased across a survey, a workplan, and an evidence file each time it moved. The defect is not arithmetic and not a harness — it is **a fact stored in two places, where updating one does not update the other**. DFD is genuinely distinct: no positive control catches it (both copies are internally consistent), and re-derivation does not either (the copy reproduces whatever it was copied from). It is caught only by reading a copy against its source, which nothing in the loop required. ## What this implies, and what changed because of it Prevention has not converged: four passes, four classes, no sign of saturation. But the *correction* side has been consistently cheap and fast: | correction | mechanism | cost | |---|---|---| | $248.46 → $92.21 | re-derivation | ~0 | | $92.21 → $92.87 | adversarial review | $1.11 | | $92.87 → $93.32 | positive control | ~0 | | $93.32 → $93.15 | dated-rate fix | ~0 | Every error found in three passes was corrected inside the same session for under ~1% of the pass. **So the loop should stop trying to enumerate failure classes in advance and invest in the properties that make correction cheap** — which it turns out already has, accidentally: 1. **Numbers are re-derivable** because the raw data (transcripts, git, the price sheet) is kept, not just the conclusions. 2. **Artifacts are small and committed**, so a wrong number is one grep from every place that quotes it. 3. **Every reported number has a command**, so re-running is free. v1.2 makes this the stated design goal rather than an accident, and adds the one rule the new class earns: > **Single source of fact.** A number, rate, or target lives in exactly > one place. Everywhere else links to it. Where a copy is unavoidable, it > is generated by a command, not typed. *(CA-15 said this for evidence > tables; DFD shows it applies to specs quoting other specs.)* ## What the gates did this pass Worth recording because it is the cheapest possible evidence that the executable-rule thesis is at least partly right: - `loop-lint` found three real violations on its **first** run — including a 543-line spec against a ~400-line rule the loop had stated since v0.2 and never measured. - `loop-lint` then **failed on its own author within the hour**, when a T07 edit pushed `specs/InnerLoop.md` to 407 lines. The file was split because a gate said so, not because anyone noticed. - `cb-cost`'s dedup assertion fired on real data on its first run and refused to print a number. Three gates, three catches, all on first contact. Against that: the two most valuable corrections of the whole project ($248.46 → $92.21, and the batching claim) came from a rule that is **decorative and cannot be made executable** — *re-derive inherited numbers*. Both facts are true and the audit records them side by side. ## Revised hypothesis The workplan opened with: *a loop rule that cannot be executed is not a rule.* Three passes of evidence say that is wrong as stated. The version this pass supports: > An executable rule fires reliably and catches one class. A decorative > rule fires unreliably and can catch any class, including ones not yet > named. **Keep both, and never let a decorative rule be the only defence > for a class that matters** — but do not delete it merely because it > cannot be automated, which is what this workplan originally proposed. ## Open, not closed - **AM-4a/AM-4b are unratified retargets** (T07). `make dep-weight` enforces targets no reviewed decision stands behind. Maintainer decision. - **9 of 58 rules are claimed by a scenario and named nowhere in the aggregate** (T09, M-D1-LNK). Reported, not failing, because tagging them without confirming implementation is the overclaim the loop already committed once. - **5 provisional U-item defaults** now have an owner and an age, and ground-game has not ruled (T08). - **The chaos roll is in a 12-declaration calibration window** at d4 (T06), expected to cost $20–30 and to be decided on evidence. - **DFD has no gate.** The single-source-of-fact rule added in v1.2 is prose. Making it executable — detecting a number duplicated across artifacts — is the obvious next candidate and is not attempted here.