The note channel closed at the moment it is worth most — a player who has just seen the outcome is the one with something to say, and that reading was collectable at every moment of the game except the one after it. Two independent defects: doc::ending never rendered the box, and serve_end had no POST /note arm, so even a hand-built post fell through to 404. Fixing either alone leaves the channel shut, so the test asserts both and is mutation-proven to fail on each half separately. A post-game note binds to the final position but is not an observation made at the last decision point. RoundStep::End is the last step of a ROUND, not the end of the game, so record_note now takes the step as an argument and the post-game path passes "after the end" — otherwise an after-the-fact reading is filed as an in-play one, which is the wrong-subject family ADR-0018 was written for. A note does not end the session: every other POST in that loop breaks it, and a player must be able to write a second one and then still play again. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
b9efa327ab
commit
499d9fe3d7
7 changed files with 512 additions and 45 deletions
106
workplans/CB-WP-0031-the-comment-box-outlives-the-game.md
Normal file
106
workplans/CB-WP-0031-the-comment-box-outlives-the-game.md
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
---
|
||||
id: CB-WP-0031
|
||||
kind: product
|
||||
title: "The comment box outlives the game"
|
||||
status: done
|
||||
---
|
||||
|
||||
# Purpose
|
||||
|
||||
```
|
||||
structural tier S (one channel reopened on an existing page; no new
|
||||
decision, no new dependency, no budget moved)
|
||||
chaos d8 = 2 → no override
|
||||
declared tier S
|
||||
```
|
||||
|
||||
**Declaration 2 of chaos window 3**, opened 2026-08-07 by ADR-0017.
|
||||
|
||||
## The report
|
||||
|
||||
> *"Before we move on, i want to be able to add comments after the game
|
||||
> finished."*
|
||||
|
||||
## What was actually broken, and it was two things
|
||||
|
||||
**The channel closed at the moment it is worth most.** A player who has
|
||||
just seen the outcome is the one with something to say — *"so that is why
|
||||
that failed"*, *"I never understood what that card did"* — and that is the
|
||||
reading the whole trial protocol (GameDesign §5) exists to collect. It was
|
||||
collectable at every moment of the game **except** the one after it.
|
||||
|
||||
Two independent defects, either of which alone shuts the channel:
|
||||
|
||||
| where | what |
|
||||
|---|---|
|
||||
| `doc::ending` | never rendered the comment box at all |
|
||||
| `hotseat::serve_end` | had no `POST /note` arm — a hand-built post fell through to `404 the game is over` |
|
||||
|
||||
**Fixing either alone leaves it shut**, which is why the test asserts both
|
||||
and why a markup-only test would have passed against a server that still
|
||||
refused.
|
||||
|
||||
## The thing that needed deciding: what a post-game note is bound to
|
||||
|
||||
A note is bound to a position by `state_hash` (ADR-0014 D3), and the
|
||||
position is genuinely the final state. But **`round` and `step` say *when
|
||||
the player said it***, and a note written after the outcome is on screen
|
||||
is not an observation made at the last decision point.
|
||||
|
||||
`RoundStep` has an `End` variant — **the last step of a round, not the end
|
||||
of the game.** Logging a post-game note under it would file an
|
||||
after-the-fact reading as an in-play one: a value that is correct about the
|
||||
wrong subject, which is the family ADR-0018 was written for two hours
|
||||
earlier.
|
||||
|
||||
So `record_note` takes the step **as an argument** rather than reading it
|
||||
off the state, and the post-game path passes `"after the end"` — which
|
||||
cannot collide with a `Debug` identifier.
|
||||
|
||||
## No box where there is nothing to bind to
|
||||
|
||||
A game that stopped without a result has no final position. The box is
|
||||
**not offered** there, and the page says why. Offering one that would
|
||||
answer `409` is a control that exists to be hit.
|
||||
|
||||
## Task: reopen the channel
|
||||
|
||||
```task
|
||||
id: CB-WP-0031-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
**Controls:**
|
||||
- **the test fails on each half separately** — mutation-proven: blanking
|
||||
the form gives *"the ending page offered no comment box"*, removing the
|
||||
server arm gives `404`; both green when restored;
|
||||
- **a note must not end the session** — every other POST in `serve_end`
|
||||
breaks the loop, and a player must be able to write a second note and
|
||||
then still press `play again`. The test posts a command after the note
|
||||
and requires it to still work;
|
||||
- **the redirect carries the token**, which is the defect that twice made
|
||||
a *saved* note read as a failed one;
|
||||
- **the log distinguishes when it was written** from the position it binds
|
||||
to.
|
||||
|
||||
**Done 2026-08-07.**
|
||||
|
||||
`note_form` extracted and shared by both pages — a hand-written twin is
|
||||
how one of them quietly stops posting anywhere useful. The placeholder
|
||||
differs, and only that: what a player has to say mid-turn and what they
|
||||
have to say having seen the result are not the same prompt.
|
||||
|
||||
**A test-harness defect worth recording.** The first draft wrote
|
||||
`Content-Length: 21` beside a 22-byte body; the server read 21 bytes and
|
||||
answered `400 unrecognised field`, which looked exactly like the feature
|
||||
being broken. The fix was to derive the length from the body in the shared
|
||||
`post` helper — **a hand-written length is a second copy of a fact the
|
||||
body already carries**, the same shape as the 62 untagged literals
|
||||
`facts-check` counts.
|
||||
|
||||
## Not done here
|
||||
|
||||
- **Notes are still not shown back to the player.** Neither page echoes
|
||||
what has been written; the trial log is the only place they exist. That
|
||||
is a real gap and it is not this report.
|
||||
Loading…
Add table
Add a link
Reference in a new issue