A second-digit bump, deliberately. 0.8.1 had been reserved for the seatless display table; that work is getting more thought, and this table pass over v0.8.0.17 earned the number on its own. Six reports: one was a rules question, one a wording complaint with a real bug underneath, four straightforward. A CAR CLEARED FROM A RED INBOUND BOX CAME BACK STILL LOADED. Reported as wording — "technically accurate but doesn't make any sense" — and the wording was the visible half. `inboundCleared` pushed `pooled(e.stock)` under a comment reading "a car back in a yard is back in the common supply, carrying nothing", and `pooled` does not do that: it strips the load's origin stamp and keeps `loaded` ON PURPOSE, because a train can retire at a Division Point with freight aboard. So the comment described an intention the call never carried out, and every car the Freight Agent cleared reached the Classification Yard carrying a load already delivered and already paid for. It bites hardest on coaches: `passengersDetrained` takes `coach && !loaded` out of the Division Yard and §2.2 refills that yard from Classification, so a cleared coach came back as stock that could never unload another passenger. Measured over five three-Day solitaire games: 18 loaded coaches in Classification against 6 empty. The red box is where a journey ENDS; clearing it sends the passengers out of the station, or the delivered load into the industry, and returns the CAR, empty. The option says that now instead of describing the counter that moves. SCOPED TO THE RED BOX ON PURPOSE. `retireTrain` also returns loaded cars and is left alone: that is what `pooled`'s own documentation describes, and a loaded car in a yard is pre-loaded cargo rather than dead stock — it can be made up and delivered, and a loaded coach can still detrain. Only the red box's contents had already finished their journey. GAMES IN PROGRESS DO RESUME, MEASURED RATHER THAN ARGUED. Yard contents change, so the worry was real. All twelve saves on the test server were pulled and replayed through `tryResumeSession` — the server's own boot check — against this build. Six resume, six refuse, and the six refusals are the SAME six, at the same moves, with the same codes, that 0.8.0.17 already logged. Nothing new was stranded. That pre-install replay is a better check than reading the next boot log, because it answers before the install rather than after. THE HISTORY SAID "Mainline card 7" and left the reader to remember what card 7 was — with two Plains dealt, which is why the slot is kept beside the name rather than replaced by it. A second fault sat one word to its left and nobody reported it: the name was built as `e.key.replace(/([A-Z])/g, ' $1')`, so `absSignals` printed as "abs Signals" while the action list directly above said "ABS Signals". `narrate` takes `mainlineAt` and `enhancementName` beside the `facilityAt` it already had, and `simpleCardName` is exported so the log reads the same table the buttons do. `mainlineModified` had both faults and is fixed with it. LEAVING A RUNNING GAME WAS A DEAD END. `enterSeating` hides the choice section and only the lobby's own two leave paths put it back; leaving a running game is a third route, so the lobby came back holding nothing but "Games you are in" with both doors on the page at display:none and no control able to reveal them. Reset in `runLobby`, which is the one function every route onto that screen goes through — which is exactly why the two paths that did it themselves missed a third. THE LOBBY'S ACTION BUTTONS CARRY THE BOARD'S AMBER. A list of the actions rather than `#lobby button`: the settings form under Create is a field of inputs, and amber on all of it would say everything is a move and so say nothing. A disabled Start game drops back to chrome. THE DEPARTMENT REFILL IS A RULE AND IS NOW WRITTEN DOWN. §6.2 — "if any of the Department decks is empty, draw a Home Office card and place it in the empty spot" — firing only when the draw actually empties the pile. Kept as implemented (Jesse's ruling) and stated in rules.md and home-deck.md, neither of which had ever mentioned it. A rule implemented from the prototype and never written down is a rule that surprises the table. 1016 fast tests and 35 sim tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUizFYCMHRWhbWwXhp7WPR
8.8 KiB
Station Master — design index
Station Master is a tabletop railroad-operations game set in the era of timetable-and-train-order railroading (1840–1950), being developed into a multiplayer digital game: players connect from a web browser and an authoritative server manages lobbies and game state.
Source of record
StationMasterPrototypeRules.pdf— the original prototype rulesStationMaster-PrototypeTurnChart.pdf— the 12-Stage turn chartDeck cards2.xlsx— the complete card list (115-card deck, 104 track pieces, 12 start cards)Mainline Cards.pdf— the ten Mainline card types with their speeds and entry pointsTrains3.pdf— all 22 train cards with names, speed class, consist and operating rulestracks.png— card art for track and industry cards
These stay as-is. Everything below is derived from them.
The last four arrived after the engine was built and expand the game roughly threefold. See
rules/implications.md: much of what is recorded below as decided was a placeholder for exactly this material, and the balance measurements in Gap 12 were taken against a ruleset that does not match the design.
For players and testers
Written to be handed to somebody who is about to play, rather than to somebody building the game.
| Document | What it is |
|---|---|
quickstart.md |
Start here if you have never played. The point of the game, how a Stage runs, what is on screen, how you win, a first twenty minutes, and what to report. |
rules.md |
The rules in full, as the engine actually runs them, with a FAQ. |
rules/as-built.md |
Every card, GENERATED from src/engine/content.ts and checked by a test, so it cannot disagree with the game. The table of record for per-card facts. |
home-deck.md |
How the Home Office deck is dealt, drawn and played out. |
mainline-deck.md |
The Mainline cards, how the deck is dealt, and what a card does to a train crossing it. |
components.md |
Rolling stock, the two yards, Crew Trays, the Fedora, the D12. |
None of these carry a version in the filename, and that is deliberate (Jesse, 2026-09-21): they
are kept current with every release rather than published as editions, so the name is always the
latest and each says at the top which build it describes. Four of them were stamped v0.4.5 until
v0.8.0.17 — the prototype rules edition they were first written against, never the version they
described — which read as though they documented a build five minor versions old.
Rules
| Document | What it is |
|---|---|
rules/rules-v0.1.md |
Faithful markdown transcription of the PDFs. No corrections. The baseline everything diffs against. |
rules/rules-v0.2.md |
The working ruleset. v0.1 with all ten gaps resolved, each change marked with its gap number. |
rules/card-reference.md |
⚠ SUPERSEDED — an invented 52-card placeholder, kept for its economy summary and its history. For what is printed on every card, read rules/as-built.md, which is generated from the code. |
rules/glossary.md |
Every defined term, alphabetized. |
rules/open-questions.md |
All thirteen gaps, each with the options considered, the decision, and the rationale. |
rules/implications.md |
Read this first. What the four recovered design files (Deck cards2.xlsx, Mainline Cards.pdf, Trains3.pdf, tracks.png) change — and which decisions they supersede. |
Architecture
Target is small self-hosted scale: a handful of concurrent games, hosted on a website or as a StartOS service. Stack is deliberately undecided; these documents describe what any implementation must do.
| Document | What it is |
|---|---|
architecture/components.md |
The project plan. All 20 buildable pieces — where each runs, when, MVP vs finished size — plus dependency graph and build order. |
architecture/overview.md |
Why the server is authoritative; state/intent/event split; transport; the rules-engine boundary. |
architecture/game-state.md |
The entity model, and the constraints that are easy to lose. |
architecture/protocol.md |
Intents, events, and per-player view redaction. |
architecture/lobby-and-sessions.md |
Create/join, seating, reconnection, persistence. |
architecture/deployment.md |
Plain web host vs StartOS service — and the one constraint that differs. |
architecture/multiplayer.md |
The multiplayer build plan. The authoritative server as a layer added on top, with solitaire unchanged and server-free — plus every decision taken, listed for review. |
Current status
v0.8.1.0. Rules formalized, card faces specified, architecture documented, and the game
playable solitaire and multiplayer in a browser against an authoritative server. See
../CHANGELOG.md for what each version changed and ../TODO.md for
what is open; this section is the shape of the project, not a running tally, because a
hand-maintained tally is what drifted last time.
What is built. The rules engine, the developer bot, the balance harness, the replay viewer, the playable page — and the server: lobby, game codes, seating, bots, per-seat reconnection, persistence by replaying the intent history, and an ordered replay of other players' turns on each player's own screen. It ships as a StartOS package. 999 fast tests and 35 simulation tests.
What is not. The 22 opponent-directed cards — the Action and Space-use categories — are held out of every dealt deck until they have an implementation, along with the two defensive cards whose only purpose is to answer them. Real audio: everything the game plays is synthesised from oscillators.
Balance is not where it should be, and no conclusion should be read from the revenue numbers yet.
The rebalance pass is deliberately deferred until the rules stop moving — card counts, industry counts
and the track mix all need moving together. TODO.md carries the standing distortions and the
measurements behind them.
Three rules are genuinely open, and they are the reason the README does not claim the rules are finished: where the Local's coach stands while its engine works (a §A.4 question, not a train-card question); Poling, the one card in the deck with no defined behaviour; and whether a Heavy Grade's orientation is rolled or chosen at setup. Thirteen gaps in the prototype rules were found and closed; these three came after.
The event log narrates; the intents reconstruct. Settled in v0.4.0 after the documentation had
claimed state = fold(events) for months. It is not true — the phase driver mutates state and then
describes it — so the canonical record is { seed, history: Intent[] } and persistence will be built
on that. architecture/protocol.md §3 has the reasoning;
test/events.test.ts pins it.
The economy, in one line: Local Operations actions are the main currency — one per Stage, twelve per Day — but inbound work bypasses them, which is where the game's variance comes from.
Stack: TypeScript, chosen so the engine runs in both the server and the browser — one
implementation of the movement rules, and instant affordances without a round-trip. Node 22 runs
TypeScript natively, so there is no build step during development, which also means erasable syntax
only: no enum, no parameter properties, no namespaces.
Running alongside, and independent of all of it: print-and-play components.
rules/as-built.md carries every card face as the game actually deals it, so
layout and art are the only remaining work before a table playtest — which answers the one question
simulation cannot, whether it is fun.
Run the harness with node src/sim/harness.ts [games] [length].
Watch a game: node src/sim/replay.ts --seed 1234 [--length standard] [--out replay.html]
writes a self-contained HTML file — open it in any browser and step through the game. It shows the
Division, the Office Area grid, every facility's boxes and MEN|AT|WORK track, plain-English
narration of each event, and a Blocked panel explaining why nothing is moving.
Running alongside, and independent of all of it: print-and-play components. card-reference.md
specifies every card face, so layout and art are the only remaining work before a table playtest —
which answers the one question simulation cannot, whether it is fun.