Some checks failed
ci / check (push) Failing after 3s
make trials reported "positions unreachable: 4, target 0 — the recording exists but the position moved". All four were false. A recording holds one hash, the final state, and reachability asked whether the note's hash was in that file — so a mid-game note could never match, and a post-game note from any but the last game could not either. Instance 8 of the ADR-0018 family: vary only WHEN a note was written and the answer flips, with nothing having moved. The root cause was not the metric. play again reused state belonging to a game: it overwrote the previous game's recording (data loss), never cleared the journal (game 2's log opened with game 1's commands), and so a note's command index pointed into a recording without those commands. Fixing reachability alone would have gone green while a session still destroyed its own evidence. A note now binds by (game, after) — an index into the recording's own commands list, which a reader can replay to. The hash keeps a job as the integrity check at the end of a game, where it can actually fail. Game 1 keeps the path it was given, so GameDesign §5's documented invocation is unchanged; later games get -2, -3 and nothing is overwritten. Legacy 5-column logs stay readable and are reported as legacy, never as orphans — an unsubstantiated orphan claim is the defect being fixed. All three fixes mutation-proven, including at the call site via a real two-game session. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
108 lines
4.3 KiB
Markdown
108 lines
4.3 KiB
Markdown
---
|
|
id: CB-WP-0032
|
|
kind: product
|
|
title: "The comments in the account"
|
|
status: done
|
|
state_hub_workstream_id: "71d744f9-7e40-49c3-994e-f3fb007a1430"
|
|
---
|
|
|
|
# Purpose
|
|
|
|
```
|
|
structural tier S (one existing renderer gains a second input; no new
|
|
decision, no dependency, no budget moved)
|
|
chaos d8 = 6 → no override
|
|
declared tier S
|
|
```
|
|
|
|
**Declaration 3 of chaos window 3.**
|
|
|
|
## The report
|
|
|
|
> *"Commenting after the game is a good practice. We could show the
|
|
> comments in the log. I would think that as useful."*
|
|
|
|
This closes the gap [CB-WP-0031](CB-WP-0031-the-comment-box-outlives-the-game.md)
|
|
named and did not fix: notes were written to a file and **shown back
|
|
nowhere**. `trials.py`'s own docstring calls that shape *"this project's
|
|
signature failure in a new medium"* (ADR-0014 D6) — and the note channel
|
|
had it, in the interface, for as long as it has existed.
|
|
|
|
## The decision: what a comment is positioned against
|
|
|
|
A note carries `round`, `step` and `state_hash`. **None of them can order
|
|
it against the log**, because the log is a sequence of *commands* and
|
|
several commands share a step. Positioning by round would put a comment
|
|
*somewhere in* the right round — for a remark about a specific move, the
|
|
wrong position stated confidently.
|
|
|
|
So the server records **how many commands had been played** when the note
|
|
was written, which it knows exactly (`journal.len()`), and the page places
|
|
it there. `after = 0` is *before anyone moved*, which is a real thing to
|
|
have an opinion at.
|
|
|
|
## The thing that must not happen
|
|
|
|
**A comment must not be readable as something the game did.** The log is
|
|
the recorder's vocabulary — `record::to_step`, what the scenario file will
|
|
say — so a note styled as a log line would be a sentence the game never
|
|
produced, sitting in the account of what the game produced. ADR-0014 D4
|
|
turns on that distinction being visible: raw notes stay in this repo, and
|
|
only a human promotes one to a finding.
|
|
|
|
Notes render in their own `.note` block, attributed *"you — round N,
|
|
step"*, with the words in quotes. **The `.note` CSS already existed and was
|
|
never used by anything** — defined for this, and left unwired.
|
|
|
|
## What was NOT done, and why it is the interesting half
|
|
|
|
**No column was added to the trial log**, though `after` is genuinely the
|
|
anchor and persisting it would let a later reader interleave.
|
|
|
|
`trials.py` skipped any row that was not five cells — with `continue`, in
|
|
silence. A sixth column would have made `make trials` report **zero notes
|
|
for every log at once**, which is verbatim the failure the parser's own
|
|
docstring says it exists to prevent. `after` is therefore held in memory
|
|
for the session and the file format is untouched.
|
|
|
|
**The latent defect was fixed on its own terms**: a wrong column count now
|
|
raises, and the walk that wraps it reports the *real* reason instead of
|
|
blaming a missing block for every failure.
|
|
|
|
## Task: show the comments where they were made
|
|
|
|
```task
|
|
id: CB-WP-0032-T01
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "31f60440-e049-45f7-99fa-87c01c00adb7"
|
|
```
|
|
|
|
**Controls:**
|
|
- **placement is asserted, not just presence** — mutation-proven: append
|
|
the notes at the end instead of interleaving and the test goes red;
|
|
- **a comment cannot be read as a game event** — asserted on its own
|
|
marking and its attribution;
|
|
- **a comment past the end of the log still appears**, because a post-game
|
|
note is written when every command has been played and a dropped comment
|
|
is the failure the channel exists to avoid;
|
|
- **the round trip, not the write** — the `cb-play` test posts a note and
|
|
then GETs the page, which is the path a player takes.
|
|
|
|
**Done 2026-08-07.**
|
|
|
|
`Account { lines, notes }` replaces the bare `&[LogLine]` on both pages —
|
|
a struct because `document_with_log` was already at seven arguments, and
|
|
because the two are only meaningful together, which is the point.
|
|
|
|
`trials.py` hardened as described above, with two new controls: a
|
|
wrong-shaped row raises, and the walk names the reason.
|
|
|
|
## Not done here
|
|
|
|
- **Nothing lets a player edit or delete a comment.** A note is a record
|
|
of what they thought at the time and rewriting it would defeat that,
|
|
but a typo is a real case and there is no answer for it yet.
|
|
- **The ending page shows the same interleaved account**, so a long game
|
|
puts the post-game comment at the bottom of a long log. Whether that is
|
|
where a reader looks for it is untested.
|