174 lines
8.1 KiB
Markdown
174 lines
8.1 KiB
Markdown
|
|
# CB-EV-0023 — the table you can watch
|
||
|
|
|
||
|
|
CB-WP-0024 T05. Tier S (structural S — renders state the projection
|
||
|
|
already carries; chaos d8=6 → no override). Declaration 7 of chaos window
|
||
|
|
2. Closed 2026-08-05.
|
||
|
|
|
||
|
|
**Delivered:** the ending control says what it does and the page acts on
|
||
|
|
it; draw and discard drawn as stacks; each seat's play drawn as a card;
|
||
|
|
a session tally across games. Five remarks from play, four addressed
|
||
|
|
(remarks 2 and 3 are CB-WP-0025).
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. How many of the five remarks were already implemented
|
||
|
|
|
||
|
|
**Three of five were data the projection already carried, rendered as
|
||
|
|
text.** That is the finding this pass is really about, and it was
|
||
|
|
established by reading the code before writing the workplan rather than
|
||
|
|
by building anything.
|
||
|
|
|
||
|
|
| remark | what was actually missing |
|
||
|
|
|---|---|
|
||
|
|
| follow the other players | nothing in the model — `selections` project as `Shown` after Reveal and were already drawn, **as sentences** |
|
||
|
|
| draw and discard stacks | nothing in the model — `solution_deck_len` and `solution_discard` were one line of text |
|
||
|
|
| show the scores | nothing at all — `personal`, `mastery`, `winners`, `total`/`threshold` were already drawn |
|
||
|
|
| the ending button | a real defect, two of them |
|
||
|
|
| a cumulative score | genuinely absent |
|
||
|
|
|
||
|
|
**So the table's problem was legibility, not content.** A renderer can
|
||
|
|
show every field it is given and still be unreadable, and the coverage
|
||
|
|
gate — which asserts every view field appears in the parsed document —
|
||
|
|
passes either way. It is a completeness gate, and completeness was never
|
||
|
|
the issue.
|
||
|
|
|
||
|
|
**That is worth naming as a gap in the controls**, not as a criticism of
|
||
|
|
them. `every_view_field_is_classified_in_the_emitted_document` proves
|
||
|
|
nothing is silently omitted. Nothing proves it is *readable*, and nothing
|
||
|
|
could, cheaply. The maintainer playing the game is the instrument, which
|
||
|
|
is what makes remarks like these worth more than their length suggests.
|
||
|
|
|
||
|
|
## 2. The defect that mattered was untestable, and that is why it survived
|
||
|
|
|
||
|
|
The ending control was labelled *"close — I have read this"* while
|
||
|
|
`hotseat.rs` read it as **stop the server**, and acknowledging it changed
|
||
|
|
nothing: the tab kept a full table and a `play again` pointing at a closed
|
||
|
|
port.
|
||
|
|
|
||
|
|
The label was a wording bug. **The second half survived for a structural
|
||
|
|
reason:**
|
||
|
|
|
||
|
|
> `jsrun`'s `fetch` stub returned `{then: function () { return this; }}`,
|
||
|
|
> which never invoked its callbacks. **Every line of the script that
|
||
|
|
> reacts to what the server said was unreachable from every test in this
|
||
|
|
> project.**
|
||
|
|
|
||
|
|
The `ok` → reload branch, the status text, and the branch that did not yet
|
||
|
|
exist were all equally unexercised. A page that ignores the server was
|
||
|
|
indistinguishable, under test, from one that acts on it.
|
||
|
|
|
||
|
|
CB-WP-0014 embedded a real JS engine specifically so the script could be
|
||
|
|
executed rather than string-matched, and CB-WP-0016 found that *a stub too
|
||
|
|
thin to express a failure is how the failure survives*. This is the same
|
||
|
|
finding one layer deeper: the stub was thin at the **reply**, and the
|
||
|
|
reply is where the session's ending lives.
|
||
|
|
|
||
|
|
The stub now delivers a real then-chain and `gesture_with_reply` reports
|
||
|
|
which controls survive. The seal is mutation-proven — deleting the branch
|
||
|
|
turns exactly one test red — and a negative control asserts `ok: dealing`
|
||
|
|
does **not** seal, because a seal that fired on every reply would pass the
|
||
|
|
first test and silently break `play again`.
|
||
|
|
|
||
|
|
### One browser-versus-stub trap, avoided by writing it down
|
||
|
|
|
||
|
|
The first seal used `setAttribute('data-drop', null)`. In the stub that
|
||
|
|
stores a real `null` and `getAttribute` returns `null`; **in a browser it
|
||
|
|
writes the literal string `"null"`, which is truthy**, so the control would
|
||
|
|
have stayed live while its own test called it sealed. `removeAttribute`
|
||
|
|
is correct in both. The stub gained the method rather than the script
|
||
|
|
gaining a workaround.
|
||
|
|
|
||
|
|
## 3. The reshuffle question: already ruled, already implemented
|
||
|
|
|
||
|
|
T02 required settling whether the deck-exhaustion shuffle happens before
|
||
|
|
drawing it, and to raise a finding if it did not.
|
||
|
|
|
||
|
|
**It does.** `games/ground/src/lib.rs:1419-1435` (`draw_solution`):
|
||
|
|
deterministic reshuffle of the discard, seeded `seed ^ round`, carried in
|
||
|
|
a `DeckReshuffled` event so replay never re-derives it; if both are empty
|
||
|
|
the draw is skipped. That is the **U4 default**, which ground-game
|
||
|
|
**confirmed on 2026-08-03** — a ruled rule.
|
||
|
|
|
||
|
|
So nothing was raised, which is the correct outcome and the one the task
|
||
|
|
was written to allow. The piles show the state in which the *next* draw
|
||
|
|
triggers it, because that is derivable from the view; a claim that a
|
||
|
|
reshuffle *has* happened is not, and is not made. The event already reads
|
||
|
|
out in the log.
|
||
|
|
|
||
|
|
**Two days earlier this question would have been raised as a finding.**
|
||
|
|
CB-WP-0026 applied the U4 ruling on 2026-08-05; this pass consumed it the
|
||
|
|
same day. The register paying off inside 24 hours is not proof it works,
|
||
|
|
but it is the first time the answer to *"is this underdetermined?"* was
|
||
|
|
one lookup instead of a message.
|
||
|
|
|
||
|
|
## 4. What the controls caught
|
||
|
|
|
||
|
|
**The coverage gate caught its own probe going stale.** Replacing the
|
||
|
|
`17 remaining` text broke `solution_deck_len`'s classification — the gate
|
||
|
|
does not care *how* a field is rendered, only that its token appears, so
|
||
|
|
changing the rendering changed the token. Fixed by putting the count in
|
||
|
|
the pile's `<title>`: a stable probe, and what a screen reader announces.
|
||
|
|
The on-canvas numeral would have been a weak probe, since a bare `17`
|
||
|
|
could be anything on the page.
|
||
|
|
|
||
|
|
**The compiler caught a misplaced doc comment** that had silently taken
|
||
|
|
`Summary`'s `#[derive(Debug)]` with it.
|
||
|
|
|
||
|
|
Neither is interesting alone. Together they are the ordinary case for
|
||
|
|
this project: the gates fire on rendering changes, and the cost of that is
|
||
|
|
small enough that nobody is tempted to loosen them.
|
||
|
|
|
||
|
|
## 5. The cumulative-score question, and why it stayed a note
|
||
|
|
|
||
|
|
The task required deciding what "cumulative" means **before** summing
|
||
|
|
anything. The answer is that **GROUND defines one game and no series at
|
||
|
|
all.**
|
||
|
|
|
||
|
|
`personal` sums per seat; `winners` counts games won; `group_success`
|
||
|
|
counts games the table cleared. They answer different questions, and a
|
||
|
|
test asserts they can point at different seats — a 9/0/0 versus 0/1/1
|
||
|
|
series leads on summed score for one seat and on games won for the other.
|
||
|
|
|
||
|
|
**Both are shown and both are named.** Picking one would have made it *the*
|
||
|
|
score by default, which is canonising a rule nobody wrote.
|
||
|
|
|
||
|
|
Registered **F15, `underdetermined`, state `note`**. It stays a note on
|
||
|
|
purpose: the test demonstrates the two tallies *can differ*, which is
|
||
|
|
arithmetic — not evidence that the ambiguity harms play. Under GameDesign
|
||
|
|
§3.1 a note may not be reported to ground-game until an artifact exists.
|
||
|
|
|
||
|
|
**This is the note tier doing exactly what ADR-0012 D6 built it for**, on
|
||
|
|
the first genuinely new question since it was written. The tier was
|
||
|
|
argued as the way to admit findings from *play* that the engine cannot
|
||
|
|
produce; its first real use is a question from *building*, which the ADR
|
||
|
|
did not anticipate and which the tier handles unchanged.
|
||
|
|
|
||
|
|
## 6. Chaos window 2
|
||
|
|
|
||
|
|
**Declaration 7 of 12.** Structural S, d8 = 6, no override.
|
||
|
|
|
||
|
|
Ten declarations in, **no 8 has been rolled**. At d8 the expected count
|
||
|
|
over twelve is 1.5, so this is unremarkable — but it means window 2 will
|
||
|
|
likely close with **zero overrides to evaluate**, and its retirement
|
||
|
|
condition (*retire if an override changes nothing twice running*) will be
|
||
|
|
untestable. Window 1 closed with 2 overrides at d4 and both changed the
|
||
|
|
outcome. **The rate cut to d8 may have made the mechanism unevaluable**,
|
||
|
|
which is a real cost that CB-EV-0015's decision did not price, and the
|
||
|
|
window's closing evidence should say so.
|
||
|
|
|
||
|
|
## 7. Cost
|
||
|
|
|
||
|
|
CB-WP-0023's cost, by re-running the instrument — the pass this one is
|
||
|
|
required to quote. Reported by `make cost`; **not inlined here as a
|
||
|
|
literal** (§Single source of fact), and see CB-EV-0019 §4 on the chain
|
||
|
|
breaking beyond ~4 passes.
|
||
|
|
|
||
|
|
## Open after this pass
|
||
|
|
|
||
|
|
- **Remarks 2 and 3** — *how could we have won* and *difficulty* — are
|
||
|
|
CB-WP-0025, declared and ready, tier L.
|
||
|
|
- **F15** needs an artifact before the series question can go to
|
||
|
|
ground-game.
|
||
|
|
- **Legibility has no gate** (§1) and probably should not get one; the
|
||
|
|
honest control is a person playing it.
|