diff --git a/workplans/CB-WP-0018-the-browser-is-a-client.md b/workplans/CB-WP-0018-the-browser-is-a-client.md new file mode 100644 index 0000000..abe0ae9 --- /dev/null +++ b/workplans/CB-WP-0018-the-browser-is-a-client.md @@ -0,0 +1,173 @@ +--- +id: CB-WP-0018 +kind: product +title: "The browser is a client: game over, a log, and where a drop goes" +status: todo +--- + +# Purpose + +``` +structural tier M (changes how the loop constrains its own operation: + the chaos rate d4 → d8 and a second calibration + window, owed by CB-EV-0015 §5) +chaos d4 = 3 → no override +declared tier M +``` + +**Declaration 1 of the second chaos window** — opened by T04 below, at the +rate T04 sets. This declaration was rolled at the old d4, because the rate +changes when the decision lands, not retroactively. + +## The defect: a finished game is indistinguishable from a crash + +Reported: *"after some time i get an empty page back. I guess the game +crashes or ends but that is unclear as the ui disappears."* + +Reproduced by driving a real game to completion over HTTP: + +``` +move 5 accepted → ok +GET / → URLError: [Errno 111] Connection refused +``` + +The game **ended normally** — 5 rounds, 30 commands — and everything it +produced went to the terminal: + +``` +coalition [P2] score 0 +coalition [P3] score 0 +winners +game over — 30 commands, hash f6c890a65271 +``` + +The browser got nothing, because `next_choice` only accepts connections +*inside* a human decision point. When `play()` returns, `run_game` writes +the outcome to stdout and the process exits; the listener dies and the +page's post-`ok` reload is refused. + +**The browser is a second-class client.** Every terminal outcome — the +result, the scores, the winners, and every error `run_game` can return — +is invisible to the only interface a player is actually using. A crash and +a win render identically: nothing. + +This is the same defect class as CB-WP-0016's silent drop. The system +refuses to say what happened and the player is left to infer it. + +## Task: serve the end of the game + +```task +id: CB-WP-0018-T01 +status: todo +priority: high +``` + +When `play()` returns — **`Ok` or `Err`** — the browser must be told, on +the page, in terms a player understands. + +- **A win** shows the outcome the spectator projection already carries: + totals, threshold, coalitions, mastery, winners. +- **An error** shows that the game ended badly and why. `run_game` + currently returns `Err(String)` to a terminal nobody is reading. +- The final page must **not** auto-reload into a refused connection. + +The listener has to outlive the game, which it does not today. Decide how +it ends — a timeout, an explicit close, or serving until interrupted — and +say why; a server that never exits is its own defect. + +**Controls:** a test that plays a game to its end through the socket and +asserts the final GET returns a page naming the outcome, **not** a refused +connection. And the error path, driven by a game that fails, asserting the +reason reaches the page. + +## Task: a game log, and what to do next + +```task +id: CB-WP-0018-T02 +status: todo +priority: high +``` + +Requested: *"a game log about the events that have been generated and a +hint of what the next move/options are."* + +The events exist — `validate` produces them and `record::to_scenario` +already turns them into a scenario file. The page shows none of them, so a +player sees state change with no account of why. + +- **The log** is the event stream, in the recorder's vocabulary. Do not + invent a fourth phrasing: `cb-play`'s `describe()` exists precisely so + *"what the player reads is what the scenario file will say"*, and the + HTML renderer currently uses `{c:?}` Debug instead. Reuse it or say why + it cannot be reused. +- **The hint** is what the seat may do now, which is `legal` — already in + hand, already rendered as cards by CB-WP-0017. The hint is the *step* + context that makes those cards make sense: what the round is waiting + for, and what happens when it stops waiting. + +**Control:** the log must be derived from the events the aggregate +produced, not re-narrated from the state. A log that describes the state +after the fact is a second implementation of the rules and will drift. + +## Task: say where a drop goes, and what it means + +```task +id: CB-WP-0018-T03 +status: todo +priority: high +``` + +Requested: *"an overlay arrow pointing to the droptarget element with an +explanation showing up besides it, when we drag on it."* + +CB-WP-0017 marks every legal target. What it does not do is say what any +particular drop **means** — the player sees five outlined boxes and must +still guess which one does what. + +So the explanation is the substance and the arrow is the delivery. Both +need the meaning to exist as data first, and **ADR-0010 Decision 1 binds +here**: the description of what `action-attack → seat-1` does must be +written by Rust into the page. The script may render it on hover; it may +not compose it. + +**Controls:** every advertised target carries a description, and the +description a target shows is the one Rust wrote for *that* pair — a +mutation that shows a neighbouring pair's text must go red. The set +equality property from ADR-0010 D2 must still hold. + +## Task: close the chaos change, and the evidence + +```task +id: CB-WP-0018-T04 +status: todo +priority: high +``` + +CB-EV-0015 §5 recommended and did not make the change, on the grounds that +it alters how the loop constrains its own operation and was therefore owed +a tier-M declaration. This is it. + +**Make it or reject it**, in `specs/InnerLoop.md` and `gates.toml`: + +- rate **d4 → d8**; +- a **second window of 12** declarations; +- new retirement condition: *retire if an override changes nothing twice + running*. + +The argument to test rather than assume: both overrides were informative +*because they were rare*. If that is right the rate should fall; if the +real driver was that they landed on passes with something to find, the +rate is irrelevant and the recommendation is wrong. Say which, and note +that n=2 is thin evidence for a rate change — including the possibility +that the honest answer is to keep d4 for another window and decide with +four data points instead of two. + +Then `evidence/CB-EV-0016-*.md`: + +- **What the maintainer's three reports have cost and bought**, now that + there are three in a row and every one found something no test did. +- **Whether stage 1 closes.** +- **Quote CB-WP-0017's cost by re-running the instrument**, not from + memory — CB-EV-0015 §6 found that quoting a remembered figure defeats + the rule even when the boundary is right, and this is the first pass + that can apply that correction.