Two reports, one cause: the script's fetch had no .catch. When the server had exited the promise rejected, the chain never ran, and not even the status line moved — so a dead server and a click that did nothing were indistinguishable. play again read as a dead button, and the linger timeout was invisible. Now a rejection says the session is gone and seals the page, and a 5-second heartbeat against a new /alive notices it without needing a click, which is what the timeout case requires. The beat carries the token, and does not extend the linger: that deadline is absolute. The harness had the same hole. jsrun's fetch stub had no .catch, so the branch that notices a dead server would have been unreachable in every test — the very defect the stub's own comment records from CB-WP-0024. Teaching it __failing, .catch and a recorded setInterval was the fix; writing the script defensively would have repeated the trap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
3.8 KiB
| id | kind | title | status | state_hub_workstream_id |
|---|---|---|---|---|
| CB-WP-0034 | product | Who you are bonding with | done | bde0c1d5-a76c-4431-a48a-a9fa81f4d0cc |
Purpose
structural tier S (rendering only -- no rule, no dependency, no budget,
no artifact contract moved)
chaos d8 = 4 → no override
declared tier S
Declaration 5 of chaos window 3.
The report, three times
"RespondToSupport is unclear as i cant see whos support i accept." — 2026-08-06
"i can not [see] the player i bond or not-bond with. We should change that." — 2026-08-07 15:58
"But i still cant see the player i am bonding with." — 2026-08-07 16:12
Three sessions, two days, and it survived a whole UI rebuild.
It was not styling
The move button's label was format!("{c:?}"). The player was reading
RespondToSupport { response: AcceptBond }
— Rust struct syntax, with no name anywhere in it. And
GroundCommand::RespondToSupport { response } does not carry the
counterparty, so nothing rendering the command alone could have named
them. It comes off the view: whoever played Support targeting this seat.
Four more fields on the seat panel had the same defect, including the
one the report names — support AcceptBond, an answer with nobody
attached. CB-WP-0020 fixed exactly this for selections and left its four
neighbours as they were.
Task: name who
id: CB-WP-0034-T01
status: done
priority: high
state_hub_task_id: "d8429ce2-991a-45df-9fcd-6b70b0e81511"
Controls:
- the seat is named, and the raw command name never reaches the page;
- an unseen offer is not given an invented name — a confidently wrong seat is worse than an honest gap (ADR-0018);
- no catch-all arm, so a new command cannot silently fall back to
Debug; - mutation-proven both ways.
Done 2026-08-07.
The exhaustiveness control paid for itself immediately, before any
test ran: GroundChoice::RejectReverse and
SupportResponse::BreakRivalry both failed to compile. Two moves that
would have shipped as struct dumps, caught by the absence of a _ =>.
These are clay-borg's words, not the edition's. The Action cards carry
the game's own rules_text (ADR-0015); nothing vendored names these
control moves, and Glossary is unvendored (F18). If it lands, this is a
caller to revisit.
The finding underneath: a gate that pinned the defect
every_view_field_is_classified_in_the_emitted_document — the probe that
exists to prove every view field reaches the player — was matching
"problem: 7 }" "player: Some(PlayerId(1))" "members: [PlayerId(1)"
It was proving the field reached the page by matching Rust struct syntax. A gate written to protect the player's view of the game had been certifying the defect as coverage, and would have gone red if anyone had fixed it.
Second confirmation of CB-WP-0024's finding, from the other side. That pass found a probe naming a presentation does not survive a rendering change. This is the sharper case: a probe naming Debug output actively holds the defect in place.
Probes now name facts — Ground & Restate, P2 + P3, accepted P3's Support. The fixture gained a visible Support offer so the probe can name
a seat rather than a response with nobody attached.
Not done here
- The unknown-supporter path is untested against a real game. It is
reachable only if
RespondToSupportis offered while the Support is not visible in the responder's view; whether the rules permit that is a question forground-game, and the page currently says it cannot see rather than guessing. - Other Debug output may remain outside the two surfaces audited here (the move buttons and the seat panel). The exhaustive match guards commands only.