Files
station-master-startos/startos/versions/current.ts
T
JesseandClaude Opus 5 da0e5d49d6 0.8.0.11:0 — bundle Station Master v0.8.0.11
Sixteen fixes from the second multiplayer playtest. Making up a train now says
what the consist STILL needs rather than only what its card calls for, says that
a player adds one car before the round passes on, marks the train being loaded
on the Division map, and gives an addable car in the yard the same amber every
other button wears. A train's arrival names whose Office it reached instead of
telling every seat they can work it. While the board catches up a player can
pause the playback, look at another player's Office Area, and sees one
consistent Day/Stage/phase rather than a turn chart running ahead of the board.
Saves download named after the game, with the join code, Day and Stage.

GAMES IN PROGRESS RESUME NORMALLY, and that was measured rather than assumed:
`git diff v0.8.0.10..v0.8.0.11 -- src/engine/` is three files and nineteen
insertions — an `owner` field on the `trainArrived` event, the one line that
fills it, and a reworded card description. `check()` and `legal.ts` are
untouched and no predicate changed its answer, so nothing once legal became
illegal, and events are derived rather than persisted. That is the opposite of
0.8.0.10, which deliberately stranded games holding a ruling the rules no longer
ask for — README, instructions.md and the release notes in all five locales say
which of the two this is.

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

