T03: input::describe writes a sentence per legal command; data-descs
carries them in step with data-targets; the ghost already following the
pointer shows the one for whatever legal target is under it, so the
explanation lands beside the target with no overlay layer to keep
aligned. ADR-0010 D1 binds -- the page renders it, never composes it.
Both mutations INITIALLY SURVIVED because the fixture's Attack card had
exactly one target, where an off-by-one shift and a truncation are both
no-ops. CB-EV-0014's lesson one level in: a fixture too thin to express
a failure is how the failure survives. Two attack targets now, both red.
T04: chaos rate d4 -> d8, window 2 open at 12 declarations, retiring if
an override changes nothing twice running. Window 1's condition was NOT
met -- both overrides changed the outcome -- so the mechanism is kept.
The weakest part of the decision is that it is a rate change argued from
n=2, so window 2 carries a falsifier: no override at all is evidence the
rate went too far, not that the mechanism is healthy.
InnerLoop.md hit 401 lines and the loadability gate fired; the rationale
moved to InnerLoopReference.md, structurally, per the standing precedent
that limits are not raised.
CB-WP-0017 settled at $9.48/40 against $5.19/23 reported mid-flight,
83% higher. Six for six, always low -- read by re-running the instrument
at the moment of quoting, which is CB-EV-0015's correction applied for
the first time.
make all exits 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bot::Journal -- a shared list of Applied { actor, command, events } the
driver appends to via play_journaled; play delegates with None so
nothing existing changed. BotGame.events only appears after play
returns, which is no use to a page rendered mid-game.
Phrased with record::to_step, the recorder's vocabulary, so what the
player reads is what the scenario file will say, and all 29 GroundEvent
variants now render in words instead of Debug.
A command that produced no events says 'no effect'; the mutation
dropping that branch goes red. Honest limitation recorded: the reported
SOLVE case is resolved inside the system's resolve command, which does
produce events for other seats, so it shows as a selection with no claim
following rather than an explicit 'no effect'. Making it explicit would
mean the renderer deciding why a rule did nothing -- a second
implementation of the rules, which this task's control forbids.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Server::serve_end plus doc::ending, wired into both of run_game's exits.
Where the browser used to get Connection refused it now gets the ending
page with the result and the final table. It serves until the page posts
'done' (the page carries a close control), with a 600s linger so an
abandoned tab cannot hold the process open.
document() split into body() and move_section() so the ending shows the
same table rather than a second rendering of it.
The control had to be built twice and the first was worthless:
the_end_of_the_game_reaches_the_browser calls serve_end directly, and
deleting the call from run_game left it GREEN -- it tested the link and
not the chain, which is CB-EV-0012's finding recurring.
a_real_game_played_to_its_end_leaves_the_ending_on_screen runs the real
play() with a browser seat, drives a real game to its end over a real
socket, and goes red under that mutation printing an empty page -- the
reported symptom exactly.
A weak assertion of mine caught by itself: the first draft grepped the
page for location.reload, which would have forced a second script to
satisfy a test rather than a requirement. It now asserts the ending
endpoint cannot answer 'ok'.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A discard pile already exists -- solution_discard on GroundState,
SolutionDiscarded removes from hand, and the page renders it. Building
one would have been building a thing that is there. What was dragged are
ACTION cards, which are not cards and are correctly never consumed;
solution cards leave the hand at Resolve because a selection is a
face-down commit.
But the report points at something real. Measured live: Investigate
draws correctly (2 -> 3 -> 4 cards), and Solve was then played three
times on problem-1 with the hand unchanged at 4 and discard empty --
because problem-1 was face down and GR-A02's resolver silently
continues. legal_commands offers Solve on every face-up problem without
consulting the hand.
Whether SOLVE should be selectable against a face-down problem or an
unmatchable suit is a game-semantics question and INTENT defers those to
ground-game. What is ours is that a provably-inert move is offered,
accepted and never accounted for. Folded into T02 as the case the log
must handle: a command that produced NO events is the one the player
needs to see.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Provenance: the maintainer 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, then GET / -> Connection refused. The game ENDED
normally, 5 rounds and 30 commands, and its whole result -- coalitions,
scores, winners, hash -- went to the terminal. next_choice only accepts
connections inside a human decision point, so when play() returns the
listener dies and the post-ok reload is refused. A crash and a win
render identically: nothing. Same class as CB-WP-0016's silent drop.
Also carries the chaos rate change CB-EV-0015 owed to the next
declaration (d4 -> d8, second window of 12), which is what makes this
structurally M. Rolled at the old d4=3, no override, because a rate
changes when the decision lands and not retroactively.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>