The screen does what you tell it — three Display items, and a fourth declined

Reviewed with Jesse out of TODO.md's Display section. Stays in the
unshipped v0.7.9.

#17 (hiding the Division map) was DECLINED, and the reason is that its
premise had already died. Gitea#18 replaced the wrapped layout with a
single row, and the reason to fold the map away was that it GREW — a
horseshoe of three or a square of four pushed the board off the screen.
One row is boardH = PAD * 2 + CH + 30: 150px, fixed, at every seat
count. That is not worth a control, three states and a persisted
preference. It was a sixth member of the drawing pass that got closed
with Gitea#18 and stayed open only because it reads as a control
question rather than a drawing one — recorded in TODO.md as an explicit
decision, with the design that had already been worked out kept, and
with the one thing that would justify reopening it: the map growing
again.

#16 THE OFFICE AREA'S AUTO-HIDE COULD NOT REACH EVERY STATE. One button
cycling auto -> pinned -> auto, where the pin was `open ? 'closed' :
'open'` and `open` is what auto is doing AT THAT MOMENT. So the pin a
press offered depended on the phase, and going from always-show to
always-hide meant clicking back to auto, waiting for the phase to turn
over, and clicking again. Three controls now, one per mode. The labels
still say what pressing DOES, which was an earlier deliberate fix; what
the cycle could not do was report the state it was in, and aria-pressed
carries that now.

They are addressed by id rather than queried off the container, and
that is testability rather than style: the stub DOM the web suite runs
against only models markup the page WROTE, so a child query finds
nothing and the control would have shipped green and unexercised. The
test presses always-show to always-hide directly — the transition the
cycle could not make.

#23 THE HISTORY READS NEWEST FIRST. Jesse: "the top line is the most
recent and the further down you go, the older the entry." The phase
headings now trail the lines they announce, ruled acceptable rather
than overlooked: "stage changes will be beneath (prior to / older than)
the following events. That is OK." Reading down is reading backwards.
Grouping by phase and reversing the groups was offered and declined as
more machinery than the complaint needs. replays.ts keeps its
oldest-first log deliberately — it is paired with a frame stepper,
where newest-first would fight the stepping. The slice(-60) cap is
untouched and stays open.

#28 THE SETTINGS MOVED INTO A CARD. The top line carried six things and
now carries four: Revenue, the objective, the collision counts and the
game code. The rest is a This Game card at the foot of the right-hand
column, folded by default. Nothing new travels for it — configFromFrame
already existed and main.ts already called it three times, so
rulesListHtml(configFromFrame(f), ...) needed no refactor, and the card
draws from the same renderer as the lobby's join preview so the two
cannot drift.

THE COLLISION COUNTS ARE NEW ON THE BOARD, NOT MOVED. The Frame has
carried collisionsToday and collisionsTotal since v0.7.0 and nothing
drew them, so the one victory condition that ends a game EARLY ran
invisibly — the second time this release that the Frame had the answer
and the view never asked (see #43's actingPlayer). They stay on the top
line while the limits go in the card: a limit is agreed to once, "2 of
3 today" changes how you play the next Stage.

One stub gap closed to get here: none of the five element factories in
test/web.test.ts had setAttribute, so the first render threw and any
control reporting state through ARIA was untestable.

WHAT IS NOT VERIFIED: the layout. There is no browser on this box, so
nothing has confirmed the segmented control, the card or the reversed
panel look right on screen. The logic is tested; the appearance is not,
and wants the next play session.

881 tests pass, fourteen new.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YTaNBL1jVxNqgFdjHkHoo3
This commit is contained in:
Jesse.Markowitz
2026-08-30 18:16:08 -04:00
co-authored by Claude Opus 5
parent 31b942cc38
commit d267f89a82
5 changed files with 576 additions and 80 deletions
+88 -1
View File
@@ -254,6 +254,93 @@ and the line reads "resumed" when it is.
Both halves are pinned, including that a brand-new game does *not* claim to be a resume — an
announcement that fires either way says nothing.
### The screen does what you tell it — three items off `TODO.md`
Reviewed with Jesse 2026-08-30 out of the Display section. A fourth, #17 (hiding the Division map),
was **declined** in the same pass: its premise died with Gitea#18 and nobody had connected the two.
The map used to grow a row at a time and was worth folding away at three or four seats; a single row
is `boardH = PAD * 2 + CH + 30` — 150px, fixed, at every seat count — and that is not worth a
control, three states and a persisted preference.
#### The Office Area's auto-hide could not reach every state (#16)
One button cycling `auto -> pinned -> auto`, where the pin it reached was
`open ? 'closed' : 'open'` — and `open` is what auto is doing **at that moment**,
`FOCUS_PHASES.has(f.phaseKey)`. So which pin a press offered depended on the phase: "always hidden"
during Local Operations and Cargo, "always showing" everywhere else. Getting from one pin to the
other meant clicking back to auto, waiting for the phase to turn over, and clicking again. That is
why it never read as a setting — it was not one.
Three controls now, one per mode, and every mode is one press from every other. The labels still say
what pressing **does** rather than what the panel is doing, which was an earlier deliberate fix; what
the cycle could not do was report the state it was in, and `aria-pressed` on the lit button carries
that instead of the label.
**Addressed by id (`#dm-auto`/`#dm-open`/`#dm-closed`) rather than by querying the container's
children**, and that is a testability decision rather than a style one. The page never writes this
markup, so a child query finds nothing in the stubbed DOM the web suite runs against — the control
would have shipped green and completely unexercised. The test that now pins it presses
always-show → always-hide directly, which is precisely the transition the cycle could not make.
#### The history reads newest first (#23)
Jesse: "it should be reversed so the top line is the most recent and the further down you go, the
older the entry." The panel ran oldest-first and scrolled itself to the bottom, so the thing that had
just happened was the one line you had to go and find.
**The phase headings now trail their lines, and that is accepted rather than overlooked.** A
`t-phase` line reads forwards — it introduces what follows it — so reversing puts each one below the
events it announced. Jesse ruled on it directly: "stage changes will be beneath (prior to / older
than) the following events. That is OK." Reading down the panel is reading backwards in time, and a
heading under its own lines is what backwards looks like. Grouping by phase and reversing the groups
was the alternative and was declined as more machinery than the complaint needs. The
"— the game began —" marker moves by the same logic: it is the oldest thing on screen, so it goes
last.
`replays.ts` keeps its own oldest-first log deliberately. It is paired with a frame stepper, where
"what just happened" is the step you have this moment clicked, so newest-first would fight the
stepping rather than help it.
#### The settings moved into a card, and the collision counts came out of hiding (#28)
Jesse, 2026-08-23: "the game-specific information in the very top line should probably be a card like
Facilities, timetable or blocked… we can give complete information about all the game options and not
take up valuable real estate at the top of the screen." And on when it is read: "To go, 'Oh wait, what
did we set that to?'"
The top line carried six things and now carries four: **Revenue, the objective, the collision counts,
and the game code**. The seed or seat, the game type and the abbreviated house rules moved into a
**This Game** card at the foot of the right-hand column, folded by default and persisted with the
other display preferences.
**Nothing new travels for it.** `configFromFrame` already turns the Frame's copy of the config back
into a `GameConfig`, and `rulesListHtml` is the renderer the lobby's join preview and seating screen
already draw — so what a player agreed to before the deal and what they read mid-game come from one
implementation and cannot drift. The card adds only the half `rulesListHtml` has no notion of: which
seed or seat this is, and what the game is called.
**The collision counts are new on the board, not merely moved.** The Frame has carried
`collisionsToday` and `collisionsTotal` since v0.7.0 and nothing drew them, so the one victory
condition that ends a game EARLY ran invisibly — which v0.7.9 made reachable in solitaire too, and
that is what made them worth having. They stay on the top line while the limits go in the card,
because the two are different kinds of thing: a limit is a setting agreed to once, "2 of 3 today" is
a number that changes how you play the next Stage. A limit of `0` is off, and an off half is left out
rather than shown as "1 of 0"; with both off the chip is empty and collapses.
In solitaire the game-code span is empty, so its tooltip — which still carries the type, the blurb and
the victory conditions — is unreachable. Not a hole: the card's summary line is on screen whether the
card is open or shut and opens with the type, "Solitaire · 5 Days · floor 15 · 3 cards · 4/2/1".
**One gap in the test stubs was closed to get here.** None of the five element factories in the web
suite had `setAttribute`, so the first render threw `b.setAttribute is not a function` — meaning any
control that reports its state through ARIA could not be tested at all. They carry an attribute bag
now, and the segmented control's test reads `aria-pressed` through it.
**What is verified and what is not.** The logic is covered by three new tests and the two that used to
read `#houserules`/`#gametype` were rewritten to open the card rather than deleted. **The layout is
not verified** — there is no browser on this box, so nothing has confirmed the segmented control, the
card, or the reversed panel actually look right on screen. That wants a play session.
### Three wording and layout fixes
- **The collision entries** on all three screens now read "The game ends immediately and results in
@@ -268,7 +355,7 @@ announcement that fires either way says nothing.
cost, not a danger, and red would outrank the actual rules above it. The buttons say **Continue
Existing Saved Game** and **Deal New Game** rather than "Continue saved game" and "Deal".
878 tests pass, eleven of them new; one existing test asserted the opposite of the collision ruling
881 tests pass, fourteen of them new; one existing test asserted the opposite of the collision ruling
above and says so where it was reversed.
---