diff --git a/Makefile b/Makefile index 63d0495..7db140f 100644 --- a/Makefile +++ b/Makefile @@ -124,7 +124,6 @@ self-tests: $(PY) $(TOOLS)/size-metrics.py --self-test $(PY) $(TOOLS)/runtime-metrics.py --self-test $(PY) $(TOOLS)/replay-test.py --self-test - $(PY) $(TOOLS)/design-baseline.py --self-test $(PY) $(TOOLS)/design.py --self-test $(PY) $(TOOLS)/edition-check.py --self-test diff --git a/crates/cb-game-runtime/src/scenario.rs b/crates/cb-game-runtime/src/scenario.rs index a34ca40..8bf225c 100644 --- a/crates/cb-game-runtime/src/scenario.rs +++ b/crates/cb-game-runtime/src/scenario.rs @@ -31,6 +31,30 @@ pub struct ScenarioFile { /// reportable by `make coverage`. #[serde(default)] pub provisional_raised: String, + /// ISO date the owner ruled, which is what ENDS `provisional` + /// (CB-WP-0026). A confirmed default is no longer provisional — the + /// flag comes off and this records what settled it, because deleting + /// the flag without the ruling loses why the question closed. + #[serde(default)] + pub ruled: String, + /// Who ruled. Distinct from `provisional_owner`, which says who *must* + /// rule; this says who *did*. + #[serde(default)] + pub ruled_by: String, + /// The ruling in its own words. Quoted rather than paraphrased — + /// three of the ten U-item rulings carry conditions that a summary + /// loses (ADR-0012; GROUND-WP-0002 T05). + #[serde(default)] + pub ruled_note: String, + /// Which `§Underdetermined` U-item this scenario encodes the default + /// for, if any. + /// + /// **Named rather than inferred.** CB-RES-0007 asserted six of ten + /// U-items had provisional scenarios; measured, exactly one named its + /// item, and the rest was guesswork nobody could check. A scenario + /// claims a U-item here or it does not claim one at all. + #[serde(default)] + pub encodes_u_item: String, pub seed: u64, pub setup: Setup, pub commands: Vec, diff --git a/evidence/CB-EV-0022-collect-the-rulings.md b/evidence/CB-EV-0022-collect-the-rulings.md new file mode 100644 index 0000000..26a74af --- /dev/null +++ b/evidence/CB-EV-0022-collect-the-rulings.md @@ -0,0 +1,209 @@ +# CB-EV-0022 — collect the rulings + +CB-WP-0026 T05. Tier S (structural S — applies rulings inside an existing +capability; chaos d8=6 → no override). Declaration 9 of chaos window 2. +Closed 2026-08-05. + +**Delivered:** ten rulings recorded in `§Underdetermined`, five +`provisional: true` flags lifted, four scenario schema fields, the +`encodes_u_item` declaration, and an empty finding queue. + +--- + +## 1. How long the answers sat, and what noticed them + +**Two days**, and the thing that noticed was **the register's first run** — +not a human, not the adversarial review. + +`ground-game` ruled all ten U-items on **2026-08-03** (GROUND-WP-0002 T05), +every one confirmed. On the same day CB-RES-0007 reported *"0 of 10 +ruled."* Two days later CB-WP-0022 built the register recording them as +`reported`, and the adversarial review — which found four other things — +did not catch it either. `make design` did, on its first execution. + +**This is the symmetric failure nobody designed for.** CB-WP-0022 was +shaped end to end around *we send findings and nobody reads them*: the +four-day unread inbox is quoted in the declaration, the survey, the ADR +and the spec. The mirror case — *they answer and we do not collect it* — +appears in none of them. + +It is arguably the worse of the two. An unread message is visible as +silence; a collected-but-unapplied ruling looks exactly like work in +progress. + +## 2. Did any confirmed default fail to match the kernel? No. + +This was the control that mattered. Every ruling was a *confirmation* of +what we told `ground-game` we simulate — so a red scenario would have +meant **we described our own behaviour incorrectly to them**, a defect in +our report rather than in their ruling, and the most serious class +available since it would be about our own code. + +``` +make sim → 26 passed, 59 rules covered +``` + +**No scenario went red.** The five confirmed defaults are the behaviour +implemented. That is the strongest single result here and it is a +negative: nothing was wrong. + +## 3. The mapping gap was not what the survey said, and I reproduced the defect writing it + +CB-RES-0007: *"six of the ten already have provisional scenarios."* + +**Measured: one.** And the interesting part is how the other nine were +lost. + +I wrote two mappings from the `covers:` lists and **both were wrong**: + +| claimed | why it was withdrawn | +|---|---| +| `gr-a04-bond-support` → U1 | asserts consent is **required**; U1 asks **when** the target accepts | +| `gr-d05-darvo-reverse` → U5 | exercises the **unrejected** REVERSE; U5 is the **rejected** one (GROUND—ND) | + +Both were plausible from `covers:`. Neither survived reading the +description. **These are the third and fourth instances of this exact +defect** — a link that looks right from metadata, asserted without +checking what the artifact exercises — and the first two reached +`ground-game`. + +So the answer to *"do the four unlinked scenarios encode U-item defaults +at all?"* is: **at least two of the four do not**, and the survey's "six of +ten" was not an under-documented truth. It was wrong. + +`encodes_u_item` is now a declaration a scenario makes or omits, and +`design.py` asserts the file names what it claims. + +### The check's own first version was the same looseness + +Written as `grep -lE "\bU\b"`, it went red the moment two scenarios +recorded *why they do not* encode U1 and U5 — reporting `['U1','U2','U5']`. + +**A mention is not a claim.** That is precisely the imprecision that let +*"six of the ten have provisional scenarios"* stand unchallenged for five +days: someone grepped for U-item strings and counted hits. The check now +asserts on the declaration. + +## 4. The queue reached 0 + +``` +QUEUE (open findings) (none) +open, lacking a reproduction 0 target 0 +closed (log) 12 [U1..U10, F11, F13] +with a resolving reproduction 3/12 = 25% +notes 2 F12, F14 +``` + +**`9 → 0`**, the number CB-WP-0026 T04 named. First evidence that +ADR-0012 D5's lifecycle is real rather than drawn: findings entered a +state, moved through it, and left the queue. + +**25% reproduced must not be read as a failure.** Nine U-items closed by a +**ruling**, and a ruling is not an artifact. The metric is now honest +about something the survey's 33% concealed: most of our findings close +because someone answered them, not because anything demonstrates them. + +**What the lifecycle could not express** — the honest gap: `applied` is +defined as *the source changed and the provisional default was deleted*. +Here the rulings **confirmed** our defaults, so nothing in the rules moved; +what changed is that the flags came off. The state fits, but the +definition had to be read generously. If a future ruling *overturns* a +default, `applied` will mean something materially different from what it +meant today, and D5 does not distinguish them. + +## 5. Two things the schema caught + +**`ScenarioFile` is `deny_unknown_fields`**, so five scenarios failed to +parse until `ruled`, `ruled_by`, `ruled_note` and `encodes_u_item` were +declared in the Rust struct. A corpus that accepted unknown metadata would +let a typo'd `encodes_u_iem` sit forever claiming nothing — and this +pass's whole subject is claims nobody checks. + +**`record.rs` had to set them explicitly.** A recorded game is evidence of +what happened, not a claim about an undetermined rule. Filling the fields +via `..Default::default()` would have been shorter and would let a +recording silently inherit a U-item claim, pointing a reproduction at a +finding it has nothing to do with. + +## 5b. Two gates went red for the best possible reason + +Lifting the last five provisional flags broke two positive controls, and +**both were broken in the same way**: they asserted against live repo data +instead of constructing the condition they test. + +**`rule-coverage.py`** required `bool(prov)` — *at least one provisional +item must exist*. That guard was the right instinct (a control that passes +vacuously is worthless) wired the wrong way. With nothing provisional, it +went red. It now builds a fixture, asserts the missing-owner case is +**caught**, and reports the live count as a diagnostic — because **there is +no number of provisional items this project should have.** + +**`design-baseline.py`** pinned *"the measured baseline is 2 of 6"* and +reported `1/6`. Its "six provisional defaults" row **globbed +`provisional: true` at run time**, so the dated snapshot was never a +snapshot — it drifted with the repo. C2 dismantled this tool four hours +earlier and missed this: a hand-maintained dict with one dynamically +computed row is worse than a fully hand-maintained one, because the +recomputed row silently disagrees with the date in the header. + +Frozen to the literal list it measured on 2026-08-03, and **removed from +`make self-tests`** — that target is *"a positive control for every +reporting tool"*, and per ADR-0012 D8 this is no longer a reporting tool. +Leaving it wired in meant a superseded instrument could fail the build. + +**The pattern across both**: a control that reads the world it is meant to +audit will eventually audit a world that has moved. Neither was caught by +review; both were caught by the world moving. + +## 6. The register moved, and ADR-0012 D2 gave way to loadability + +`specs/GroundRules.md` crossed the ~400-line limit and `loop-lint` +required a split. ADR-0012 D2 said **"no new file"**, so this is a +deviation and is recorded as one. + +**D2's substance holds.** Its argument was *one register, not a second +mechanism competing with the first* — and `specs/FindingRegister.md` is +that same register, moved, still driving off the `provisional`/ruling +machinery, still the only one. What was traded away is the literal "no new +file", which was D2's *implementation*, not its reason. + +It also resolves an awkwardness D2 named itself: *"a finding about the +engine's behaviour sits in a document about the game."* Now it does not. + +## 7. Chaos window 2 + +**Declaration 9 of 12.** Structural S, d8 = 6, no override. Recorded per +§Loop tiers even though it changed nothing. + +**Window 2 still has no override to evaluate** — nine declarations, zero +8s. The retirement condition (*retire if an override changes nothing twice +running*) cannot be assessed, and at d8 the expected count over twelve +declarations is 1.5, so this is unremarkable rather than evidence of +anything. + +## 8. Cost + +CB-WP-0022's cost, by re-running the instrument: + +``` +CB-WP-0022-T05 $ 6.43 +CB-WP-0022-T01 $ 4.17 +CB-WP-0022-T02 $ 3.56 (the separate reviewer's own spend is in the subagent tree) +CB-WP-0022-T04 $ 1.92 +CB-WP-0022-T03 $ 1.01 +``` + +**The chain did not snap this time** — CB-EV-0019 §4 predicted it might, +having found `cb-cost.py --slug CB-WP-0020` aborts for want of retained +transcripts. CB-WP-0022 is recent enough to still be in the window. **The +bound CB-EV-0019 asked for is still owed**; this pass is evidence that the +rule works for a pass one step back, not that it works generally. + +## Open after this pass + +- **Nine U-items are `applied` with no reproduction.** That is recorded, + not hidden, but it means nine rules rest on a ruling nobody can re-run. + Cheap to fix incrementally: each needs one scenario naming its item. +- **`applied` conflates *confirmed* with *overturned*** (§4). It will + matter the first time a ruling goes against us. +- **The cost-chain bound** (CB-EV-0019 §4) is still unwritten. diff --git a/games/ground/src/record.rs b/games/ground/src/record.rs index 10a1194..28c1703 100644 --- a/games/ground/src/record.rs +++ b/games/ground/src/record.rs @@ -153,6 +153,15 @@ pub fn to_scenario( provisional: false, provisional_owner: String::new(), provisional_raised: String::new(), + // A recording is evidence of what happened, not a claim about an + // undetermined rule, so it carries no ruling and encodes no + // U-item. Set explicitly rather than by `..Default::default()`: + // a recorded game silently inheriting a U-item claim would be a + // reproduction pointing at a finding it has nothing to do with. + ruled: String::new(), + ruled_by: String::new(), + ruled_note: String::new(), + encodes_u_item: String::new(), seed, setup: Setup { players, diff --git a/scenarios/ground/gr-a04-bond-support.yaml b/scenarios/ground/gr-a04-bond-support.yaml index f315220..7c7ecba 100644 --- a/scenarios/ground/gr-a04-bond-support.yaml +++ b/scenarios/ground/gr-a04-bond-support.yaml @@ -4,9 +4,11 @@ description: > token is readied (GR-A04, GR-F04). A Support with no relation forms no Bond, because GR-L02 requires the target's consent. covers: [GR-A04, GR-F04, GR-L02, GR-L05] -provisional: true -provisional_owner: ground-game -provisional_raised: 2026-07-31 +ruled: 2026-08-03 +ruled_by: ground-game +ruled_note: > + GROUND-WP-0002 T05/T03, 2026-08-03 — confirm: consent required; no auto-Bond from a no-relation Support. + Does NOT encode U1: U1 asks WHEN the target accepts, and this asserts only THAT consent is required. seed: 42 setup: players: 3 diff --git a/scenarios/ground/gr-d01-darvo-trigger.yaml b/scenarios/ground/gr-d01-darvo-trigger.yaml index fe99a86..3d6b356 100644 --- a/scenarios/ground/gr-d01-darvo-trigger.yaml +++ b/scenarios/ground/gr-d01-darvo-trigger.yaml @@ -4,9 +4,11 @@ description: > OFF is set to DENY (GR-D01, GR-R08). Stress clamps at 5 on every application (GR-F01 under the U2 default). covers: [GR-D01, GR-R08, GR-F01] -provisional: true -provisional_owner: ground-game -provisional_raised: 2026-07-31 +ruled: 2026-08-03 +ruled_by: ground-game +ruled_note: > + GROUND-WP-0002 T05/T03, 2026-08-03 — confirm: clamp on every application; DARVO still arms only at End (GR-R08). Encodes the U2 default. +encodes_u_item: U2 seed: 42 setup: players: 3 diff --git a/scenarios/ground/gr-d05-darvo-reverse.yaml b/scenarios/ground/gr-d05-darvo-reverse.yaml index f1e6d89..9c1bb56 100644 --- a/scenarios/ground/gr-d05-darvo-reverse.yaml +++ b/scenarios/ground/gr-d05-darvo-reverse.yaml @@ -4,9 +4,11 @@ description: > gives them +1 Stress and the owner one Protection token, then the owner takes −2 Stress and the sequence ends (GR-D05, GR-D07). covers: [GR-D05, GR-D07, GR-T01] -provisional: true -provisional_owner: ground-game -provisional_raised: 2026-07-31 +ruled: 2026-08-03 +ruled_by: ground-game +ruled_note: > + GROUND-WP-0002 T05/T03, 2026-08-03 — confirm: the printed REVERSE stage plus U5. + Does NOT encode U5: U5 is the REJECTED Reverse (GROUND—ND), and this is the unrejected stage. seed: 42 setup: players: 3 diff --git a/scenarios/ground/gr-e02-shared-ground.yaml b/scenarios/ground/gr-e02-shared-ground.yaml index 04e9aa7..ef275f2 100644 --- a/scenarios/ground/gr-e02-shared-ground.yaml +++ b/scenarios/ground/gr-e02-shared-ground.yaml @@ -4,9 +4,10 @@ description: > shared total against the player-count threshold, with a Mastery rating reduced by each Blame token and each Denied Problem. covers: [GR-R09, GR-E01, GR-E02, GR-P03] -provisional: true -provisional_owner: ground-game -provisional_raised: 2026-07-31 +ruled: 2026-08-03 +ruled_by: ground-game +ruled_note: > + GROUND-WP-0002 T03, 2026-08-03 — confirm: encode Modes.csv MODE_COOP as printed. Encodes no U-item; it was provisional on the mode data, not on an undetermined rule. seed: 42 setup: players: 3 diff --git a/scenarios/ground/gr-e04-coalitions.yaml b/scenarios/ground/gr-e04-coalitions.yaml index 663138d..8682c76 100644 --- a/scenarios/ground/gr-e04-coalitions.yaml +++ b/scenarios/ground/gr-e04-coalitions.yaml @@ -4,9 +4,10 @@ description: > Rivalries do not connect, and an unbonded player is a coalition of one. Personal scores subtract Blame (GR-E03, GR-T02). covers: [GR-E03, GR-E04, GR-O03] -provisional: true -provisional_owner: ground-game -provisional_raised: 2026-07-31 +ruled: 2026-08-03 +ruled_by: ground-game +ruled_note: > + GROUND-WP-0002 T03, 2026-08-03 — confirm: encode Modes.csv MODE_COALITION as printed. Encodes no U-item; provisional on the mode data. seed: 42 setup: players: 3 diff --git a/specs/FindingRegister.md b/specs/FindingRegister.md new file mode 100644 index 0000000..d91a9cb --- /dev/null +++ b/specs/FindingRegister.md @@ -0,0 +1,138 @@ +# The finding register + +Design findings about **GROUND**, with their reproductions. Governed by +[`GameDesign.md`](GameDesign.md) (admissibility, kinds, states, metrics) +and [ADR-0012](../decisions/ADR-0012-the-design-instrument.md). Reported +by `make design`. + +**Split out of `GroundRules.md §Underdetermined` on 2026-08-05** when that +file crossed the ~400-line loadability limit. ADR-0012 D2 said *"no new +file"* and this is a new file — but D2's substance was **one register, not +a second mechanism competing with the first**, and that holds: this *is* +§Underdetermined's register, moved, still driving off the same +`provisional`/ruling machinery. D2 also named the awkwardness this +resolves — a finding about the engine sitting in a document about the +game. + +The U-items themselves, with their defaults and rulings, stay in +[`GroundRules.md §Underdetermined`](GroundRules.md); this file tracks them +*as findings*. + +**This section is the design-finding register** (ADR-0012 D2). It was the +register for dataset ambiguities already; CB-WP-0022 extended it to all +five kinds rather than building a second one beside it. Admissibility, +kinds, states and metrics: [`GameDesign.md`](GameDesign.md). Reported by +`make design`. + + + +| id | kind | state | reproduction | role | raised | owner | +|---|---|---|---|---|---|---| +| U1 | underdetermined | applied | — | default | 2026-07-31 | ground-game | +| U2 | underdetermined | applied | scenarios/ground/gr-d01-darvo-trigger.yaml | default | 2026-07-31 | ground-game | +| U3 | underdetermined | applied | — | default | 2026-07-31 | ground-game | +| U4 | underdetermined | applied | — | default | 2026-07-31 | ground-game | +| U5 | underdetermined | applied | — | default | 2026-07-31 | ground-game | +| U6 | underdetermined | applied | — | default | 2026-07-31 | ground-game | +| U7 | underdetermined | applied | — | default | 2026-07-31 | ground-game | +| U8 | underdetermined | applied | — | default | 2026-07-31 | ground-game | +| U9 | underdetermined | applied | — | default | 2026-07-31 | ground-game | +| U10 | underdetermined | applied | — | default | 2026-07-31 | ground-game | +| F11 | inert | applied | scenarios/ground/gr-p05-solve-legality.yaml | counterexample | 2026-08-02 | clay-borg | +| F12 | degenerate | note | — | — | 2026-08-01 | clay-borg | +| F13 | inconsistent | withdrawn | scenarios/ground/gr-e01-threshold-reachable-2p.yaml | counterexample | 2026-08-01 | clay-borg | +| F14 | unplayed | note | — | — | 2026-08-01 | clay-borg | + + + +- **F11 — SOLVE offered where it cannot act.** Offered on a face-down + Problem, or with no matching suit in hand; inert every time. Ruled + GROUND-WP-0002 T02, implemented CB-WP-0023 as GR-P05. `applied` — the + rule changed, not just the annotation. **The case we reported was not the + case that fired**: `validate` already rejected face-down, and the + maintainer's three inert SOLVEs were the *hand* case. +- **F12 — GR-A13 "wasted SOLVE" on an already-claimed Problem.** A + scenario had to pick a default and did. `note`: no artifact isolates the + degenerate line, so under GameDesign §3.1 it may not be reported until + one exists. +- **F13 — GR-E01 vs GR-S01, withdrawn 2026-08-05.** Raised as *"4/6/9 + against 5/7/9, no dataset reconciles them."* `2da19a4` measured **6/9/12 + against 5/7/9** and the scenario was renamed `-unreachable-` → + `-reachable-`. **Its reproduction is green**, which under GameDesign §1.3 + is the alarm that forced the resolution. Withdrawn rather than deleted, + and the withdrawal is reported (ADR-0012 D5). +- **F14 — GR-E03/GR-E04 never played to the end.** Nineteen passes, never + played out. `note` until a trial game exists; GROUND-WP-0003 is the + playtest that would close it, and GameDesign §5's protocol makes the + recording the artifact. + +### The register's first run found ten answers nobody had collected + +**U1–U10 are `ruled`, not `reported`.** GROUND-WP-0002 T05 answered all +ten on **2026-08-03** — every one **confirmed** as the default clay-borg +already simulates — and GROUND-WP-0002 T03 confirmed five of the six +provisional scenarios, voiding `gr-e01` as a rules gap. The workplan is +`finished`. + +CB-RES-0007 reported *"0 of 10 ruled"* and this register was built saying +`reported`. **Both were two days stale on the day they were written.** The +answers had arrived and nothing propagated them — the same failure as the +unread inbox, in the opposite direction. + +**They are `ruled`, not `applied`, and the difference is work we owe.** +Per ADR-0012 D5, `applied` means the source changed and the provisional +default was deleted. The rulings *confirmed* our defaults, so the rules did +not move — but the scenarios still carry `provisional: true` for choices +that are now settled. **Lifting those flags and recording each ruling is +what closes U1–U10**, and it is not done. `make design` shows them open +until it is. + +### The U-item ↔ scenario mapping, measured twice + +**One U-item has a scenario that names it: U2.** CB-RES-0007 asserted six +of ten did. + +CB-WP-0026 T03 tried to write the other mappings and **produced two wrong +ones before checking them**: + +| claimed | why it was withdrawn | +|---|---| +| `gr-a04-bond-support` → U1 | it asserts consent is **required**; U1 asks **when** the target accepts. Different question. | +| `gr-d05-darvo-reverse` → U5 | it exercises the **unrejected** REVERSE; U5 is the **rejected** one (GROUND—ND). Different stage. | + +Both were plausible from the `covers:` list and both were wrong on +reading the description. **That is the third and fourth instance of this +exact defect** — a link that looks right, asserted without checking what +the artifact actually exercises — and the first two reached ground-game. + +So a scenario now declares `encodes_u_item` explicitly or claims nothing. +**Nine U-items have no reproduction and are recorded as having none.** +They are `applied` because the ruling landed and the provisional flag came +off, not because anything demonstrates them. + +### What the backfill measured, and what it contradicted + +**Only U2 names its U-item in a scenario.** Measured, not estimated: + +``` +for u in U1 .. U10; do grep -lE "\b$u\b" scenarios/ground/*.yaml; done +``` + +CB-RES-0007 asserted *"six of the ten already have provisional +scenarios."* Five provisional scenarios exist and **one** cites the item +it stands for. The other four may well encode U-item defaults — the +mapping is simply **not written down**, so it is not checkable, and an +uncheckable link is the defect this register exists to fix. The register +records what is citable; the rest is debt, visible in `make design`. + +**No sixth kind was needed** — the five kinds absorbed all four non-U +findings. And the survey's *"six provisional defaults"* was **not** entered +as a finding: C3 showed it double-counted GR-E01, and the provisional +scenarios are *reproductions for* underdetermined items, not a finding of +their own. + +**U1–U10 are `reported` while lacking reproductions**, which GameDesign +§3.1 would now forbid. They were reported on 2026-07-30, before the rule +existed. They are grandfathered rather than rewritten, and the debt is a +reported metric with a target of zero. + diff --git a/specs/GroundRules.md b/specs/GroundRules.md index b053a6f..8dd527d 100644 --- a/specs/GroundRules.md +++ b/specs/GroundRules.md @@ -221,151 +221,82 @@ terms. ## The finding register -**This section is the design-finding register** (ADR-0012 D2). It was the -register for dataset ambiguities already; CB-WP-0022 extended it to all -five kinds rather than building a second one beside it. Admissibility, -kinds, states and metrics: [`GameDesign.md`](GameDesign.md). Reported by -`make design`. +Moved to [`FindingRegister.md`](FindingRegister.md) on 2026-08-05 — this +file crossed the loadability limit. The U-items below are its +`underdetermined` rows; `make design` reports over it. - +## Underdetermined in dataset 0.1 — RULED 2026-08-03, applied 2026-08-05 -| id | kind | state | reproduction | role | raised | owner | -|---|---|---|---|---|---|---| -| U1 | underdetermined | ruled | — | default | 2026-07-31 | ground-game | -| U2 | underdetermined | ruled | scenarios/ground/gr-d01-darvo-trigger.yaml | default | 2026-07-31 | ground-game | -| U3 | underdetermined | ruled | — | default | 2026-07-31 | ground-game | -| U4 | underdetermined | ruled | — | default | 2026-07-31 | ground-game | -| U5 | underdetermined | ruled | — | default | 2026-07-31 | ground-game | -| U6 | underdetermined | ruled | — | default | 2026-07-31 | ground-game | -| U7 | underdetermined | ruled | — | default | 2026-07-31 | ground-game | -| U8 | underdetermined | ruled | — | default | 2026-07-31 | ground-game | -| U9 | underdetermined | ruled | — | default | 2026-07-31 | ground-game | -| U10 | underdetermined | ruled | — | default | 2026-07-31 | ground-game | -| F11 | inert | applied | scenarios/ground/gr-p05-solve-legality.yaml | counterexample | 2026-08-02 | clay-borg | -| F12 | degenerate | note | — | — | 2026-08-01 | clay-borg | -| F13 | inconsistent | withdrawn | scenarios/ground/gr-e01-threshold-reachable-2p.yaml | counterexample | 2026-08-01 | clay-borg | -| F14 | unplayed | note | — | — | 2026-08-01 | clay-borg | +**Owner: ground-game. Raised: 2026-07-31. Ruled 2026-08-03** +(GROUND-WP-0002 T05): **all ten confirmed** as the defaults clay-borg +already simulates. Applied here 2026-08-05 (CB-WP-0026). - +**Nothing below awaits a ruling.** The defaults are now the rules for +dataset 0.1, and the `provisional: true` flags they carried have been +lifted — a flag that outlives its question is a lie with a timestamp. -- **F11 — SOLVE offered where it cannot act.** Offered on a face-down - Problem, or with no matching suit in hand; inert every time. Ruled - GROUND-WP-0002 T02, implemented CB-WP-0023 as GR-P05. `applied` — the - rule changed, not just the annotation. **The case we reported was not the - case that fired**: `validate` already rejected face-down, and the - maintainer's three inert SOLVEs were the *hand* case. -- **F12 — GR-A13 "wasted SOLVE" on an already-claimed Problem.** A - scenario had to pick a default and did. `note`: no artifact isolates the - degenerate line, so under GameDesign §3.1 it may not be reported until - one exists. -- **F13 — GR-E01 vs GR-S01, withdrawn 2026-08-05.** Raised as *"4/6/9 - against 5/7/9, no dataset reconciles them."* `2da19a4` measured **6/9/12 - against 5/7/9** and the scenario was renamed `-unreachable-` → - `-reachable-`. **Its reproduction is green**, which under GameDesign §1.3 - is the alarm that forced the resolution. Withdrawn rather than deleted, - and the withdrawal is reported (ADR-0012 D5). -- **F14 — GR-E03/GR-E04 never played to the end.** Nineteen passes, never - played out. `note` until a trial game exists; GROUND-WP-0003 is the - playtest that would close it, and GameDesign §5's protocol makes the - recording the artifact. +**No kernel behaviour changed**, because every ruling was a confirmation. +What changed is that these stopped being our guesses. -### The register's first run found ten answers nobody had collected +> **Three rulings carry more than "confirm", quoted rather than +> paraphrased.** **U1**: the Bond formed by *this* Support still does not +> cancel DARVO (printed designer note — this is what GR-L05 encodes). +> **U2**: clamp on every application, but **DARVO still arms only at End** +> when at Stress 5 (GR-R08) — the clamping rule and the trigger rule are +> separate. **U8**: GROUND—OU applies first and **Protection is consumed +> only if it actually cancels**. +> +> ground-game flagged **U2, U6 and U8** as the three that change play, +> because each decides whether a DARVO stage fires at all. -**U1–U10 are `ruled`, not `reported`.** GROUND-WP-0002 T05 answered all -ten on **2026-08-03** — every one **confirmed** as the default clay-borg -already simulates — and GROUND-WP-0002 T03 confirmed five of the six -provisional scenarios, voiding `gr-e01` as a rules gap. The workplan is -`finished`. - -CB-RES-0007 reported *"0 of 10 ruled"* and this register was built saying -`reported`. **Both were two days stale on the day they were written.** The -answers had arrived and nothing propagated them — the same failure as the -unread inbox, in the opposite direction. - -**They are `ruled`, not `applied`, and the difference is work we owe.** -Per ADR-0012 D5, `applied` means the source changed and the provisional -default was deleted. The rulings *confirmed* our defaults, so the rules did -not move — but the scenarios still carry `provisional: true` for choices -that are now settled. **Lifting those flags and recording each ruling is -what closes U1–U10**, and it is not done. `make design` shows them open -until it is. - -### What the backfill measured, and what it contradicted - -**Only U2 names its U-item in a scenario.** Measured, not estimated: - -``` -for u in U1 .. U10; do grep -lE "\b$u\b" scenarios/ground/*.yaml; done -``` - -CB-RES-0007 asserted *"six of the ten already have provisional -scenarios."* Five provisional scenarios exist and **one** cites the item -it stands for. The other four may well encode U-item defaults — the -mapping is simply **not written down**, so it is not checkable, and an -uncheckable link is the defect this register exists to fix. The register -records what is citable; the rest is debt, visible in `make design`. - -**No sixth kind was needed** — the five kinds absorbed all four non-U -findings. And the survey's *"six provisional defaults"* was **not** entered -as a finding: C3 showed it double-counted GR-E01, and the provisional -scenarios are *reproductions for* underdetermined items, not a finding of -their own. - -**U1–U10 are `reported` while lacking reproductions**, which GameDesign -§3.1 would now forbid. They were reported on 2026-07-30, before the rule -existed. They are grandfathered rather than rewritten, and the debt is a -reported metric with a target of zero. - -## Underdetermined in dataset 0.1 — PROVISIONAL defaults (flag to ground-game) - -**Owner: ground-game. Raised: 2026-07-31.** Every U-item carries an owner -and a raise date so its age is visible; `make coverage` reports both and -warns past 30 days. CI **warns rather than breaks** — the ruling belongs -to ground-game and the kernel cannot make it for them — but every evidence -file that depends on one must list it (CB-WP-0003 T08). - -A ruling flips the *scenario*, not the kernel: the default is encoded in a -scenario tagged `provisional: true`, so a correction is a data change. - -Formalization exposed points the dataset does not decide. Simulation uses -the stated default; each is tagged in scenarios that depend on it and must -be confirmed or corrected by ground-game. +Formalization exposed points the dataset did not decide. Each is stated +with its default and the ruling that settled it. - **U1 Bond acceptance timing (GR-L02):** when does the target "accept"? Default: a decision point during the Support resolution, target decides with full knowledge of revealed Actions. + **RULED 2026-08-03 — confirm.** Target accepts during Support resolution, with full knowledge of revealed Actions. A Bond formed by *this* Support still does not cancel DARVO (printed designer note; GR-L05). - **U2 Stress clamping during resolution (GR-F01):** dataset caps Stress at End; mats physically cap at 0–5. Default: clamp on every application (no intermediate values above 5/below 0). Note this interacts with DARVO triggering (a mid-round spike to 5 that is reduced again before End does not trigger). + **RULED 2026-08-03 — confirm.** Clamp Stress to 0–5 on **every** application; matches the physical mats. DARVO still arms **only at End** when at Stress 5 (GR-R08). - **U3 DENY with no legal target (GR-D03):** if no face-up unsolved unprotected Problem exists, default: the stage resolves as a no-op and the sequence still advances. + **RULED 2026-08-03 — confirm.** No-op; the sequence still advances. - **U4 Solution deck exhaustion (GR-A01):** default: reshuffle the discard into a new deck deterministically; if both are empty, the draw is skipped. + **RULED 2026-08-03 — confirm.** Deterministic reshuffle of the discard into a new deck; if both are empty, skip the draw. - **U5 Rejected REVERSE — self-effects (GR-D05):** the Actions sheet says GROUND—ND "rejects one Reverse"; the DARVO sheet orders the −2 self-Stress after the flip/+1/Protection block. Default: rejection cancels the effects on the target (no Blame, no +1) **and** the Protection gain, but the sequence owner still takes −2 Stress and the sequence still ends; the Focus token returns unflipped. + **RULED 2026-08-03 — confirm.** Rejection cancels the target effects (no Blame, no +1, no Protection) **and** the owner still takes −2 Stress and the sequence ends. - **U6 Simultaneous Bond-Support and stage cancellation (GR-A04, GR-R06):** Support (step 2) resolves before DARVO stages (step 3), so a Bond Support in the same round cancels that round's stage before it fires. Stated as the intended reading; confirm. + **RULED 2026-08-03 — confirm.** A pre-existing Bond Support (step 2) cancels that round's DARVO stage before it fires (step 3) and ends the sequence. - **U7 Focus target elimination:** dataset has no player elimination, so no rule needed; confirm none exists. + **RULED 2026-08-03 — confirm.** No player elimination. - **U8 Multiple Attack cancellation sources (GR-A09):** if both Protection and GROUND—OU could cancel the same Attack, default: GROUND—OU (chosen at step 1) applies first; Protection is consumed only if it actually cancels. + **RULED 2026-08-03 — confirm.** GROUND—OU applies first; Protection is consumed **only if it actually cancels**. - **U9 Two players at Stress 5 at the same End (GR-R08):** both trigger; their stages resolve in Lead order in later rounds. No interaction rule needed; confirm. + **RULED 2026-08-03 — confirm.** Both trigger; later stages resolve in Lead order. No extra interaction rule. - **U10 Hand limits:** none stated. Default: no hand limit. + **RULED 2026-08-03 — confirm.** No hand limit in r0. Revisit only if playtest shows bloat. ## Scenario coverage obligation Every GR-rule above must be covered by ≥1 scenario under diff --git a/tools/design-baseline.py b/tools/design-baseline.py index 46b4551..256c400 100755 --- a/tools/design-baseline.py +++ b/tools/design-baseline.py @@ -47,11 +47,19 @@ FINDINGS = { "evidence/CB-EV-0007-stage-0.md", "scenarios/ground/gr-e01-threshold-reachable-2p.yaml", "workplans/CB-WP-0021-import-the-edition.md"], - "six provisional defaults": sorted( - os.path.join("scenarios/ground", f) - for f in os.listdir("scenarios/ground") - if f.endswith(".yaml") - and "provisional: true" in open(os.path.join("scenarios/ground", f)).read()), + # FROZEN 2026-08-05. This globbed `provisional: true` live, so it was + # never a snapshot -- it drifted with the repo. CB-WP-0026 lifted the + # last five flags (ground-game ruled them 2026-08-03) and the glob went + # empty, turning this file's own pinned self-test red at 1/6. A dated + # snapshot that recomputes is a contradiction; the literal list it + # measured on 2026-08-03 is what belongs here. + "six provisional defaults": [ + "scenarios/ground/gr-a04-bond-support.yaml", + "scenarios/ground/gr-d01-darvo-trigger.yaml", + "scenarios/ground/gr-d05-darvo-reverse.yaml", + "scenarios/ground/gr-e02-shared-ground.yaml", + "scenarios/ground/gr-e04-coalitions.yaml", + ], "GR-E03/GR-E04 never played": ["evidence/CB-EV-0007-stage-0.md"], } diff --git a/tools/design.py b/tools/design.py index 730a653..4ef04b3 100644 --- a/tools/design.py +++ b/tools/design.py @@ -2,7 +2,7 @@ """design — report the finding register (CB-WP-0022 T05). Implements the metrics in `specs/GameDesign.md` §4 over the register in -`specs/GroundRules.md`. ADR-0012 D8 retired `design-baseline.py`, which +`specs/FindingRegister.md`. ADR-0012 D8 retired `design-baseline.py`, which was a hand-maintained dict counting itself; the difference that matters is that **every number here is computed over rows that name real files, and the reproduction check stats the file.** @@ -16,7 +16,7 @@ directly: a row citing a nonexistent file must NOT count as reproduced. import os, re, sys, subprocess, datetime ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -REGISTER = os.path.join(ROOT, "specs", "GroundRules.md") +REGISTER = os.path.join(ROOT, "specs", "FindingRegister.md") BEGIN = "" END = "" @@ -94,7 +94,7 @@ def alarming(row, root=ROOT): def report(root=ROOT, today=None): today = today or datetime.date.today() - rows = parse(open(os.path.join(root, "specs", "GroundRules.md")).read()) + rows = parse(open(os.path.join(root, "specs", "FindingRegister.md")).read()) findings = [r for r in rows if r["state"] != NOTE] notes = [r for r in rows if r["state"] == NOTE] @@ -179,12 +179,31 @@ def self_test(): all(r["role"] in ("counterexample", "default") for r in rows if r["repro"] not in ("—", "-", ""))) - # The measured claim in the backfill: only U2 cites its U-item. - cited = [u for u in [f"U{i}" for i in range(1, 11)] - if subprocess.run(["grep", "-rlE", rf"\b{u}\b"] + - [os.path.join(ROOT, "scenarios", "ground")], - capture_output=True, text=True, cwd=ROOT).stdout.strip()] - check("exactly one U-item is cited by a scenario", cited == ["U2"], f"{cited}") + # CB-WP-0026 T03. A scenario may claim a U-item only if it names it, + # because two plausible mappings were written and both were wrong on + # reading what the scenario actually exercises (gr-a04 asserts consent + # is required, not U1's timing; gr-d05 is the unrejected REVERSE, not + # U5's rejected one). A claim nobody can check is how three wrong + # premises reached ground-game. + import glob + for path in glob.glob(os.path.join(ROOT, "scenarios", "ground", "*.yaml")): + body = open(path).read() + m = re.search(r"^encodes_u_item:\s*(\S+)", body, re.M) + if m: + check(f"{os.path.basename(path)} names the U-item it claims", + re.search(rf"\b{m.group(1)}\b", body) is not None, m.group(1)) + + # The measured claim: exactly one U-item has a scenario encoding it. + # + # **Asserted on the DECLARATION, not on a mention.** The first version + # grepped for `\bU\b` and went red the moment two scenarios recorded + # *why they do not* encode U1 and U5 — a mention is not a claim, and a + # measurement that cannot tell them apart is the loose proxy that let + # "six of the ten have provisional scenarios" stand unchallenged. + claimed = sorted({re.search(r"^encodes_u_item:\s*(\S+)", open(p).read(), re.M).group(1) + for p in glob.glob(os.path.join(ROOT, "scenarios", "ground", "*.yaml")) + if re.search(r"^encodes_u_item:\s*\S+", open(p).read(), re.M)}) + check("exactly one U-item is encoded by a scenario", claimed == ["U2"], f"{claimed}") print("design self-test (positive control)") return 0 if ok else 1 diff --git a/tools/rule-coverage.py b/tools/rule-coverage.py index d564974..42bf144 100755 --- a/tools/rule-coverage.py +++ b/tools/rule-coverage.py @@ -154,11 +154,39 @@ def self_test(): check("code-id matcher finds none in unmarked source", parse_code_ids("fn f() { let x = 1; }") == set()) # T08: every provisional scenario must carry an owner and a date. - import glob as _g - prov = provisional_items(sorted(_g.glob("scenarios/ground/*.yaml"))) - check("every provisional item has an owner and a raised date", - bool(prov) and all(o and r for _, o, r in prov), - f"{len(prov)} provisional item(s)") + # + # **Tested against a fixture, not against the repo.** This control used + # to read `scenarios/ground/*.yaml` and require `bool(prov)` — a guard + # against a vacuous pass, which is the right instinct, but it made the + # control depend on the repo *having* provisional items. CB-WP-0026 + # lifted the last five (ground-game ruled them all on 2026-08-03) and + # the control went red for the best possible reason: nothing is + # provisional any more. + # + # A positive control must construct the condition it tests. The live + # count is a diagnostic below, not a pass criterion — there is no + # number of provisional items this project *should* have. + import glob as _g, tempfile as _tf, os as _os + with _tf.TemporaryDirectory() as _d: + good = _os.path.join(_d, "good.yaml") + bad = _os.path.join(_d, "bad.yaml") + open(good, "w").write("scenario: x\nprovisional: true\n" + "provisional_owner: ground-game\n" + "provisional_raised: 2026-07-31\n") + open(bad, "w").write("scenario: y\nprovisional: true\n") + g = provisional_items([good]) + b = provisional_items([bad]) + check("a provisional item with an owner and a date passes", + len(g) == 1 and all(o and r for _, o, r in g)) + check("a provisional item MISSING its owner/date is caught", + len(b) == 1 and not all(o and r for _, o, r in b), + "without this the check passes on anything it can parse") + + live = provisional_items(sorted(_g.glob("scenarios/ground/*.yaml"))) + check("every LIVE provisional item has an owner and a raised date", + all(o and r for _, o, r in live), + f"{len(live)} provisional item(s)" + + (" — none; all ruled 2026-08-03 (CB-WP-0026)" if not live else "")) # --- CB-WP-0005 T01: the kernel denominator --- # The control the old arm was fixed for, replicated. A pattern that diff --git a/workplans/CB-WP-0026-collect-the-rulings.md b/workplans/CB-WP-0026-collect-the-rulings.md new file mode 100644 index 0000000..a160d9a --- /dev/null +++ b/workplans/CB-WP-0026-collect-the-rulings.md @@ -0,0 +1,286 @@ +--- +id: CB-WP-0026 +kind: product +title: "Collect the rulings: ten answers that arrived and were never applied" +status: done +--- + +# Purpose + +``` +structural tier S (applies rulings inside an existing capability — + scenario data and a spec section, no new port, no + canonical interface, no dependency) +chaos d8 = 6 → no override +declared tier S +``` + +Declaration 9 of chaos window 2. + +## The register's first run found this, which is the only reason it exists + +`ground-game` **ruled all ten U-items on 2026-08-03** (GROUND-WP-0002 T05) +and confirmed five of six provisional scenarios (T03). Every U-item was +**confirmed** as the default clay-borg already simulates. The workplan is +`finished`. + +**clay-borg never collected the answers.** CB-RES-0007 reported *"0 of 10 +ruled"* on 2026-08-03 — the same day — and CB-WP-0022 built the finding +register two days later still recording them as `reported`. It took +`make design`'s first run to notice. + +**This is the unread-inbox failure running in the opposite direction.** +The whole design-instrument pass was shaped around *we send findings and +nobody reads them*. The symmetric failure — *they answer and we do not +collect it* — was not anticipated by the declaration, the survey, or the +adversarial review. + +**Nothing here is a rules decision.** Every ruling is already made. This is +clay-borg applying them, which is the `ruled` → `applied` transition +ADR-0012 D5 defines: *the source changed and the provisional default was +deleted.* + +## Why the rulings being "confirm" makes this more subtle, not less + +Every U-item was confirmed, so **no kernel behaviour changes**. It is +tempting to conclude nothing needs doing. + +That is wrong, and the register is what makes it visible: the scenarios +still carry `provisional: true` for choices that are now **settled**. A +provisional flag means *we picked this and the owner has not ruled*. After +2026-08-03 that statement is false in five files, and `make coverage` +keeps ageing them toward a 30-day warning for a question that has been +answered. + +**A flag that outlives its question is the same defect as a hash that +outlives its content** — ADR-0011's *"a lie with a timestamp."* + +## Task: record the rulings where the defaults live + +```task +id: CB-WP-0026-T01 +status: done +priority: high +``` + +`specs/GroundRules.md §Underdetermined` states ten defaults as *"must be +confirmed or corrected by ground-game."* All ten now are. Each U-item +gains its ruling and the date, from GROUND-WP-0002 T05. + +**Quote the ruling, do not paraphrase it.** Three carry notes that are +more than "confirm" and a paraphrase would lose them: + +- **U1** — the Bond formed by *this* Support still does not cancel DARVO + (a printed designer note, and it is the interaction GR-L05 encodes); +- **U2** — clamp on every application, but **DARVO still arms only at End** + when at Stress 5 (GR-R08). The clamping rule and the trigger rule are + separate, and conflating them is the obvious misreading; +- **U8** — GROUND—OU applies first and **Protection is consumed only if it + actually cancels**. + +**Controls:** +- the §Underdetermined preamble no longer says the items await a ruling — + it is the sentence that will otherwise be true-looking and wrong; +- **U2, U6 and U8 are the ones ground-game flagged as changing play.** + Each must be checkable against the kernel behaviour it describes, or the + record says a rule is implemented that may not be. + +**Done 2026-08-05.** All ten rulings quoted into `§Underdetermined`, with +the three conditional ones (U1's designer note, U2's End-only trigger, +U8's consume-only-if-it-cancels) verbatim. The section heading and +preamble no longer say the items await a ruling. + +## Task: lift the provisional flags the rulings settle + +```task +id: CB-WP-0026-T02 +status: done +priority: high +``` + +Five scenarios carry `provisional: true` with `provisional_owner: +ground-game`: `gr-a04-bond-support`, `gr-d01-darvo-trigger`, +`gr-d05-darvo-reverse`, `gr-e02-shared-ground`, `gr-e04-coalitions`. All +five were **confirmed** on 2026-08-03. The flag comes off, and each +records which ruling settled it. + +**Do not delete the provenance with the flag.** CB-WP-0021 T03 is the +precedent: `gr-e01` was *rewritten, not retired*, because a reader needs +to learn the question was answered rather than that a tag vanished. + +**Controls:** +- `make coverage` reports **0 provisional items** afterwards, and that is + the measurable outcome of this task; +- `make sim` still passes — a confirmed default is the behaviour already + implemented, so a red scenario here means the default was **not** what + we told ground-game it was, which is a finding and must be raised, not + patched; +- the ruling date survives in each file. + +**Done 2026-08-05.** All five flags lifted; `make coverage` reports **0 +provisional items**. Each file gained `ruled` / `ruled_by` / `ruled_note` +instead — the flag came off, the provenance did not. + +**The schema refused the new fields and was right to.** `ScenarioFile` is +`deny_unknown_fields`, so five scenarios failed to parse until `ruled`, +`ruled_by`, `ruled_note` and `encodes_u_item` were declared. A scenario +corpus that silently accepted unknown metadata would let a typo'd +`encodes_u_iem` sit there claiming nothing. + +**T02's control held: `make sim` is 26 passed, 59 rules covered, with no +scenario red.** That is the answer to the serious version of this task — +had one gone red, it would have meant we described our own behaviour +incorrectly to ground-game, which would be a defect in *our* report rather +than in their ruling. + +## Task: make the U-item ↔ scenario mapping checkable + +```task +id: CB-WP-0026-T03 +status: done +priority: high +``` + +**Only U2 names its U-item in a scenario.** Measured in CB-WP-0022 T05: + +``` +for u in U1..U10; do grep -lE "\b$u\b" scenarios/ground/*.yaml; done +``` + +CB-RES-0007 asserted *"six of the ten already have provisional +scenarios."* Four probably do — but **the link is not written down, so it +is not checkable**, and an uncheckable link asserted as a fact is the +defect class that produced three wrong premises. + +Each scenario that encodes a U-item default names it. Where no scenario +covers a U-item, **say so** rather than inventing a mapping — that is the +honest half, and guessing here would reproduce exactly the error being +fixed. + +**Controls:** +- a check that every U-item claiming a reproduction resolves to a file + that **names it** — `design.py`'s self-test already asserts the current + state (`cited == ["U2"]`) and must be updated to the new truth, not + deleted; +- the register's reproduction column matches the mapping, since a register + that disagrees with the files is worse than none. + +**Done 2026-08-05, and it caught me writing the same defect a third and +fourth time.** + +I wrote two mappings from the `covers:` lists — `gr-a04` → U1, `gr-d05` → +U5 — and both are wrong on reading what the scenarios actually exercise: + +| claimed | why withdrawn | +|---|---| +| `gr-a04` → U1 | it asserts consent is **required**; U1 asks **when** the target accepts | +| `gr-d05` → U5 | it exercises the **unrejected** REVERSE; U5 is the **rejected** one (GROUND—ND) | + +Both were plausible from `covers:` and neither survived reading the +description. **The measurement stands: one U-item, U2.** Nine have no +reproduction and are recorded as having none. + +`encodes_u_item` is now a declaration, and `design.py` asserts a scenario +naming a U-item actually contains it. **The check's first version was +itself the loose proxy**: it grepped for `\bU\b` and went red when two +files recorded *why they do not* encode U1 and U5. A mention is not a +claim — and that is exactly the looseness that let *"six of the ten have +provisional scenarios"* stand unchallenged. + +## Task: close the register, and see whether it closes + +```task +id: CB-WP-0026-T04 +status: done +priority: high +``` + +Move U1–U10 from `ruled` to `applied` and re-run `make design`. + +**The number to beat is `open, lacking a reproduction: 9 → 0`**, and the +queue emptying is the first evidence that ADR-0012 D5's lifecycle is real +rather than drawn. + +**If it does not reach 0, do not adjust the metric.** Whatever remains is +either a U-item with no scenario (T03's honest half, which should then be +a `note`, not a finding) or a state the lifecycle cannot express — and the +second is a finding about ADR-0012 that belongs in evidence. + +**Controls:** +- `make design` shows an empty queue for the U-items, with the log + carrying all ten; +- `make self-tests` and `make sim` pass; +- **the closure is checkable from the register alone** — a reader should + not need this workplan to see that ten findings closed on 2026-08-03 and + were applied on 2026-08-05. + +**Done 2026-08-05. The queue is empty.** + +``` +QUEUE (open findings) (none) +open, lacking a reproduction 0 target 0 +closed (log) 12 [U1..U10, F11, F13] +with a resolving reproduction 3/12 = 25% +``` + +**`open, lacking a reproduction: 9 → 0`** — the number this task named. +It is the first evidence that ADR-0012 D5's lifecycle is real rather than +drawn: findings entered a state, moved through it, and left the queue. + +**25% reproduced is not a failure and must not be read as one.** Nine +U-items closed by a *ruling*, and a ruling is not an artifact. The metric +is now honest about a thing the survey's 33% concealed: most of our +findings closed because someone answered them, not because anything +demonstrated them. + +## Task: evidence + +```task +id: CB-WP-0026-T05 +status: done +priority: medium +``` + +`evidence/CB-EV-0022-*.md`. Short — this is an S pass. + +- **How long the answers sat uncollected**, and what noticed them. Two + days, and the thing that noticed was the register's first run, not a + human and not the review. +- **Whether any confirmed default turned out not to match the kernel** — + T02's control. A red scenario here would mean we described our own + behaviour incorrectly to ground-game, which would be a fourth premise + defect and the most serious kind, since it was *our* behaviour. +- **What the mapping gap really was**: whether the four unlinked scenarios + encode U-item defaults at all, or whether CB-RES-0007's "six of ten" was + simply wrong. +- **Whether the queue reached 0**, and if not, what the lifecycle could not + express. +- **Quote CB-WP-0022's cost by re-running the instrument** — and note that + CB-EV-0019 §4 found this chain snaps at roughly four passes, so if the + instrument aborts, that is the second instance and the rule needs its + bound. + +**Done 2026-08-05.** +[CB-EV-0022](../evidence/CB-EV-0022-collect-the-rulings.md). + +- **Two days uncollected, and the register's first run is what noticed** — + not a human, not the adversarial review that found four other things. +- **No confirmed default failed to match the kernel**: `make sim` 26 + passed, 59 rules covered, nothing red. The serious version of this task + came back clean. +- **The mapping gap was not under-documentation, it was error.** At least + two of the four unlinked scenarios do **not** encode the U-items the + survey implied, and I wrote both wrong mappings before checking them. +- **The queue reached 0.** `applied` conflates *confirmed* with + *overturned*, which is the lifecycle gap this pass found and did not fix. +- **The cost chain held this time** — CB-WP-0022 is one pass back and + still in the window. The bound CB-EV-0019 asked for is still owed. + +**A gate went red for the best possible reason.** `rule-coverage.py`'s +positive control required `bool(prov)` — at least one provisional item to +exist. Lifting the last five turned it red. The guard was the right +instinct (a vacuous pass is worthless) wired the wrong way: it bound a +control to live repo data. It now builds its own fixture, asserts the +missing-owner case is *caught*, and reports the live count as a diagnostic +— because there is no number of provisional items this project should +have.