CB-WP-0026: collect the rulings -- ten answers that arrived and were never applied
Some checks failed
ci / check (push) Failing after 3s
Some checks failed
ci / check (push) Failing after 3s
ground-game ruled all ten U-items on 2026-08-03, every one CONFIRMED as the default clay-borg simulates, and confirmed five of six provisional scenarios. clay-borg never collected the answers: CB-RES-0007 reported "0 of 10 ruled" the same day, and CB-WP-0022 built the finding register two days later still recording them as `reported`. make design's first run is what noticed -- not a human, not the adversarial review that found four other things. That is the unread-inbox failure running in the opposite direction, and it appears nowhere in the declaration, survey, ADR or spec of the pass that was built entirely around the forward version. It is arguably worse: an unread message is visible as silence, while a collected-but-unapplied ruling looks exactly like work in progress. Ten rulings quoted into §Underdetermined (the three conditional ones verbatim -- U1's designer note, U2's End-only trigger, U8's consume-only-if-it-cancels). Five provisional flags lifted, replaced by ruled/ruled_by/ruled_note so the flag went and the provenance stayed. Register queue 9 -> 0. T02's control came back clean: make sim is 26 passed, 59 rules covered, nothing red. Had a scenario gone red it would have meant we described our own behaviour incorrectly to ground-game. I wrote two U-item mappings and both were wrong. gr-a04 -> U1 (it asserts consent is REQUIRED; U1 asks WHEN the target accepts) and gr-d05 -> U5 (it exercises the UNREJECTED Reverse; U5 is the rejected one). Both plausible from covers:, neither survived reading the description. Third and fourth instance of this defect; the first two reached ground-game. So encodes_u_item is now a declaration and design.py asserts the file names what it claims -- and that check's own first version grepped for mentions and went red when two files recorded why they do NOT encode U1 and U5. A mention is not a claim, which is exactly the looseness that let "six of the ten have provisional scenarios" stand. Two positive controls went red for the best possible reason, both broken the same way -- asserting against live repo data instead of constructing their condition. rule-coverage.py required at least one provisional item to EXIST; it now builds a fixture and reports the live count as a diagnostic, because there is no number of provisional items this project should have. design-baseline.py pinned "2 of 6" while recomputing one row from a live glob, so the dated snapshot was never a snapshot; frozen to its 2026-08-03 list and unwired from self-tests, since per ADR-0012 D8 it is no longer a reporting tool. ScenarioFile is deny_unknown_fields and refused the four new fields until declared -- correct: a corpus accepting unknown metadata would let a typo'd encodes_u_iem sit there claiming nothing. DEVIATION: ADR-0012 D2 said "no new file". GroundRules.md crossed the loadability limit, so the register moved to specs/FindingRegister.md. D2's substance holds -- one register, same machinery, nothing competing -- but the literal instruction did not, and it resolves an awkwardness D2 named itself. make all: exit 0. loop-lint clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
b1f4a85a4a
commit
6be9fbc9af
15 changed files with 797 additions and 138 deletions
1
Makefile
1
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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<CommandStep>,
|
||||
|
|
|
|||
209
evidence/CB-EV-0022-collect-the-rulings.md
Normal file
209
evidence/CB-EV-0022-collect-the-rulings.md
Normal file
|
|
@ -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<n>\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.
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
138
specs/FindingRegister.md
Normal file
138
specs/FindingRegister.md
Normal file
|
|
@ -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`.
|
||||
|
||||
<!-- design-register:begin -->
|
||||
|
||||
| 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 |
|
||||
|
||||
<!-- design-register:end -->
|
||||
|
||||
- **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.
|
||||
|
||||
|
|
@ -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.
|
||||
|
||||
<!-- design-register:begin -->
|
||||
## 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).
|
||||
|
||||
<!-- design-register:end -->
|
||||
**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
|
||||
|
|
|
|||
|
|
@ -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"],
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = "<!-- design-register:begin -->"
|
||||
END = "<!-- design-register: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<n>\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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
286
workplans/CB-WP-0026-collect-the-rulings.md
Normal file
286
workplans/CB-WP-0026-collect-the-rulings.md
Normal file
|
|
@ -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<n>\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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue