v0.8.0.17 — four things the game knew and the screen did not say

All four reported from a table on Day 1 of v0.8.0.16, and all the same shape.

ABS SIGNALS COULD ONLY BE PLAYED ON ONE MAINLINE CARD, while its tooltip said
"any Mainline card". The engine was never wrong: check accepts any node whose
kind is mainline and legalActions filters by check, so all of them were legal.
The failure was the LABEL — describeIntent named i.placement and never i.node,
so every placement described itself as plain "play ABS Signals", and the action
list drops duplicate labels. All but the lowest-index node were discarded before
the menu saw them. This is the THIRD time that trap has fired and the file
documents the other two three lines apart: a turnout's two rotations, and three
Department discards. Same fix — name what distinguishes them.

The card is also called what the card face calls it. prettyKey rendered
absSignals as "Abs Signals" beside a tooltip saying ABS, an acronym no
key-splitter can recover, so the authored names now win. Three of those names
were transcribed in sentence case and were CORRECTED rather than adopted: the
repository says "Yard Office" 36 times against "Yard office" twice. A lookup
that imports its own source's typos is the drift it exists to prevent.

NOTHING ON A MAINLINE CARD SHOWED WHAT WAS STANDING ON IT. Played, ABS left no
mark and you found out by hovering — the same complaint the Heavy Grade wedge
answered, and it matters more here because ABS decides whether a second train on
that card is safe. It draws a signal mast with a lit lamp now; a signal is the
literal object and needs no room for words, which is what lets it sit clear of a
name as long as "Uncontrolled Siding" on a 152px cell. The Mainline modifiers
draw as BRK, AIR and HLP. Realignment is deliberately not among them: reduce
takes the `became` branch and changes node.card, so a realigned Trestle IS an
Uncontrolled Siding afterwards. Asserted, so the absence reads as a finding.

A FREIGHT AGENT TURN SAID A CAR MOVED WHEN NONE HAD. Three faults behind one
line. It asserted an outcome, where §6.3 requires no action and the bot declines
deliberately — unjamming a healthy box destroys a load that cost a whole action
to stock. An idle Agent was then silent, which read as a dropped turn; a new
freightAgentIdled event says so and why, reducing to nothing exactly like
switchingEnded. And the work named a coordinate rather than the industry, though
a `place` helper has existed for precisely that since the switching lines moved
to it. "Loaded a loaded boxcar INTO the green Outbound box at the Freight House",
with the direction in capitals because to-or-from was the question asked.

THE LOG AND THE ACTION MENU SPELLED THE SAME SQUARE DIFFERENTLY. view.ts wrote
(col,row) — X,Y, east/west then north/south — with a comment saying why;
narrate.ts wrote the internal storage order with no comment at all. So the menu
offered a move to "(1,-1)" and the log reported it at "(-1,1)", side by side.
Pinned by a test that renders one square through BOTH describers and compares
them to each other: a test written against either file alone would have passed.

THE DOCUMENTATION IS REACHABLE FROM A RUNNING GAME, AND ALL OF IT IS PUBLISHED.
v0.8.0.16 published the Quickstart and nothing it points at — its §8 links five
documents by relative path and every one 404'd on the package, verified against
the running container. The build publishes the full set, and the test reads the
links OUT OF the guide rather than listing them. They are linked from the This
Game card, where reference already lives, rather than the header that must not
wrap; no mode awareness is needed, because solitaire and multiplayer are the
same page on the same origin.

THE REFERENCES DROPPED THE VERSION FROM THEIR NAMES. Four described v0.8.0.16
and had since the v0.8.0.15 audit; the v0.4.5 was the prototype edition they
were first written against, kept only because 36 citations pointed at it — and
it read as documentation five minor versions stale. They are quickstart.md,
rules.md, home-deck.md, mainline-deck.md and components.md now, kept current
with each release rather than published as editions. Two errors surfaced while
checking them against this release, which is the argument for doing it:
home-deck.md filed ABS Signals under Enhancements "played into your district"
that "change what a square does" — it does neither, this release's bug written
down — and mainline-deck.md, which lists everything playable onto a Mainline
card, never mentioned it at all.

1010 fast tests and 35 sim tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUizFYCMHRWhbWwXhp7WPR
This commit is contained in:
Jesse.Markowitz
2026-09-21 05:53:52 -04:00
co-authored by Claude Opus 5
parent dc31555625
commit b90c0413d2
27 changed files with 819 additions and 94 deletions
+112 -5
View File
@@ -19,6 +19,113 @@ page as `v0.1.0 · <sha> · <date>`, so what is deployed can always be identifie
---
## 0.8.0.17 — 2026-09-21
Four things a table found on Day 1 of v0.8.0.16, all of them the same shape: the game knew
something and the screen did not say it.
### ABS Signals could only ever be played on one Mainline card
Its tooltip says "any Mainline card". Exactly one was ever on offer — the Trestle, on the board it
was reported from.
**The engine was never wrong.** `check` accepts any node whose kind is `mainline`, and
`legalActions` filters by `check`, so every Mainline card was enumerated and legal. The whole
failure was in the LABEL: `describeIntent` named `i.placement` and never `i.node`, so all of them
described themselves as plain "play ABS Signals" — and the action list drops duplicate labels, which
discarded every one but the lowest-index node before the menu saw it.
**This is the third time that trap has been sprung**, and the file documents the other two three
lines apart: a turnout's two rotations produced one label each until the rotation was named, and
three Department discards collapsed into one button until the pile was named. The fix is the same
one both times: name the thing that distinguishes them. Pinned by a test that counts the Mainline
cards in the division and requires a distinct, named spot for each.
The card is also called what the card face calls it. `prettyKey` rendered `absSignals` as "Abs
Signals" on a button while the tooltip beside it said ABS — an acronym no key-splitter can recover —
so the authored names in the content tables now win. Three of those names were transcribed in
sentence case and were corrected rather than adopted: the repository says "Yard Office" 36 times
against "Yard office" twice, and a lookup that imports its own source's typos is the drift it exists
to prevent.
### Nothing on a Mainline card showed what was standing on it
Played, ABS left no mark. You found out it existed by hovering the card — which is the complaint the
Heavy Grade wedge answered in v0.8.0.7, and it matters more here, because ABS is what decides
whether running a second train onto that card is safe.
A card carrying it now draws a **signal mast with a lit lamp** at its top-right corner. A signal is
the literal object, and unlike a text badge it needs no room for words, which is what lets it sit
clear of a name as long as "Uncontrolled Siding" on a 152px cell.
The Mainline modifiers had the same defect and are drawn too, as **BRK**, **AIR** and **HLP** beside
the card's name. Tags rather than names only because the measurements leave no choice — "Brakeman ·
Airbrakes · Helpers" is thirty characters where about eleven fit — and the tooltip has always spelled
them out. **Realignment is deliberately not among them:** it never sits on a card, because `reduce`
takes the `became` branch and changes `node.card` outright, so a realigned Trestle simply IS an
Uncontrolled Siding afterwards. Asserted in the test, so the absence reads as a finding rather than
an omission.
### A Freight Agent turn said a car moved when none had
"Chose FREIGHT AGENT work — one car moved to or from a facility", and then nothing. Three faults
behind one line.
The line **asserted an outcome**. §6.3 requires no action at all, and the bot takes that route
deliberately — unjamming a healthy box destroys a load that cost a whole Local Operations action to
stock, so an idle Stage is strictly better. It now says what the Freight Agent MAY do.
**An idle Agent was silent.** A new `freightAgentIdled` event says so, and gives the reason. It
reduces to nothing, exactly like `switchingEnded`: it reports a choice the state already holds.
**The work named a coordinate, not the industry.** A `place` helper has existed for this since the
switching lines were moved to it, and its own comment makes the argument — "(-1,1)" is the grid's
notation and means nothing at a table where people are looking at cards. These three lines were
missed. A Freight Agent turn now reads "loaded a loaded boxcar INTO the green Outbound box at the
Freight House", with the direction in capitals because to-or-from was the question asked.
### The log and the action menu spelled the same square differently
`view.ts` wrote `(col,row)` — X,Y, east/west then north/south — with a comment saying why.
`narrate.ts` wrote `(row,col)`, the internal storage order, with no comment at all. So the menu
offered a move to "(1,-1)" and the log then reported it at "(-1,1)", in two panels read side by
side. The log follows the map now.
Pinned by a test that renders one square through BOTH describers and compares them to each other
rather than to a literal — a test written against either file alone would have passed all along.
### The documentation is reachable from inside a game, and all of it is published
**v0.8.0.16 published the Quickstart and nothing it points at.** Its §8 "Where to read more" links
five further documents by relative path, and every one of them 404'd on the package — verified
against the running container, five of six paths missing. The whole table was dead. The build
publishes the full set now, and the test reads the links OUT OF the guide rather than listing them,
so it cannot go stale the way the references themselves did.
**The guide is linked from the This Game card** (Jesse's call), which is where reference already
lives — the seed, the seat, the house rules — rather than from the header, which is the line that
must not wrap. It needs no mode awareness: solitaire and multiplayer are the same page on the same
origin, so one relative link resolves in both, on the public site and on a StartOS box alike. Every
link opens in a new tab, because a player reading the rules mid-turn must not lose the game behind
them.
### The references dropped the version from their names
`StationMaster-Rules-v0.4.5.md` and three like it described **v0.8.0.16** and had done since the
v0.8.0.15 audit. The `v0.4.5` was the prototype rules edition they were first written against, kept
in the filename only because thirty-six citations pointed at it — and it read, to anyone opening the
published guide, as documentation five minor versions out of date.
They are `quickstart.md`, `rules.md`, `home-deck.md`, `mainline-deck.md` and `components.md` now,
with every citation rewritten. **These are kept current with each release rather than published as
editions**, so the name is always the latest and the build each describes is stated at the top.
Two errors surfaced while checking them against this release, which is the argument for doing it:
`home-deck.md` listed ABS Signals among the Enhancements "played into your district" that "change
what a square does" — it does neither, and that miscategorisation is this release's bug written
down. And `mainline-deck.md`, which lists everything that may be played onto a Mainline card, never
mentioned ABS Signals at all. Both corrected.
## 0.8.0.16 — 2026-09-20
The Quickstart put where a tester can actually reach it, the last place that still told the old
@@ -31,7 +138,7 @@ v0.8.0.15 wrote a Quickstart for a tester who has never played, and then left it
a tester does not look — reachable only by someone who already has the repository. Nobody being
handed the box has it.
`build-web.ts` now copies `docs/StationMaster-Quickstart.md` into `dist/quickstart.md`, and the
`build-web.ts` now copies `docs/quickstart.md` into `dist/quickstart.md`, and the
splash page offers it under the three doors: *New to Station Master? Read the Quickstart guide.*
**Not a fourth door** — reading the guide is not a way to play, and giving it equal weight in that
grid would say it is.
@@ -132,7 +239,7 @@ is the prototype rules edition they were first written against, and the names ar
`src/`, `CHANGELOG.md` and `docs/rules/` all cite them, and several of those citations are historical
records of what a document said at the time.
- **`StationMaster-Quickstart.md` — NEW.** What the game is, how you win, the shape of a Stage, what
- **`quickstart.md` — NEW.** What the game is, how you win, the shape of a Stage, what
is on the screen, a first twenty minutes, the things that surprise new players, and what to report.
Written for somebody about to play rather than somebody building it.
- **Rules.** §3.4 replaced outright — the `firstToTarget` / `highestAfterDays` victory model and the
@@ -3509,7 +3616,7 @@ that same occupant list.
`buildDivision` drew uniformly from the nine card TYPES **with replacement**, so a Division could be
dealt two Interchanges or two Tunnels, and Plains — printed twice in the deck — carried the same
weight as cards printed once. `docs/StationMaster-Mainline-Deck-v0.4.5.md` had flagged the mismatch
weight as cards printed once. `docs/mainline-deck.md` had flagged the mismatch
as needing correction; "an Extra may start at the Interchange if one is on the board" is what forced
it, since that only reads as a rule if the board holds at most one. Now dealt from `MAINLINE_DECK`
without replacement, verified over 1600 deals across 1–4 players.
@@ -3546,7 +3653,7 @@ cannot see is a board with nothing to click and no reason given.
**1/2 Crack Limited 3 coaches → 2. 5/6 The Sparrow 2 → 3.** A change to the cards, not a
transcription fix, so `Trains3.pdf` and the transcription in `implications.md` §5 keep the original
numbers with a footnote; `content.ts` and `StationMaster-Home-Deck-v0.4.5.md` carry what the game
numbers with a footnote; `content.ts` and `home-deck.md` carry what the game
plays. Both consists remain inside the four-car Crew Tray limit.
A test had to follow: `multiplayer.test.ts` used Train 1 *because* it had three cars, to exercise a
@@ -3680,7 +3787,7 @@ before v0.4.9 — 6 copies, one slot each direction — so §9.3 names it, and t
The card set says the same thing without needing the rules text. All three Refinery modifiers —
Pipelines, Oil Depot, Viscosity Breakers — grant **+1 outbound**; a two-way Refinery would be the only
industry in the game with no card able to raise one of its two directions.
`StationMaster-Home-Deck-v0.4.5.md` prints "Refinery · Outbound · 1 out / 0 in" and "Grocer's
`home-deck.md` prints "Refinery · Outbound · 1 out / 0 in" and "Grocer's
Warehouse · Inbound · 0 out / 1 in".
So the Refinery ships and the Grocer's receives, and the **Freight House is the one two-way industry**