241 lines
20 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.11:0',
releaseNotes: {
en_US:
'Bundles Station Master v0.8.0.11 — sixteen fixes from the second multiplayer playtest. ' +
'**Games already running on this server resume normally.** No rule changed: the whole engine ' +
'difference is one field added to one event and one card description reworded, so nothing ' +
'that was legal has become illegal. **Making up a train is far clearer.** The panel now says ' +
'what the train STILL needs rather than only what its card calls for, says that you add ONE ' +
'car before the round passes to the next player, marks the train being loaded on the Division ' +
'map, and highlights the cars you may add in the same amber every other button uses — the ' +
'cars in the Division Yard have always been the buttons, but they did not look like it. **A ' +
'train’s arrival now says whose Office it reached**, and no longer tells every player they ' +
'can work it when it is standing in somebody else’s district. **While your board catches up** ' +
'on other players’ moves you can now PAUSE it and look as long as you like — a paused move ' +
'keeps the time it had left — and you can open any other player’s Office Area for a look. The ' +
'Day, Stage and phase on screen now match the move you are watching instead of running ahead ' +
'of it, so being five moves behind looks the same everywhere on the page. When nobody is ' +
'being waited on, the screen says what the Division is doing — “the Division is moving ' +
'trains” — instead of “waiting on nobody”. **Saved games download named after the game**, ' +
'with the join code, Day and Stage in the filename. Also: the LIMITS sign is printed below ' +
'its card instead of through the middle of the word, the dividing line on a Mainline card can ' +
'actually be seen, a Restaurant or Hotel next to a Whistle Post says it is dormant until the ' +
'Office is upgraded rather than appearing to do nothing, only the one card that reads ' +
'FAST/SLOW mentions it, and the version number appears once in the page header instead of ' +
'twice.',
es_ES:
'Incluye Station Master v0.8.0.11 — dieciséis correcciones de la segunda partida de prueba ' +
'multijugador. **Las partidas en curso en este servidor se reanudan con normalidad**: no ha ' +
'cambiado ninguna regla. **Formar un tren es mucho más claro.** El panel indica ahora lo que ' +
'al tren le FALTA, y no solo lo que pide su carta; explica que añades UN vagón y el turno ' +
'pasa al siguiente jugador; marca en el mapa de la División el tren que se está formando; y ' +
'resalta los vagones que puedes añadir con el mismo ámbar que los demás botones — los vagones ' +
'del Patio de División siempre fueron los botones, pero no lo parecían. **La llegada de un ' +
'tren dice a qué Oficina llegó**, y ya no le dice a todo el mundo que puede trabajarlo cuando ' +
'está en el distrito de otro jugador. **Mientras tu tablero se pone al día** con los ' +
'movimientos de otros, ahora puedes PAUSARLO el tiempo que quieras — el movimiento pausado ' +
'conserva el tiempo que le quedaba — y abrir el Área de Oficina de cualquier otro jugador ' +
'para verla. El Día, la Etapa y la fase en pantalla coinciden con el movimiento que estás ' +
'viendo, así que ir cinco movimientos por detrás se ve igual en toda la página. Cuando no se ' +
'espera a nadie, la pantalla dice qué está haciendo la División — «la División está moviendo ' +
'trenes» — en lugar de «esperando a nadie». **Las partidas guardadas se descargan con el ' +
'nombre de la partida**: el código de acceso, el Día y la Etapa en el nombre del archivo. ' +
'Además: el letrero LIMITS se imprime debajo de su carta y no atravesando la palabra, la ' +
'línea divisoria de una carta de Línea Principal se ve de verdad, un Restaurante u Hotel ' +
'junto a un Whistle Post indica que está inactivo hasta que se mejore la Oficina, solo la ' +
'única carta que lee RÁPIDO/LENTO lo menciona, y el número de versión aparece una vez en la ' +
'cabecera en lugar de dos.',
de_DE:
'Enthält Station Master v0.8.0.11 — sechzehn Korrekturen aus der zweiten Mehrspieler-' +
'Testpartie. **Laufende Spiele auf diesem Server werden normal fortgesetzt**: keine Regel ' +
'wurde geändert. **Das Zusammenstellen eines Zuges ist viel klarer.** Das Panel nennt jetzt, ' +
'was dem Zug noch FEHLT, statt nur, was seine Karte verlangt; es sagt, dass Sie EINEN Wagen ' +
'anhängen und die Runde dann zum nächsten Spieler weitergeht; es markiert den Zug, der gerade ' +
'beladen wird, auf der Divisionskarte; und die Wagen, die Sie anhängen dürfen, leuchten im ' +
'selben Bernstein wie jede andere Schaltfläche — die Wagen im Divisionsbahnhof waren immer ' +
'schon die Schaltflächen, sahen aber nicht danach aus. **Die Ankunft eines Zuges nennt jetzt, ' +
'wessen Office er erreicht hat**, und sagt nicht mehr allen, sie könnten ihn bearbeiten, wenn ' +
'er im Bezirk eines anderen steht. **Während Ihr Spielfeld die Züge der anderen nachholt**, ' +
'können Sie es nun PAUSIEREN, so lange Sie wollen — der pausierte Zug behält die Zeit, die ' +
'ihm blieb — und das Office-Gebiet jedes anderen Spielers ansehen. Tag, Etappe und Phase auf ' +
'dem Bildschirm stimmen mit dem Zug überein, den Sie gerade sehen; fünf Züge im Rückstand zu ' +
'sein sieht überall auf der Seite gleich aus. Wartet das Spiel auf niemanden, zeigt der ' +
'Bildschirm, was die Division tut — „die Division bewegt Züge“ — statt „wartet auf niemanden“. ' +
'**Gespeicherte Spiele werden nach dem Spiel benannt**: Beitrittscode, Tag und Etappe im ' +
'Dateinamen. Außerdem: Das LIMITS-Schild steht unter seiner Karte statt mitten im Wort, die ' +
'Trennlinie auf einer Hauptstreckenkarte ist tatsächlich zu sehen, ein Restaurant oder Hotel ' +
'neben einem Whistle Post meldet sich als ruhend, bis das Office ausgebaut wird, nur die eine ' +
'Karte, die SCHNELL/LANGSAM liest, erwähnt es, und die Versionsnummer erscheint einmal in der ' +
'Kopfzeile statt zweimal.',
pl_PL:
'Zawiera Station Master v0.8.0.11 — szesnaście poprawek z drugiej testowej partii ' +
'wieloosobowej. **Gry w toku na tym serwerze wznawiają się normalnie**: żadna zasada się nie ' +
'zmieniła. **Kompletowanie pociągu jest dużo jaśniejsze.** Panel pokazuje teraz, czego ' +
'pociągowi jeszcze BRAKUJE, a nie tylko czego wymaga jego karta; mówi, że dodajesz JEDEN ' +
'wagon, po czym kolejka przechodzi do następnego gracza; zaznacza kompletowany pociąg na ' +
'mapie Dywizji; a wagony, które możesz dodać, świecą tym samym bursztynem co pozostałe ' +
'przyciski — wagony na Stacji Dywizyjnej zawsze były przyciskami, ale tak nie wyglądały. ' +
'**Przyjazd pociągu podaje, czyje Biuro osiągnął**, i nie mówi już wszystkim, że mogą go ' +
'obsłużyć, gdy stoi w dzielnicy innego gracza. **Gdy twoja plansza nadrabia ruchy innych**, ' +
'możesz ją teraz ZATRZYMAĆ na dowolnie długo — zatrzymany ruch zachowuje pozostały mu czas — ' +
'i obejrzeć Biuro dowolnego innego gracza. Dzień, Etap i faza na ekranie zgadzają się z ' +
'ruchem, który oglądasz, więc pięć ruchów w tyle wygląda tak samo na całej stronie. Gdy gra ' +
'na nikogo nie czeka, ekran mówi, co robi Dywizja — „Dywizja przesuwa pociągi” — zamiast ' +
'„czeka na nikogo”. **Zapisane gry pobierają się nazwane po grze**: kod dołączenia, Dzień i ' +
'Etap w nazwie pliku. Ponadto: napis LIMITS jest drukowany pod kartą, a nie przez środek ' +
'słowa, linia podziału na karcie Magistrali jest wreszcie widoczna, Restauracja lub Hotel ' +
'obok Whistle Post informuje, że jest uśpiony do czasu rozbudowy Biura, tylko ta jedna karta, ' +
'która czyta SZYBKO/WOLNO, o tym wspomina, a numer wersji pojawia się w nagłówku raz zamiast ' +
'dwa razy.',
fr_FR:
'Inclut Station Master v0.8.0.11 — seize corrections issues de la deuxième partie de test ' +
'multijoueurs. **Les parties en cours sur ce serveur reprennent normalement** : aucune règle ' +
'n’a changé. **Composer un train est bien plus clair.** Le panneau indique désormais ce qu’il ' +
'MANQUE au train, et non plus seulement ce que sa carte demande ; il précise que vous ajoutez ' +
'UN wagon avant que le tour ne passe au joueur suivant ; il marque sur la carte de la ' +
'Division le train en cours de composition ; et les wagons que vous pouvez ajouter ' +
's’allument dans le même ambre que tous les autres boutons — les wagons du Dépôt de Division ' +
'ont toujours été les boutons, mais n’en avaient pas l’air. **L’arrivée d’un train indique à ' +
'quel Office il est parvenu**, et ne dit plus à tout le monde qu’il peut le manœuvrer alors ' +
'qu’il stationne dans le district d’un autre. **Pendant que votre plateau rattrape** les ' +
'coups des autres, vous pouvez maintenant le METTRE EN PAUSE aussi longtemps que vous voulez ' +
'— le coup mis en pause conserve le temps qu’il lui restait — et ouvrir l’Aire de Bureau de ' +
'n’importe quel autre joueur. Le Jour, l’Étape et la phase affichés correspondent au coup que ' +
'vous regardez : avoir cinq coups de retard se voit de la même façon partout sur la page. ' +
'Quand la partie n’attend personne, l’écran dit ce que fait la Division — « la Division fait ' +
'circuler les trains » — au lieu de « en attente de personne ». **Les parties sauvegardées ' +
'portent le nom de la partie** : code d’accès, Jour et Étape dans le nom du fichier. ' +
'Également : le panneau LIMITS est imprimé sous sa carte au lieu de traverser le mot, la ' +
'ligne de séparation d’une carte de Ligne principale est enfin visible, un Restaurant ou un ' +
'Hôtel à côté d’un Whistle Post indique qu’il est en sommeil jusqu’à l’amélioration de ' +
'l’Office, seule la carte qui lit RAPIDE/LENT le mentionne, et le numéro de version apparaît ' +
'une fois dans l’en-tête au lieu de deux.',
},
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.
*
* v0.8.0.11 RESUMES, AND IT WAS MEASURED THE SAME NARROW WAY — which matters more than usual
* here, because the release immediately before it is the one that did NOT.
* `git diff v0.8.0.10..v0.8.0.11 -- src/engine/` is three files and nineteen insertions:
* `trainArrived` gains an `owner` field (`events.ts`), the one line in `advance.ts` that fills
* it from `playerAtSeat`, and a Mainline card's printed description losing a sentence about a
* speed that is not drawn on the card (`content.ts`). `check()` and `legal.ts` are untouched and
* no predicate was edited, so nothing once legal became illegal. Events are not persisted — a
* `SavedGame` is `{ seed, config, history }` and the events are derived by replaying it — so
* widening one of them cannot invalidate anything on disk, exactly as the v0.8.0.1 entry above
* argues for four of them.
*
* THE REST OF THE RELEASE IS THE SCREEN: a Pause control for the catch-up queue, a one-render
* look at another player's Office Area, the turn chart following the step being replayed rather
* than the live game, "still needs" on the make-up panel, the amber on an addable yard chip, and
* save filenames carrying the join code. None of it is a rule and none of it is on disk.
*/
up: async ({ effects }) => {},
down: IMPOSSIBLE,
},
})