Files
station-master-startos/startos/versions/current.ts
T
JesseandClaude Opus 5 c6e17fc473 0.8.0.10:0 — bundle Station Master v0.8.0.10
Playtest fixes. The Superintendent is no longer asked to rule on a train
BEHIND the one departing — which cost a real game a collision, since holding
the departing train kept the only A/D track full and the train behind it
arrived into that. The screen names the player whose move it is showing while
the board catches up, the history no longer runs ahead of it, a Realignment
says which card it converted and flashes it on the map, a ruling made by
holding the office says so, and a seated player can download their own game
as a save file.

GAMES IN PROGRESS OFTEN WILL NOT RESUME, deliberately: a save replays through
the current rules, so a game holding a ruling the rules no longer ask for is
refused at that move. The server logs which move and leaves the file alone;
re-installing 0.8.0.9 finishes such a game. Measured at 28 of 40 recorded
four-seat games. Jesse's call: ship it. The release notes say so in all five
locales, and README and instructions.md carry the warning.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017nnuCv8UodHucFfx3LWEoX
2026-09-16 05:35:46 -04:00

211 lines
17 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { IMPOSSIBLE, VersionInfo } from '@start9labs/start-sdk'
export const current = VersionInfo.of({
// `<upstream>:<downstream>` — the upstream half tracks the bundled Station Master tag exactly
// (`versions.md`'s consistency checklist), so bumping the submodule pin and bumping this are one
// action. The downstream half moves only for packaging-only changes, and RESETS to `:0` when the
// upstream half moves.
//
// FOUR COMPONENTS IS FINE. ExVer's upstream half is an arbitrary-length dotted number, not semver
// — checked against the SDK's own parser rather than assumed, since every example in `versions.md`
// has three: `0.7.9.8:0` parses to upstream `[0,7,9,8]`, which sorts above `[0,7,9]`, so a box on
// 0.7.9:0 sees this as an update rather than refusing it as a downgrade.
version: '0.8.0.10:0',
releaseNotes: {
en_US:
'Bundles Station Master v0.8.0.10 — fixes from the first multiplayer playtests. **GAMES IN ' +
'PROGRESS ON THIS SERVER WILL OFTEN NOT RESUME AFTER THIS UPDATE**, because a rule about when ' +
'the Superintendent is asked to rule has been corrected, and a saved game is replayed through ' +
'the current rules: a game holding a ruling the rules no longer ask for stops there. The ' +
'server says which move it stopped at and leaves the game\u2019s file untouched, so nothing ' +
'is lost — and putting v0.8.0.9 back lets such a game be finished. The fix: the ' +
'Superintendent was asked to rule on a train BEHIND the one departing, which is nobody\u2019s ' +
'concern — it cost a real game a collision, because holding the departing train kept the only ' +
'arrival track full and the train behind it arrived into it and was destroyed. A train moving ' +
'away behind you no longer holds you either. Also in this release: while your board is ' +
'catching up on other players\u2019 moves, the screen now says whose move you are watching ' +
'instead of naming whoever the game is really waiting on, and the history no longer races ' +
'ahead of the board — lines appear as their moves are drawn, and Skip still shows everything ' +
'at once. The name on the Division map is readable again. A Realignment says which card it ' +
'converted — “Mainline card 3, Curves, converted to Plains” — and the card flashes on the map ' +
'as it changes. A move made by holding the Superintendent\u2019s office reads “Superintendent ' +
'Player Tom”, so it is clear it was not their ordinary turn, and no line names a player twice ' +
'any more. And a seated player can now download their own game as a save file from the play ' +
'screen, which previously only solitaire could do.',
es_ES:
'Incluye Station Master v0.8.0.10 — correcciones de las primeras partidas de prueba ' +
'multijugador. **LAS PARTIDAS EN CURSO EN ESTE SERVIDOR A MENUDO NO SE REANUDARÁN TRAS ESTA ' +
'ACTUALIZACIÓN**, porque se ha corregido una regla sobre cuándo se pide una decisión al ' +
'Superintendente, y una partida guardada se reproduce con las reglas actuales: una partida ' +
'que contenga una decisión que ya no se pide se detiene ahí. El servidor indica en qué ' +
'movimiento se detuvo y deja intacto el archivo, así que no se pierde nada; volver a la ' +
'v0.8.0.9 permite terminar esa partida. La corrección: se pedía decidir sobre un tren DETRÁS ' +
'del que salía, lo que no afecta a nadie — y costó una colisión real, porque retener el tren ' +
'que salía mantenía ocupada la única vía de llegada y el tren de atrás llegaba y quedaba ' +
'destruido. Un tren que se aleja por detrás tampoco te retiene ya. Además: mientras tu ' +
'tablero se pone al día con los movimientos de otros, la pantalla dice de quién es el ' +
'movimiento que estás viendo en lugar de nombrar a quien realmente espera la partida, y el ' +
'historial ya no se adelanta al tablero — las líneas aparecen según se dibujan sus ' +
'movimientos, y Saltar sigue mostrándolo todo de golpe. El nombre en el mapa de la División ' +
'vuelve a leerse bien. Una Realineación dice qué carta convirtió y la carta parpadea en el ' +
'mapa. Una jugada hecha como Superintendente se lee «Superintendent Player Tom», y ninguna ' +
'línea nombra dos veces al jugador. Y quien ocupa un asiento puede descargar su propia ' +
'partida como archivo desde la pantalla de juego, algo que antes solo podía el solitario.',
de_DE:
'Enthält Station Master v0.8.0.10 — Korrekturen aus den ersten Mehrspieler-Testpartien. ' +
'**LAUFENDE SPIELE AUF DIESEM SERVER WERDEN NACH DIESEM UPDATE OFT NICHT FORTGESETZT**, weil ' +
'eine Regel dazu korrigiert wurde, wann der Superintendent entscheiden muss, und ein ' +
'gespeichertes Spiel mit den aktuellen Regeln nachgespielt wird: ein Spiel mit einer ' +
'Entscheidung, die es nicht mehr gibt, bricht dort ab. Der Server nennt den Zug, an dem er ' +
'stehen blieb, und lässt die Datei unangetastet — nichts geht verloren, und mit v0.8.0.9 ' +
'lässt sich ein solches Spiel zu Ende spielen. Die Korrektur: Es wurde über einen Zug HINTER ' +
'dem abfahrenden entschieden, was niemanden betrifft — und kostete real eine Kollision, weil ' +
'das Halten des abfahrenden Zuges das einzige Ankunftsgleis belegt hielt und der Zug dahinter ' +
'hineinfuhr und zerstört wurde. Auch ein entgegenkommender Zug hinter Ihnen hält Sie nicht ' +
'mehr auf. Außerdem: Während Ihr Spielfeld die Züge der anderen nachholt, zeigt der ' +
'Bildschirm, wessen Zug Sie gerade sehen, statt den zu nennen, auf den das Spiel wirklich ' +
'wartet; und die Historie eilt dem Spielfeld nicht mehr voraus — Zeilen erscheinen, während ' +
'ihre Züge gezeichnet werden, und Überspringen zeigt weiterhin alles sofort. Der Name auf der ' +
'Divisionskarte ist wieder lesbar. Eine Neutrassierung nennt die umgewandelte Karte, und die ' +
'Karte blinkt auf der Landkarte. Ein Zug als Superintendent liest sich „Superintendent Player ' +
'Tom“, und keine Zeile nennt einen Spieler doppelt. Und wer einen Platz hat, kann sein Spiel ' +
'jetzt als Datei vom Spielbildschirm herunterladen — bisher konnte das nur das Solospiel.',
pl_PL:
'Zawiera Station Master v0.8.0.10 — poprawki z pierwszych testowych partii wieloosobowych. ' +
'**GRY W TOKU NA TYM SERWERZE CZĘSTO NIE ZOSTANĄ WZNOWIONE PO TEJ AKTUALIZACJI**, ponieważ ' +
'poprawiono zasadę mówiącą, kiedy Superintendent ma rozstrzygać, a zapis gry jest odtwarzany ' +
'według bieżących zasad: gra zawierająca rozstrzygnięcie, o które już się nie pyta, ' +
'zatrzymuje się w tym miejscu. Serwer podaje, przy którym ruchu stanął, i zostawia plik ' +
'nietknięty — nic nie ginie, a powrót do v0.8.0.9 pozwala taką grę dokończyć. Poprawka: ' +
'pytano o pociąg ZA odjeżdżającym, co nikogo nie dotyczy — i kosztowało to prawdziwe ' +
'zderzenie, bo zatrzymanie odjeżdżającego pociągu blokowało jedyny tor przyjazdowy, a pociąg ' +
'z tyłu wjeżdżał na niego i ulegał zniszczeniu. Pociąg oddalający się za tobą też już cię nie ' +
'zatrzymuje. Ponadto: gdy twoja plansza nadrabia ruchy innych, ekran mówi, czyj ruch ' +
'oglądasz, zamiast wskazywać tego, na kogo gra naprawdę czeka, a historia nie wyprzedza już ' +
'planszy — wiersze pojawiają się wraz z rysowaniem ruchów, a Pomiń nadal pokazuje wszystko ' +
'naraz. Nazwa na mapie Dywizji znów jest czytelna. Zmiana trasy podaje, którą kartę ' +
'przekształcono, a karta miga na mapie. Ruch wykonany jako Superintendent brzmi ' +
'„Superintendent Player Tom”, i żaden wiersz nie nazywa gracza dwa razy. A gracz przy stole ' +
'może teraz pobrać swoją grę jako plik z ekranu gry — wcześniej potrafił to tylko tryb ' +
'samotny.',
fr_FR:
'Inclut Station Master v0.8.0.10 — corrections issues des premières parties de test ' +
'multijoueurs. **LES PARTIES EN COURS SUR CE SERVEUR NE REPRENDRONT SOUVENT PAS APRÈS CETTE ' +
'MISE À JOUR**, car une règle sur le moment où le Superintendent doit trancher a été ' +
'corrigée, et une partie sauvegardée est rejouée selon les règles actuelles : une partie ' +
'contenant une décision que l’on ne demande plus s’arrête là. Le serveur indique à quel coup ' +
'il s’est arrêté et laisse le fichier intact — rien n’est perdu, et réinstaller la v0.8.0.9 ' +
'permet de terminer une telle partie. La correction : on faisait trancher au sujet d’un train ' +
'DERRIÈRE celui qui partait, ce qui ne concerne personne — et cela a coûté une vraie ' +
'collision, car retenir le train au départ gardait l’unique voie d’arrivée occupée et le ' +
'train derrière y arrivait et était détruit. Un train qui s’éloigne derrière vous ne vous ' +
'retient plus non plus. Également : pendant que votre plateau rattrape les coups des autres, ' +
'l’écran indique de qui est le coup que vous regardez au lieu de nommer celui que la partie ' +
'attend vraiment, et l’historique ne devance plus le plateau — les lignes apparaissent à ' +
'mesure que leurs coups sont dessinés, et Passer affiche toujours tout d’un coup. Le nom sur ' +
'la carte de la Division est de nouveau lisible. Un Réalignement indique quelle carte il a ' +
'convertie, et la carte clignote sur la carte. Un coup joué en tant que Superintendent se lit ' +
'« Superintendent Player Tom », et aucune ligne ne nomme deux fois un joueur. Enfin, un ' +
'joueur assis peut désormais télécharger sa propre partie sous forme de fichier depuis ' +
'l’écran de jeu, ce que seul le solitaire permettait.',
},
migrations: {
/**
* NOTHING TO CARRY, AND CHECKED THE SAME NARROW WAY v0.7.9.8's ENTRY WAS.
*
* The rule this package lives by is in the game's README: a save is a seed plus the moves
* played, replayed through the CURRENT rules, so the only thing that strands an older save is a
* change that makes a once-legal move illegal. So the question is never "how big was the
* release" — v0.8.0 is a large one — but "did any predicate change its answer".
*
* Measured with `git diff v0.7.9.8..v0.8.0.1 -- src/engine/`. The WHOLE engine diff is eight
* lines:
* four `GameEvent` types (`trayMoved`, `carsCoupled`, `carsDropped`, `consistSorted`) gained a
* `player` field, and the four places that build them pass it. `check()` is untouched, `legal.ts`
* and `advance.ts` are untouched, and no predicate anywhere was edited. Games in progress resume,
* and the release notes say so in all five locales rather than leaving it to be discovered.
*
* EVENTS ARE NOT PERSISTED, so widening four of them cannot invalidate anything on disk: a
* `SavedGame` is `{ seed, config, history: Intent[], … }` and the events are derived by replaying
* it. The narration TEXT of two lines changed with them ("Moved Train 3 …" where it used to read
* "CREW moved …"), which a resumed game simply regenerates — the log is not stored either.
*
* WHAT IS NOT COVERED BY THAT ARGUMENT, and why it still does not need a migration: the wire
* between this server and a browser gained a lot. `Push` now carries ordered presentation steps
* and a public-board baseline, and the board deltas changed shape. None of it is on disk, and a
* browser reloads onto the new page as part of the update, so there is no version skew to
* migrate — the same reasoning v0.7.9.8 used for its own `Frame` changes.
*
* v0.8.0.1 ADDED NOTHING TO THIS ARGUMENT: `git diff v0.8.0..v0.8.0.1 -- src/engine/` is empty.
* It changed `sim/pacing.ts`, `web/step-queue.ts` and `web/main.ts` — how long a step is shown
* and when the queue calls itself idle — which is presentation and touches neither the rules nor
* anything on disk.
*
* v0.8.0.2 DOES TOUCH THE ENGINE, and it is worth being exact about why that is still nothing to
* migrate. `git diff v0.8.0.1..v0.8.0.2 -- src/engine/` is three added lines: `collisionsPrevDay`
* declared on `GameState`, initialised to 0 in `setup.ts`, and assigned at the Day rollover in
* `advance.ts`. It is a counter that nothing checks — no predicate reads it, so no once-legal move
* became illegal.
*
* v0.8.0.3 THROUGH v0.8.0.7 ADDED NOTHING: `git diff v0.8.0.2..v0.8.0.7 -- src/engine/` is empty.
* One moved the Skip button, changed what the waiting row says, and stopped the speed control
* stretching the clock; one took a hostname out of comments; the last drew the Home Office deck
* and lit the pile a watched move touched, which is a diff of two frames the client already
* holds. None of it is a rule, and none of it is on disk.
*
* v0.8.0.8 IS DIFFERENT AND IS THE ONE TO READ CAREFULLY: it changes a RULE (Gitea#23). When the
* Home Office deck is reshuffled, a timetabled train sitting in the SALVAGE YARD is no longer
* recovered — it was played, its number is on the timetable, and it cannot be run twice. The same
* card in a DEPARTMENT was discarded rather than played and still comes back, as does any Extra.
* A duplicate went with it: `trainScheduled` had been pushing a synthetic `train-<n>` beside the
* real card, so four scheduled trains left eight entries in a pile of four.
*
* GAMES IN PROGRESS STILL RESUME, and the reason is the narrow one this package always asks
* (README § Design notes): a save is a seed plus the moves played, replayed through the CURRENT
* rules, so what strands one is a change that makes a once-legal move ILLEGAL. Nothing here does.
* `check()` is untouched; a draw is a draw whatever lies on top of the deck. What differs is the
* Salvage Yard's depth, which was double-counting, and which cards a reshuffle returns — and a
* reshuffle is effectively unreachable in ordinary play: eight games driven to 4000 moves across
* eight seeds produced ZERO. A game that had somehow reshuffled would replay to a different deck
* order rather than refusing to replay, which is worth knowing and is not the same as breaking.
*
* v0.8.0.9 TOUCHES THE ENGINE FOR SPEED, AND PROVED IT CHANGED NOTHING. `git diff v0.8.0.8..v0.8.0.9
* -- src/engine/` adds a route cache scoped to one unchanged position (`withRouteCache`), splits
* `applyIntent` into `prepareIntent` + `commitEvents`, trims allocation in `exploreMoves`, adds
* `legalSwitchingActions`, and exports `badlyMadeUp`, `protoCard` and `extendLimitsIfNeeded` for the
* bot. No predicate's answer changed: every event and intent of 32 seeded games hashed identical before
* and after each step, and the game's `test/route-cache.test.ts` pins the split. Everything else is the
* developer bot, whose past moves are already intents in a save. Games in progress resume.
*
* v0.8.0.10 IS THE FIRST BUMP THAT BREAKS RESUMES, AND IT DOES SO DELIBERATELY. It corrects WHEN §8.1
* asks the Superintendent to rule (Gitea#26): a train on a card BEHIND the one departing is nobody's
* concern, and was being counted. A save is the seed and the moves replayed through the CURRENT rules,
* so a game holding a `mainline.clearance` the rules no longer ask for is refused at that move with
* NO_PENDING_DECISION — `tryResumeSession` reports which move, and `index.ts` logs the refusal and
* leaves the file alone. Measured before shipping: 40 four-seat co-op games recorded by bots under
* v0.8.0.9 and replayed under this release — 28 stop, usually 5-40% of the way in. Jesse's call
* (2026-09-15): no games in progress need keeping, so this ships without a per-game switch for the old
* rule, and the release notes say so in all five locales. A game worth finishing can be finished by
* putting v0.8.0.9 back — the save file is untouched.
*
* The rest of the release is the screen and the log — who the board says it is waiting on, what the
* history says and when it says it, a flash on a realigned Mainline card — plus one new read-only
* route (`GET /api/save?token=…`) that hands a seated player their own save. None of it is on disk.
*
* A NEW STATE FIELD CANNOT STRAND A SAVE HERE, which is the property worth naming rather than
* re-deriving: a `SavedGame` holds `{ seed, config, history }` and NOT a serialised `GameState`.
* Resuming calls `createGame` — which fills the new field from `setup.ts` — and then replays the
* intents through the current rules, so the field is populated on the way past. There is no old
* shape on disk for a new field to be missing from.
*
* THE ONE THING THAT WOULD HAVE NEEDED CARE, had it been built the way the plan first described:
* the display-step collector runs inside `submit()`, and the resume path (`fromMultiplayerSave`)
* deliberately does not go through `submit()`. So replaying a saved game emits no steps and burns
* no sequence numbers. A test in the game pins that, because it is a property rather than an
* accident.
*/
up: async ({ effects }) => {},
down: IMPOSSIBLE,
},
})