diff --git a/Makefile b/Makefile index 7db140f..63d0495 100644 --- a/Makefile +++ b/Makefile @@ -124,6 +124,7 @@ 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/WORK-RECORDS.md b/WORK-RECORDS.md index 854d421..5114c75 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -29,7 +29,7 @@ | workplan | CB-WP-0019 | done | — | workplans/CB-WP-0019-the-am4-family.md | | workplan | CB-WP-0020 | done | — | workplans/CB-WP-0020-the-table-you-can-read.md | | workplan | CB-WP-0021 | active | — | workplans/CB-WP-0021-import-the-edition.md | -| workplan | CB-WP-0022 | done | — | workplans/CB-WP-0022-the-design-instrument.md | +| workplan | CB-WP-0022 | active | — | workplans/CB-WP-0022-the-design-instrument.md | | workplan | CB-WP-0023 | done | — | workplans/CB-WP-0023-solve-legality.md | | workplan | CB-WP-0024 | ready | — | workplans/CB-WP-0024-the-table-you-can-watch.md | | workplan | CB-WP-0025 | ready | — | workplans/CB-WP-0025-could-we-have-won.md | @@ -147,12 +147,12 @@ | task | CB-WP-0021-T05 | done | — | workplans/CB-WP-0021-import-the-edition.md | | task | CB-WP-0021-T06 | done | — | workplans/CB-WP-0021-import-the-edition.md | | task | CB-WP-0022-T01 | done | — | workplans/CB-WP-0022-the-design-instrument.md | -| task | CB-WP-0022-T02 | done | — | workplans/CB-WP-0022-the-design-instrument.md | -| task | CB-WP-0022-T03 | done | — | workplans/CB-WP-0022-the-design-instrument.md | -| task | CB-WP-0022-T04 | done | — | workplans/CB-WP-0022-the-design-instrument.md | -| task | CB-WP-0022-T05 | done | — | workplans/CB-WP-0022-the-design-instrument.md | -| task | CB-WP-0022-T06 | done | — | workplans/CB-WP-0022-the-design-instrument.md | -| task | CB-WP-0022-T07 | done | — | workplans/CB-WP-0022-the-design-instrument.md | +| task | CB-WP-0022-T02 | todo | — | workplans/CB-WP-0022-the-design-instrument.md | +| task | CB-WP-0022-T03 | todo | — | workplans/CB-WP-0022-the-design-instrument.md | +| task | CB-WP-0022-T04 | todo | — | workplans/CB-WP-0022-the-design-instrument.md | +| task | CB-WP-0022-T05 | todo | — | workplans/CB-WP-0022-the-design-instrument.md | +| task | CB-WP-0022-T06 | todo | — | workplans/CB-WP-0022-the-design-instrument.md | +| task | CB-WP-0022-T07 | todo | — | workplans/CB-WP-0022-the-design-instrument.md | | task | CB-WP-0023-T01 | done | — | workplans/CB-WP-0023-solve-legality.md | | task | CB-WP-0023-T02 | done | — | workplans/CB-WP-0023-solve-legality.md | | task | CB-WP-0023-T03 | done | — | workplans/CB-WP-0023-solve-legality.md | diff --git a/crates/cb-game-runtime/src/scenario.rs b/crates/cb-game-runtime/src/scenario.rs index 8bf225c..a34ca40 100644 --- a/crates/cb-game-runtime/src/scenario.rs +++ b/crates/cb-game-runtime/src/scenario.rs @@ -31,30 +31,6 @@ 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-0019-import-the-edition.md b/evidence/CB-EV-0019-import-the-edition.md deleted file mode 100644 index 62d598c..0000000 --- a/evidence/CB-EV-0019-import-the-edition.md +++ /dev/null @@ -1,176 +0,0 @@ -# CB-EV-0019 — import the edition - -CB-WP-0021 T04. Tier M (structural M — adds or refuses an external -dependency and changes how a game is set up; chaos d8=7 → no override). -Closed 2026-08-05, four days after the code landed. - -**Delivered:** [ADR-0011](../decisions/ADR-0011-vendor-the-edition.md), -`editions/ground-darvo-r0/Problems.csv` with a digest, a ~150-line reader -in `games/ground/src/edition.rs`, `tools/edition-check.py`, and the ruled -deal — Surface ∪ hidden `1..=k`. - ---- - -## 1. What the import cost, and the budget it was measured against - -**The declaration's constraint was measured against the wrong -denominator, and the ADR corrected it.** CB-WP-0021 said a CSV crate costs -21,613 lines against AM-4a's **3,798** of headroom — *"5.7× over, settled -by measurement rather than preference."* - -`setup` and `problem_priorities` are `#[cfg(feature = "scenarios")]`. -**They are not in the shipped runtime**, so AM-4a never sees them. Against -AM-4b's graph: - -| | lines | -|---|---:| -| AM-4b headroom | 19,742 | -| `csv` marginal (`csv` + `csv-core`; `ryu`/`itoa`/`memchr` already present) | **17,651** | - -**It fits, with 2,091 to spare — and was refused anyway**, on proportion: -89% of everything the budget had left, to read 20 rows. What shipped is a -hand-rolled reader we own. - -**That is the third premise this pass had to correct**, and the second -where a real number was computed against a mis-chosen denominator. The -pattern is now well enough attested to name: *this project's arithmetic -errors are not in the arithmetic.* CB-WP-0022 T02 produced two more of the -same shape four days later. - -Current dependency position, `make dep-weight`: - -``` -AM-4c shipped-runtime 7,976 own lines per 100k third-party (diagnostic) -AM-4c dev-toolchain 1,729 own lines per 100k third-party (diagnostic) -``` - -## 2. What broke — and the hash blast radius was predicted, not discovered - -The task asked which it was. **Predicted, and then measured to be almost -nothing.** - -T01's brief said Problem values and suits enter `GroundState`, which is -hashed (K7), so *"every recorded state hash changes"*, and required the -blast radius be established **before writing code**. ADR-0011 §Decision 3 -did exactly that: - -| | | -|---|---:| -| scenario files pinning a state hash | **0** | - -So the feared radius was ~0. Scenarios assert on state *blocks*, not -hashes; `replay-test` re-executes bundles and restores them; AM-7's probe -asserts per-segment hashes within a run rather than against recorded -literals. - -**No hash was grandfathered and none recorded as "was"** — ADR-0011's -words: *a recorded hash that outliving the content it describes is a lie -with a timestamp.* - -Verified today: - -- `make replay-test` — **14/14 controls passed**, including *"the bundle - still replays after every control restored it"*; -- `make am7` — scaling **0.985 / 0.998 / 1.018** (worst/median/best of 9) - against a 0.9 floor. Green, after T06 fixed the *measurement* rather - than lowering the floor. - -**What did break** was the scenario corpus's data assumptions: six -scenario files needed value/suit updates (`gr-a02-solve`, -`gr-e03-common-problem`, `gr-e04-coalitions`, `gr-f02-no-gate`, -`gr-p05-solve-legality`) because they encoded stand-in numbers. That is -the expected cost of replacing invented data with real data, and every one -was a data edit rather than a logic change. - -## 3. Do the endings mean anything now? Yes — and the margin is one point - -This is the question the pass existed for. Played today, 2 players, -all-bots: - -``` -OUTCOME total 4 / threshold 5 group failure - personal: P1 2 P2 2 - mastery 2 - problems: [1] Repair 2 claimed by P2 [2] Clarify 2 [3] Boundary 2 claimed by P1 -``` - -**Three Problems worth 2 each — 6 available against a threshold of 5.** -The bots claimed two of three, scored 4, and lost by one point. - -**This confirms T02's control by play.** That control said: *"at 2p that -means a **full clear**, since 2+2+2 against 5 leaves no slack."* It is -exactly right — at two seats, leaving any single Problem unclaimed loses -the game. The endings are not merely reachable; they are **tight**, which -is a far better answer than "reachable" and was not guaranteed by the -ruling. - -**And it retires the finding this pass was built around.** GR-E01 was -raised as *"unreachable below 5 seats"* on 4/6/9 against 5/7/9. The real -deal is 6/9/12 against 5/7/9. `gr-e01-threshold-unreachable-2p` is now -`gr-e01-threshold-reachable-2p`, non-provisional, and the finding was -**formally withdrawn to ground-game on 2026-08-05** (CB-WP-0022 T06) — -because it took an adversarial review, four days later, for anyone to -notice that this pass had already falsified it. - -**That is the sharpest thing in this evidence file.** The work that -disproved GR-E01 is *in this workplan*. The workplan that quoted GR-E01 as -its founding example was written the next day. Nothing connected them -until a separate agent ran `git log`. - -## 4. Quoting CB-WP-0020's cost — the instrument refuses - -T04 asked for CB-WP-0020's cost by re-running the instrument. It cannot be -supplied: - -``` -$ python3 tools/cb-cost.py --slug CB-WP-0020 --by-task -ABORT — no transcripts found for CB-WP-0020 -``` - -**The instrument aborts rather than estimating, which is correct -behaviour** and is why the number is absent instead of wrong. CB-WP-0020's -transcripts are outside the retained window. - -What the instrument does report, run today: - -``` -main $ 548.03 output 2,125,069 tok $ 56.16 10.1% -subagent tree $ 5.50 cache_read 750,152,134 tok $394.44 71.3% -TOTAL $ 553.53 write_1h 9,233,711 tok $ 97.48 17.6% - - UNATTRIBUTED $230.34 41.6% - CB-WP-0021-T01 $ 17.15 3.1% PROVISIONAL — this pass's own - CB-WP-0021-T06 $ 14.31 2.6% PROVISIONAL — this pass's own -``` - -**The two CB-WP-0021 rows are provisional and must not be quoted as -final.** They are this pass's own spend, measured while writing this file, -so the number is still moving — `loop-lint`'s `own-cost` rule caught the -first draft asserting them flat, which is the rule working. - -**41.6% unattributed is the number worth carrying forward**, not any -per-task figure. A cost instrument that cannot say where two-fifths of the -spend went is a partial instrument, and the retention window is why the -chain of *"quote the previous pass's cost"* — a rule designed to keep -costs comparable across passes — **breaks after roughly four passes.** - -That rule should either be bounded to what the window holds, or the window -extended. It is not this pass's to decide; it is recorded here because -this is the first pass where the chain actually snapped. - -## 5. Chaos window 2 - -**Declaration 4 of 12.** Structural M, d8 = 7, no override. The roll is -recorded per §Loop tiers even though it changed nothing. - -Window 2 standing: no override has yet changed an outcome. The retirement -condition — *retire if an override changes nothing twice running* — has -not been triggered, because there have been no overrides to evaluate. - -## Open after this pass - -- **The cost chain is broken** (§4) and the rule that depends on it needs a - bound. -- The wrong-denominator pattern (§1) now has three instances and no - control. `facts-check` catches copies that disagree; nothing catches a - number computed correctly against the wrong base. diff --git a/evidence/CB-EV-0022-collect-the-rulings.md b/evidence/CB-EV-0022-collect-the-rulings.md deleted file mode 100644 index 26a74af..0000000 --- a/evidence/CB-EV-0022-collect-the-rulings.md +++ /dev/null @@ -1,209 +0,0 @@ -# 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 28c1703..10a1194 100644 --- a/games/ground/src/record.rs +++ b/games/ground/src/record.rs @@ -153,15 +153,6 @@ 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 7c7ecba..f315220 100644 --- a/scenarios/ground/gr-a04-bond-support.yaml +++ b/scenarios/ground/gr-a04-bond-support.yaml @@ -4,11 +4,9 @@ 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] -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. +provisional: true +provisional_owner: ground-game +provisional_raised: 2026-07-31 seed: 42 setup: players: 3 diff --git a/scenarios/ground/gr-d01-darvo-trigger.yaml b/scenarios/ground/gr-d01-darvo-trigger.yaml index 3d6b356..fe99a86 100644 --- a/scenarios/ground/gr-d01-darvo-trigger.yaml +++ b/scenarios/ground/gr-d01-darvo-trigger.yaml @@ -4,11 +4,9 @@ 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] -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 +provisional: true +provisional_owner: ground-game +provisional_raised: 2026-07-31 seed: 42 setup: players: 3 diff --git a/scenarios/ground/gr-d05-darvo-reverse.yaml b/scenarios/ground/gr-d05-darvo-reverse.yaml index 9c1bb56..f1e6d89 100644 --- a/scenarios/ground/gr-d05-darvo-reverse.yaml +++ b/scenarios/ground/gr-d05-darvo-reverse.yaml @@ -4,11 +4,9 @@ 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] -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. +provisional: true +provisional_owner: ground-game +provisional_raised: 2026-07-31 seed: 42 setup: players: 3 diff --git a/scenarios/ground/gr-e02-shared-ground.yaml b/scenarios/ground/gr-e02-shared-ground.yaml index ef275f2..04e9aa7 100644 --- a/scenarios/ground/gr-e02-shared-ground.yaml +++ b/scenarios/ground/gr-e02-shared-ground.yaml @@ -4,10 +4,9 @@ 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] -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. +provisional: true +provisional_owner: ground-game +provisional_raised: 2026-07-31 seed: 42 setup: players: 3 diff --git a/scenarios/ground/gr-e04-coalitions.yaml b/scenarios/ground/gr-e04-coalitions.yaml index 8682c76..663138d 100644 --- a/scenarios/ground/gr-e04-coalitions.yaml +++ b/scenarios/ground/gr-e04-coalitions.yaml @@ -4,10 +4,9 @@ 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] -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. +provisional: true +provisional_owner: ground-game +provisional_raised: 2026-07-31 seed: 42 setup: players: 3 diff --git a/specs/FindingRegister.md b/specs/FindingRegister.md deleted file mode 100644 index d91a9cb..0000000 --- a/specs/FindingRegister.md +++ /dev/null @@ -1,138 +0,0 @@ -# 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 8dd527d..b053a6f 100644 --- a/specs/GroundRules.md +++ b/specs/GroundRules.md @@ -221,82 +221,151 @@ terms. ## The finding register -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. +**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`. -## Underdetermined in dataset 0.1 — RULED 2026-08-03, applied 2026-08-05 + -**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). +| 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 | -**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. + -**No kernel behaviour changed**, because every ruling was a confirmation. -What changed is that these stopped being our guesses. +- **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. -> **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. +### The register's first run found ten answers nobody had collected -Formalization exposed points the dataset did not decide. Each is stated -with its default and the ruling that settled it. +**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. - **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 256c400..46b4551 100755 --- a/tools/design-baseline.py +++ b/tools/design-baseline.py @@ -47,19 +47,11 @@ FINDINGS = { "evidence/CB-EV-0007-stage-0.md", "scenarios/ground/gr-e01-threshold-reachable-2p.yaml", "workplans/CB-WP-0021-import-the-edition.md"], - # 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", - ], + "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()), "GR-E03/GR-E04 never played": ["evidence/CB-EV-0007-stage-0.md"], } diff --git a/tools/design.py b/tools/design.py index 4ef04b3..730a653 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/FindingRegister.md`. ADR-0012 D8 retired `design-baseline.py`, which +`specs/GroundRules.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", "FindingRegister.md") +REGISTER = os.path.join(ROOT, "specs", "GroundRules.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", "FindingRegister.md")).read()) + rows = parse(open(os.path.join(root, "specs", "GroundRules.md")).read()) findings = [r for r in rows if r["state"] != NOTE] notes = [r for r in rows if r["state"] == NOTE] @@ -179,31 +179,12 @@ def self_test(): all(r["role"] in ("counterexample", "default") for r in rows if r["repro"] not in ("—", "-", ""))) - # 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}") + # 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}") 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 42bf144..d564974 100755 --- a/tools/rule-coverage.py +++ b/tools/rule-coverage.py @@ -154,39 +154,11 @@ 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. - # - # **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 "")) + 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)") # --- 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-0021-import-the-edition.md b/workplans/CB-WP-0021-import-the-edition.md index f0c7aba..05130d8 100644 --- a/workplans/CB-WP-0021-import-the-edition.md +++ b/workplans/CB-WP-0021-import-the-edition.md @@ -2,7 +2,7 @@ id: CB-WP-0021 kind: product title: "Import the edition: the game plays its own data" -status: done +status: active state_hub_workstream_id: "782b1c37-f3a7-469b-87a3-fa73ebe758d2" --- @@ -278,7 +278,7 @@ collections. ```task id: CB-WP-0021-T04 -status: done +status: todo priority: high state_hub_task_id: "ba76138d-a225-470d-bb2a-3a6881f4ca82" ``` @@ -293,26 +293,3 @@ state_hub_task_id: "ba76138d-a225-470d-bb2a-3a6881f4ca82" score against the threshold. - **Quote CB-WP-0020's cost by re-running the instrument.** - **Chaos: 4 of 12 in window 2.** - -**Done 2026-08-05.** -[CB-EV-0019](../evidence/CB-EV-0019-import-the-edition.md). - -- **The budget was the wrong one, and the ADR corrected it**: `csv` costs - 17,651 against AM-4b's 19,742, so it *fits* — and is refused on - proportion (89% of the remainder to read 20 rows). Third premise this - pass corrected, second against a mis-chosen denominator. -- **The hash blast radius was predicted, then measured to be ~0** — ADR-0011 - §D3 found **0 scenario files pin a state hash**. Verified today: - `replay-test` 14/14, AM-7 0.985/0.998/1.018 against a 0.9 floor. What - actually broke was six scenarios' *data*, not any logic. -- **The endings mean something, and they are tight.** Played 2p all-bots: - **total 4 / threshold 5, group failure**, 6 available. T02's control - predicted a full clear is required at 2p — confirmed by play. -- **The cost chain snapped.** `cb-cost.py --slug CB-WP-0020` **aborts** — - no transcripts retained — so T04's last bullet cannot be satisfied, and - the instrument refusing to estimate is the correct behaviour. 41.6% of - spend is UNATTRIBUTED. The quote-the-previous-pass rule needs a bound. -- **The sharpest thing in it**: the work that disproved GR-E01 is *in this - workplan*, and CB-WP-0022 was written the next day quoting GR-E01 as its - founding example. Nothing connected them until a separate agent ran - `git log` four days later. diff --git a/workplans/CB-WP-0026-collect-the-rulings.md b/workplans/CB-WP-0026-collect-the-rulings.md deleted file mode 100644 index a160d9a..0000000 --- a/workplans/CB-WP-0026-collect-the-rulings.md +++ /dev/null @@ -1,286 +0,0 @@ ---- -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.