diff --git a/crates/cb-render-html/src/doc.rs b/crates/cb-render-html/src/doc.rs index cb3f9ca..7995e81 100644 --- a/crates/cb-render-html/src/doc.rs +++ b/crates/cb-render-html/src/doc.rs @@ -698,44 +698,19 @@ pub fn document( seat: Option, may_pass: bool, ) -> String { - document_with_log( - view, - legal, - Endpoints { - command: endpoint, - note: "/note", - }, - seat, - may_pass, - &[], - &[], - ) + document_with_log(view, legal, endpoint, seat, may_pass, &[], &[]) } /// The table, plus the game log (CB-WP-0018 T02). -/// Where the page posts, both channels (ADR-0014 D1). -/// -/// One struct rather than two `&str` parameters because they are one -/// concept — the guarded surface this page may talk to — and because -/// clippy was right that the signature had grown across three passes. -#[derive(Debug, Clone, Copy)] -pub struct Endpoints<'a> { - /// Pointer facts. `resolve` turns these into commands. - pub command: &'a str, - /// Free text. Nothing turns these into commands. - pub note: &'a str, -} - pub fn document_with_log( view: &GroundView, legal: &[games_ground::GroundCommand], - to: Endpoints<'_>, + endpoint: &str, seat: Option, may_pass: bool, log: &[LogLine], meta: &[String], ) -> String { - let (endpoint, note_to) = (to.command, to.note); let mut s = String::with_capacity(8192); let _ = write!( s, @@ -768,7 +743,7 @@ pub fn document_with_log( body(&mut s, view); move_section(&mut s, legal, seat, may_pass); s.push_str("
"); - meta_section(&mut s, meta, note_to); + meta_section(&mut s, meta); log_section(&mut s, log); s.push_str("
"); let _ = write!( @@ -786,7 +761,7 @@ pub fn document_with_log( /// /// Empty is a legitimate state — a first game has no tally and may have no /// notes — and renders as nothing rather than as an empty heading. -fn meta_section(s: &mut String, meta: &[String], note_to: &str) { +fn meta_section(s: &mut String, meta: &[String]) { // CB-WP-0027 T03: the comment box. Always present — the panel's // purpose is that a player can say something at any moment, and a box // that appears only sometimes trains them not to look for it. @@ -795,14 +770,12 @@ fn meta_section(s: &mut String, meta: &[String], note_to: &str) { // The command channel needs JavaScript because a drag is not a form // submission; a comment is, and making it depend on the script would // add a failure mode for no gain. - let _ = write!( - s, + s.push_str( "

what are you thinking?

\ -
\ + \ \
", - note_to = esc(note_to), ); if meta.is_empty() { return; diff --git a/crates/cb-render-html/src/lib.rs b/crates/cb-render-html/src/lib.rs index 2885d15..a460ab4 100644 --- a/crates/cb-render-html/src/lib.rs +++ b/crates/cb-render-html/src/lib.rs @@ -47,14 +47,7 @@ pub mod input; pub mod jsrun; pub mod serve; -pub use doc::{document, text_of, Endpoints}; - -/// The endpoint pair every test in this crate posts to. -#[cfg(test)] -const TEST_ENDPOINTS: Endpoints<'static> = Endpoints { - command: "/command?t=x", - note: "/note?t=x", -}; +pub use doc::{document, text_of}; pub use input::{resolve, Note, PointerFact}; pub use serve::{Guard, Refusal, Request}; @@ -193,7 +186,7 @@ mod coverage { text_of(&document( view, &[], - crate::TEST_ENDPOINTS.command, + "/command?t=x", Some(PlayerId(0)), false, )) @@ -602,7 +595,7 @@ mod gamelog { document_with_log( &crate::testfix::view(Some(PlayerId(0))), &[], - crate::TEST_ENDPOINTS, + "/command?t=x", Some(PlayerId(0)), false, log, @@ -786,7 +779,7 @@ mod notes { let html = document_with_log( &crate::testfix::view(Some(PlayerId(0))), &[], - crate::TEST_ENDPOINTS, + "/command?t=x", Some(PlayerId(0)), false, &[], @@ -814,16 +807,13 @@ mod notes { let html = document_with_log( &crate::testfix::view(Some(PlayerId(0))), &[], - crate::TEST_ENDPOINTS, + "/command?t=x", Some(PlayerId(0)), false, &[], &[], ); - assert!( - html.contains("action=\"/note?t=x\""), - "the form must carry the session token" - ); + assert!(html.contains("action=\"/note\""), "no comment box"); assert!( html.contains("method=\"post\""), "a plain form, so it works with the script disabled" @@ -842,7 +832,7 @@ mod two_columns { document_with_log( &crate::testfix::view(Some(PlayerId(0))), &[], - crate::TEST_ENDPOINTS, + "/command?t=x", Some(PlayerId(0)), false, &[], diff --git a/crates/cb-render-html/src/serve.rs b/crates/cb-render-html/src/serve.rs index e4f3169..dfe464d 100644 --- a/crates/cb-render-html/src/serve.rs +++ b/crates/cb-render-html/src/serve.rs @@ -158,17 +158,6 @@ impl Guard { format!("/command?t={}", self.token) } - /// The note channel's endpoint, token and all (CB-WP-0027). - /// - /// **A form's `action` must carry the token like every other - /// request.** The first version posted to a bare `/note` and was - /// refused with "no session token" — the endpoint had been tested - /// with a token appended by hand, so the test exercised the mechanism - /// and not the path a player takes. - pub fn note_endpoint(&self) -> String { - format!("/note?t={}", self.token) - } - pub fn page_url(&self) -> String { format!("{}/?t={}", self.origin, self.token) } diff --git a/specs/FindingRegister.md b/specs/FindingRegister.md index 2b84b41..1490bbf 100644 --- a/specs/FindingRegister.md +++ b/specs/FindingRegister.md @@ -44,8 +44,6 @@ kinds, states and metrics: [`GameDesign.md`](GameDesign.md). Reported by | F14 | unplayed | note | — | — | 2026-08-01 | clay-borg | | F15 | underdetermined | note | — | — | 2026-08-05 | clay-borg | | F16 | inconsistent | withdrawn | games/ground/examples/difficulty.rs | counterexample | 2026-08-05 | clay-borg | -| F17 | degenerate | note | — | — | 2026-08-06 | ground-game | -| F18 | inert | raised | — | — | 2026-08-06 | clay-borg | @@ -75,24 +73,6 @@ kinds, states and metrics: [`GameDesign.md`](GameDesign.md). Reported by since GROUND-WP-0005 is blocked on exactly this number. The withdrawal was reported (ADR-0012 D5). Its reproduction is `difficulty.rs`, whose policy panel is plural *because of this finding*. -- **F17 — no incentive to ATTACK while holding useful Solutions.** - Reported from play, 2026-08-06: *"there is no incentive to play attacks - as long as I have positive cards."* If true, ATTACK is a dead branch for - most of a game, which would make GR-A06/A07/A08 and the whole Rivalry - half of the relation system reachable only when a player is out of good - options. **`note`, not a finding**: no artifact demonstrates it yet. One - is cheap — count ATTACK selections across the policy panel and compare - against hand quality — and until it exists this may not go to - `ground-game` (GameDesign §3.1). **Owner is ground-game if it survives**; - it would be a design finding, not an engine one. -- **F18 — the engine imports one of nineteen edition files, so the cards - cannot say what they do.** Reported as *"I don't understand the GROUND - card"* — which is not a design gap: `Actions.csv` carries that card's - tagline (*"Regulate. Restore the frame. Decide."*) and full rules text, - and clay-borg never imported it. Every other card is the same: a player - sees `Clarify` where the card reads *"Ask What Happened — Invite a - concrete account before judging."* **`inert`**: the data exists and - cannot fire, because nothing reads it. **Ours, and CB-WP-0028 fixes it.** - **F15 — the rules define one game, not a series.** `OutcomeView` gives `personal` (per seat), `group_success` (per table) and `winners`. Summing the first and counting the third answer different questions, and GROUND diff --git a/tools/cb-play/src/hotseat.rs b/tools/cb-play/src/hotseat.rs index 3513935..d853c3f 100644 --- a/tools/cb-play/src/hotseat.rs +++ b/tools/cb-play/src/hotseat.rs @@ -243,10 +243,7 @@ impl Server { let page = cb_render_html::doc::document_with_log( &view, legal, - cb_render_html::Endpoints { - command: &self.guard.endpoint(), - note: &self.guard.note_endpoint(), - }, + &self.guard.endpoint(), Some(seat), may_pass, &self.log_lines(), diff --git a/workplans/CB-WP-0028-the-table-you-sit-at.md b/workplans/CB-WP-0028-the-table-you-sit-at.md deleted file mode 100644 index d7d1649..0000000 --- a/workplans/CB-WP-0028-the-table-you-sit-at.md +++ /dev/null @@ -1,277 +0,0 @@ ---- -id: CB-WP-0028 -kind: product -title: "The table you sit at: the cards' own words, an overhead view, and a game you solve rather than survive" -status: ready ---- - -# Purpose - -``` -structural tier M (imports more of the authoritative edition, which is - an external dataset under AM-4's budgets, and touches - the rendering the coverage gate is written against) -chaos d8 = 1 → no override -declared tier M -``` - -Declaration 11 of chaos window 2. Tier M: survey and decision merged; -adversarial review optional. - -## Nine observations from play - -The maintainer played with the new meta panel and reported nine things. -**Two are findings about the game, seven are about the engine**, and the -split matters because they go to different places. - -| # | observation | where it goes | -|---|---|---| -| 1 | no incentive to attack while holding positive cards | **register** — a game-design finding | -| 2 | *"I don't understand the GROUND card"* | **here** — and it is not a design gap (below) | -| 3 | overhead view: players around a table, stacks on it | T03 | -| 4 | click the draw stack to take cards | T04 | -| 5 | optional auto-draw | T04 | -| 6 | play again / stop belong in the meta column | T05 | -| 7 | so does the full log | T05 | -| 8 | *"Game over"* is wrong when you won | T06 | -| 9 | rankings — MVP, most problems solved | T07 | - -## Observation 2 is a data-import gap, and it reframes the pass - -*"I don't understand the GROUND card or why other cards should be played -to the table"* reads as a design problem. It is not. **The card explains -itself in the dataset and we never imported the explanation.** - -`ground-game/editions/*/Actions.csv`, the GROUND row: - -> **tagline:** *"Regulate. Restore the frame. Decide."* -> **rules_text:** *"After all actions are revealed, choose one mode: -> GR—Ground & Restate: −2 Stress, ready Freedom… OU—Observe & Uphold…"* - -**We vendored one file of nineteen.** `editions/ground-darvo-r0/` holds -`Problems.csv` and nothing else; `Actions`, `Solutions`, `Modes`, -`Scenarios`, `Relations`, `DARVO`, `Tokens`, `Glossary` and the rest live -only in `ground-game`. Everything the engine knows about them is a -**hand-transcription into `GroundRules.md`'s 59 numbered rules** — which -is enough to *play* the game and gives a player nothing to *read*. - -So the page shows `Clarify` where the card says **"Ask What Happened — -Invite a concrete account before judging."** - -**This is the most valuable thing in the pass**, and it was found by a -player saying he did not understand something rather than by any gate. -Rule coverage is 59/59 and has been for weeks. - -## Task: decide what else to import, and what it costs - -```task -id: CB-WP-0028-T01 -status: todo -priority: high -``` - -`decisions/ADR-0015-*.md` (tier M merges survey and decision). - -**ADR-0011 is the precedent and it constrains this.** It vendored -`Problems.csv` with a checked digest and a ~50-line hand reader, and -**refused the `csv` crate on proportion** — 17,651 lines against AM-4b's -19,742 of remaining headroom, 89% of the budget to read 20 rows. - -Decide: - -- **which files**, and the answer is not "all of them". `Actions`, - `Solutions` and `Modes` carry text a player reads. `BOM`, `Print_Manifest`, - `Back_Designs`, `Symbols` are production artifacts for a physical print - run and have no business here. -- **whether the hand reader survives contact.** `Solutions.csv` has - `microcopy` and `rules_text` — prose fields with commas, quotes and - possibly embedded newlines. ADR-0011 named exactly this as the revisit - condition: *"nested quoting, embedded newlines, multiple dialects."* - **If the reader cannot parse them correctly, say so and re-run the - dependency argument rather than writing a fragile parser.** -- **what happens when text and rules disagree.** `GroundRules.md` was - derived by hand from these files. If a `rules_text` contradicts a - numbered rule, that is a **finding**, and it goes to the register — not - a quiet edit of either. -- **`Extensions.csv` exists** and names content deliberately outside the - core. Decide whether it is imported at all; the answer is probably no, - but *"we did not know it existed"* must not be the reason. - -## Task: the cards say what they do - -```task -id: CB-WP-0028-T02 -status: todo -priority: high -``` - -Every card a player can see carries its own title, tagline and rules text, -from the edition — not from a phrase we invented. - -- action cards: GROUND, SOLVE, INVESTIGATE, SUPPORT, ATTACK; -- Solution cards: the title and microcopy, not just the suit; -- the mode in play, with its own description. - -**Controls:** -- **a test asserts the text comes from the dataset**, not from a Rust - literal — the whole point is that the game's own words reach the player, - and a hand-copied string drifts from the source it copies; -- the drag affordance still works: the explanation is *additional*, and - CB-WP-0020 T02 already found that replacing a label with an explanation - loses the label; -- **the page stays readable.** Five action cards with full rules text is a - wall; the tagline is the default and the rules text is on demand. - -## Task: an overhead view of a real table - -```task -id: CB-WP-0028-T03 -status: todo -priority: high -``` - -> *"Players sitting around the table, the draw and discard stacks on it. -> An overhead view of a game table would instantly help with understanding -> what is going on."* - -The seats become positions around a table rather than a row of cards; the -Problems, draw and discard sit in the middle; each seat's played card -appears in front of that seat. - -**This subsumes `relations_svg`**, which already places seats on a circle -for the relationship graph — so the layout exists and is drawn twice, once -as a circle and once as a row. **One table, not two diagrams.** - -**Controls:** -- the coverage gate still passes: every view field appears in the parsed - document. CB-WP-0027 showed a reflow costs nothing when probes name - facts; this is a bigger reflow and the same rule applies; -- **the viewer's own seat is identifiable at a glance** — a table where - you cannot find yourself is worse than a list; -- 2 through 6 seats all lay out without overlap, asserted per seat count - rather than eyeballed at 3. - -## Task: take cards from the stack, or let the table do it - -```task -id: CB-WP-0028-T04 -status: todo -priority: medium -``` - -Observations 4 and 5. Clicking the draw stack to take a card is the -natural gesture; auto-draw is for when that stops being interesting. - -**Both are subject to a rule the engine already has.** Drawing is not a -free action — GR-A01 draws a Solution as part of INVESTIGATE, and the deck -is not a thing a player may simply take from. **So this task must first -establish whether "click to draw" is a legal move at all**, and if it is -not, it is a **finding for `ground-game`**, not a feature: the maintainer -expected an interaction the rules do not offer, and that gap is the -signal. - -**Do not implement a draw the rules do not have.** CB-WP-0023 exists -because SOLVE was offered where it could not act. - -**Controls:** -- the answer to *"is clicking the deck a legal move?"* is in the task - record with the rule that settles it; -- auto-draw changes no outcome — a game played with it and without it from - the same seed produces the same end-state hash, or it is not automation - but a rules change. - -## Task: the meta column takes the controls and the log - -```task -id: CB-WP-0028-T05 -status: todo -priority: medium -``` - -Observations 6 and 7. `play again` and `end session` move to the meta -column, and the ending page gets the same two-column shape as the table — -it currently has none. - -The full log belongs there too, which CB-WP-0027 did for the live page and -not for the ending page. - -**Controls:** -- CB-WP-0024 T01's seal still works: acknowledging the end must still - remove every control, wherever they now live; -- the ending page's existing tests pass unchanged or the change is a - regression. - -## Task: a game you solve - -```task -id: CB-WP-0028-T06 -status: todo -priority: medium -``` - -> *"'Game Over' is negative and should only be used for lost games. If the -> players won, let's use 'Game solved'."* - -**Right, and it is more than tone.** GROUND is a co-operative game about -repairing a situation; `group_success` means the table *solved* something. -"Game over" is arcade vocabulary for a failure state, and using it for a -win tells the player the wrong thing about what they just did. - -**Controls:** -- the heading is a function of `outcome.group_success`, asserted **both - ways** — a test that only checks the win case passes for a page that - always says "solved"; -- the no-outcome case (a game that ended badly) keeps its own wording and - claims neither. - -## Task: who did what — rankings that are not invented - -```task -id: CB-WP-0028-T07 -status: todo -priority: medium -``` - -> *"A ranking of players in the final stats. 'Most valuable player', 'most -> problems solved' and other conditions that might apply."* - -`OutcomeView` already carries `personal`, `winners`, `coalitions` and -`mastery`, and the aggregate knows who claimed each Problem. - -**The risk is inventing scoring the game does not have.** *"Most problems -solved"* is countable from `claimed_by` and is a fact. *"Most valuable -player"* is a **judgement**, and any formula for it is a rule we made up. - -So: **report what the game counts, and mark anything derived as ours.** -A superlative computed from real data is fine; a superlative presented as -if the rules defined it is the same defect as a provisional default -silently canonised. - -**Controls:** -- every ranking names its source: a rule id, or *"clay-borg's reading"*; -- ties are handled and shown as ties, not broken arbitrarily — `Modes.csv` - has a `scoring_tiebreak` column, so the game may already say how; -- a ranking that no seat leads (nobody solved anything) renders as that, - not as an empty list. - -## Task: evidence - -```task -id: CB-WP-0028-T08 -status: todo -priority: medium -``` - -`evidence/CB-EV-0026-*.md`. - -- **Whether importing the card text changed what the maintainer - understood** — observation 2 is the acceptance test and it has a person - attached to it. -- **Whether any `rules_text` contradicted `GroundRules.md`.** Nineteen - files were hand-transcribed into 59 rules; if the import surfaces a - disagreement, that is the most valuable output of this pass. -- **Whether "click the deck to draw" was legal**, and if not, what went to - `ground-game`. -- **What the overhead view cost the coverage gate**, against CB-WP-0027's - finding that a probe naming a fact survives a reflow. -- **Quote CB-WP-0027's cost by re-running the instrument.**