clay-borg/workplans/CB-WP-0031-the-comment-box-outlives-the-game.md

109 lines
4.1 KiB
Markdown
Raw Normal View History

---
id: CB-WP-0031
kind: product
title: "The comment box outlives the game"
status: done
state_hub_workstream_id: "7b7450cd-3972-4c78-a53c-fba54b3fc02b"
---
# 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
state_hub_task_id: "6b92d470-b3a2-4cde-a307-d20cb6a45ab4"
```
**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.