import { IMPOSSIBLE, VersionInfo } from '@start9labs/start-sdk' export const current = VersionInfo.of({ // `:` — 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.13:0', releaseNotes: { en_US: 'Bundles Station Master v0.8.0.13 — nine fixes from a live playtest, almost all of them about ' + 'what the table can see. **Games already running on this server resume normally**: no rule ' + 'changed. **The one that moved a car.** While your board is catching up on turns taken by ' + 'other players the game hides the action list, because a move made against a position that ' + 'has already moved on is a move made blind — but the cars in the Division Yard stayed ' + 'clickable. One click during a bot’s turn sent a real move against a board several turns ' + 'stale, and a coach left the yard. The yard follows the same rule as every other control now, ' + 'and the car counts beside it show the board you are looking at rather than the one the game ' + 'has already raced ahead to. **You can look at your own district again.** The row of buttons ' + 'for looking at another player’s Office Area had one for everybody except you, so the player ' + 'with nothing to do but wait was the one player who could not look at their own board. Your ' + 'seat is in the row now, and the buttons are in map order — west to east along the Division — ' + 'instead of the order people joined. **The Fedora is announced when it passes.** The ' + 'Superintendent changes hands at the end of Stages 3, 6, 9 and 12, and nothing said so out ' + 'loud; now it is announced on screen and written into the history, the same as a completed ' + 'run. **A collision says whose district it was and who paid for it.** The line named the ' + 'wreck and the reason and stopped there, leaving the 5 Revenue penalty in a separate entry ' + 'further down the log. **A Depot that cannot take another passenger says why.** It needs a ' + 'loaded coach waiting in the Division Yard, and when there is none the option simply vanished ' + 'from the menu with no explanation. The shortage itself stays — running out is part of the ' + 'game — but the panel now says how much room is left, what is missing, and how many loaded ' + 'coaches are waiting in the Classification Yard. **Smaller:** the train make-up panel says ' + '“working eastward” rather than “working left” — the same rule, but the map runs west to east ' + 'and the table is not everybody’s left; and the history panel keeps 90 lines instead of 60, ' + 'in the same amount of screen.', es_ES: 'Incluye Station Master v0.8.0.13 — nueve correcciones surgidas de una partida de prueba, ' + 'casi todas sobre lo que la mesa alcanza a ver. **Las partidas en curso en este servidor se ' + 'reanudan con normalidad**: no ha cambiado ninguna regla. **La que movió un vagón.** Mientras ' + 'tu tablero se pone al día con los turnos de los demás, el juego oculta la lista de acciones, ' + 'porque decidir sobre una posición que ya ha cambiado es decidir a ciegas; pero los vagones ' + 'del Patio de División seguían siendo pulsables. Un clic durante el turno de un bot envió una ' + 'jugada real contra un tablero atrasado varios turnos, y un coche salió del patio. Ahora el ' + 'patio sigue la misma regla que los demás controles, y los recuentos de vagones muestran el ' + 'tablero que estás viendo, no aquel al que la partida ya se ha adelantado. **Puedes volver a ' + 'mirar tu propio distrito.** La fila de botones para ver el Área de Oficinas de otro jugador ' + 'tenía uno para cada cual menos para ti, así que el jugador que solo podía esperar era el ' + 'único que no podía mirar su propio tablero. Tu asiento ya está en la fila, y los botones ' + 'siguen el orden del mapa — de oeste a este a lo largo de la División — en vez del orden de ' + 'llegada. **El sombrero se anuncia al pasar.** El Superintendente cambia al final de las ' + 'Etapas 3, 6, 9 y 12, y nada lo decía en voz alta; ahora se anuncia en pantalla y queda ' + 'escrito en el historial, igual que un recorrido completado. **Una colisión dice en qué ' + 'distrito fue y quién la paga.** La línea nombraba el accidente y la causa y ahí se detenía, ' + 'dejando la penalización de 5 de Ingresos en otra entrada más abajo. **Un Depósito que no ' + 'puede recibir otro pasajero explica por qué.** Necesita un coche cargado esperando en el ' + 'Patio de División, y cuando no lo hay la opción simplemente desaparecía del menú sin ' + 'explicación. La escasez se mantiene — quedarse sin material es parte del juego — pero ahora ' + 'el panel indica cuánto sitio queda, qué falta y cuántos coches cargados esperan en el Patio ' + 'de Clasificación. **Menores:** el panel de formación de trenes dice «trabajando hacia el ' + 'este» en lugar de «trabajando hacia la izquierda» — la misma regla, pero el mapa va de oeste ' + 'a este y la mesa no tiene una izquierda común; y el historial guarda 90 líneas en vez de 60, ' + 'en el mismo espacio.', de_DE: 'Enthält Station Master v0.8.0.13 — neun Korrekturen aus einer echten Testpartie, fast alle ' + 'davon darüber, was der Tisch sehen kann. **Laufende Spiele auf diesem Server werden normal ' + 'fortgesetzt**: keine Regel wurde geändert. **Die eine, die einen Wagen bewegt hat.** Während ' + 'Ihr Brett die Züge der anderen nachholt, verbirgt das Spiel die Aktionsliste — ein Zug gegen ' + 'eine Stellung, die längst weiter ist, ist ein Zug im Blindflug —, doch die Wagen im ' + 'Bezirksbahnhof blieben anklickbar. Ein Klick während des Zuges eines Bots schickte einen ' + 'echten Zug gegen ein mehrere Züge altes Brett, und ein Wagen verließ den Bahnhof. Der ' + 'Bahnhof folgt jetzt derselben Regel wie jedes andere Bedienelement, und die Wagenzahlen ' + 'daneben zeigen das Brett, das Sie sehen, statt jenes, dem das Spiel schon vorausgeeilt ist. ' + '**Sie können wieder auf Ihren eigenen Bezirk schauen.** Die Knopfreihe zum Betrachten ' + 'fremder Bürobereiche hatte einen Knopf für jeden außer für Sie — ausgerechnet der Spieler, ' + 'der nur warten konnte, kam nicht an sein eigenes Brett. Ihr Platz ist jetzt in der Reihe, ' + 'und die Knöpfe stehen in Kartenreihenfolge — von West nach Ost entlang der Division — statt ' + 'in der Beitrittsreihenfolge. **Der Hut wird angesagt, wenn er weitergegeben wird.** Der ' + 'Superintendent wechselt am Ende der Etappen 3, 6, 9 und 12, und nichts sagte es laut; jetzt ' + 'wird es auf dem Bildschirm angesagt und in den Verlauf geschrieben, wie eine abgeschlossene ' + 'Fahrt. **Eine Kollision nennt den Bezirk und den, der dafür zahlt.** Die Zeile nannte das ' + 'Wrack und den Grund und hörte dort auf; die 5 Einnahmen Strafe stand in einem eigenen ' + 'Eintrag weiter unten. **Ein Depot, das keinen weiteren Fahrgast aufnehmen kann, sagt ' + 'warum.** Es braucht einen beladenen Personenwagen im Bezirksbahnhof, und fehlte er, ' + 'verschwand die Option kommentarlos aus dem Menü. Der Mangel bleibt — auszugehen gehört zum ' + 'Spiel —, aber die Anzeige nennt jetzt den freien Platz, das Fehlende und die Zahl der ' + 'beladenen Wagen, die im Rangierbahnhof warten. **Kleineres:** die Zugbildungsanzeige sagt ' + '„ostwärts arbeitend“ statt „nach links arbeitend“ — dieselbe Regel, aber die Karte läuft von ' + 'West nach Ost, und der Tisch hat kein gemeinsames Links; und der Verlauf behält 90 Zeilen ' + 'statt 60, auf gleichem Raum.', pl_PL: 'Zawiera Station Master v0.8.0.13 — dziewięć poprawek z prawdziwej partii testowej, niemal ' + 'wszystkie o tym, co widzi stół. **Gry w toku na tym serwerze wznawiają się normalnie**: ' + 'żadna zasada się nie zmieniła. **Ta, która przestawiła wagon.** Kiedy twoja plansza nadrabia ' + 'ruchy pozostałych graczy, gra chowa listę akcji, bo ruch wykonany wobec pozycji, która już ' + 'się zmieniła, to ruch na ślepo — ale wagony na Stacji Rejonowej wciąż dawały się kliknąć. ' + 'Jedno kliknięcie w czasie tury bota wysłało prawdziwy ruch wobec planszy sprzed kilku tur i ' + 'wagon opuścił stację. Teraz stacja podlega tej samej regule co każdy inny element, a ' + 'liczniki wagonów obok pokazują planszę, którą widzisz, a nie tę, do której gra już pobiegła ' + 'naprzód. **Znów możesz zajrzeć do własnego okręgu.** W rzędzie przycisków do podglądania ' + 'Biura innego gracza był przycisk dla każdego prócz ciebie, więc gracz, który tylko czekał, ' + 'jako jedyny nie mógł spojrzeć na własną planszę. Twoje miejsce jest już w rzędzie, a ' + 'przyciski są w kolejności mapy — z zachodu na wschód wzdłuż Dywizji — zamiast w kolejności ' + 'dołączania. **Przekazanie kapelusza jest ogłaszane.** Superintendent zmienia się na koniec ' + 'Etapów 3, 6, 9 i 12, a nic nie mówiło tego na głos; teraz jest ogłaszane na ekranie i ' + 'zapisywane w historii, tak jak ukończony przejazd. **Kolizja mówi, czyj to okręg i kto za ' + 'nią płaci.** Wiersz podawał wrak i przyczynę, i na tym kończył — kara 5 Przychodu trafiała ' + 'do osobnego wpisu niżej. **Dworzec, który nie może przyjąć kolejnego pasażera, mówi ' + 'dlaczego.** Potrzebny jest załadowany wagon osobowy czekający na Stacji Rejonowej, a gdy go ' + 'nie było, opcja po prostu znikała z menu bez wyjaśnienia. Sam niedobór zostaje — kończące ' + 'się zasoby to część gry — ale panel podaje teraz, ile zostało miejsca, czego brakuje i ile ' + 'załadowanych wagonów czeka na Stacji Rozrządowej. **Drobniejsze:** panel formowania składu ' + 'mówi „pracując na wschód” zamiast „pracując w lewo” — ta sama zasada, ale mapa biegnie z ' + 'zachodu na wschód, a stół nie ma wspólnej lewej strony; historia zaś zachowuje 90 wierszy ' + 'zamiast 60, na tym samym miejscu.', fr_FR: 'Inclut Station Master v0.8.0.13 — neuf corrections issues d’une vraie partie de test, ' + 'presque toutes sur ce que la table peut voir. **Les parties en cours sur ce serveur ' + 'reprennent normalement** : aucune règle n’a changé. **Celle qui a déplacé un wagon.** ' + 'Pendant que votre plateau rattrape les tours des autres, le jeu masque la liste des actions, ' + 'car jouer contre une position qui a déjà avancé, c’est jouer à l’aveugle — mais les wagons ' + 'de la Gare de Division restaient cliquables. Un clic pendant le tour d’un bot a envoyé un ' + 'vrai coup contre un plateau vieux de plusieurs tours, et une voiture a quitté la gare. La ' + 'gare suit désormais la même règle que les autres commandes, et les comptes de wagons à côté ' + 'affichent le plateau que vous regardez, et non celui où la partie a déjà filé. **Vous pouvez ' + 'de nouveau regarder votre propre district.** La rangée de boutons permettant de consulter ' + 'l’Espace Bureaux d’un autre joueur en avait un pour tout le monde sauf vous : le joueur qui ' + 'n’avait qu’à attendre était le seul à ne pas pouvoir regarder son propre plateau. Votre ' + 'place y figure désormais, et les boutons suivent l’ordre de la carte — d’ouest en est le ' + 'long de la Division — plutôt que l’ordre d’arrivée. **Le chapeau est annoncé quand il ' + 'passe.** Le Superintendant change à la fin des Étapes 3, 6, 9 et 12, et rien ne le disait à ' + 'voix haute ; c’est maintenant annoncé à l’écran et inscrit dans l’historique, comme un ' + 'parcours achevé. **Une collision indique le district et celui qui paie.** La ligne nommait ' + 'l’accident et la cause puis s’arrêtait, laissant la pénalité de 5 Revenus dans une entrée ' + 'séparée plus bas. **Un Dépôt qui ne peut pas prendre un passager de plus dit pourquoi.** Il ' + 'lui faut une voiture chargée en attente à la Gare de Division, et faute de quoi l’option ' + 'disparaissait du menu sans explication. La pénurie demeure — manquer de matériel fait partie ' + 'du jeu — mais le panneau indique désormais la place restante, ce qui manque, et combien de ' + 'voitures chargées attendent à la Gare de Triage. **Plus petit :** le panneau de formation ' + 'des trains dit « en travaillant vers l’est » plutôt que « en travaillant vers la gauche » — ' + 'même règle, mais la carte va d’ouest en est et la table n’a pas de gauche commune ; et ' + 'l’historique conserve 90 lignes au lieu de 60, dans la même place.', }, 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-` 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. * * v0.8.0.12 TOUCHES THE ENGINE NOT AT ALL, which is the shortest entry this comment has ever * had a right to: `git diff v0.8.0.11..v0.8.0.12 -- src/engine/` is EMPTY. The release is the * server's HTTP surface, the browser client and the docs — a seat recovery flow (Gitea#33) for * a player whose browser has lost the token that is their only identity. * * WHAT IT ADDS, AND WHY NONE OF IT IS A MIGRATION: two routes (`POST /api/games//claim` * behind the admin secret, `POST /api/claim` for the player following the link), a `?claim=` * entry path in the client, and `seatedPlayers` on the administrative game listing. The codes * themselves live in memory and are deliberately NOT persisted — minted on demand and spent * within minutes, so a restart dropping them is the right failure rather than a loss. Nothing * new is written to the volume, no predicate was edited, and `check()` and `legal.ts` are * untouched, so no once-legal move became illegal. Games in progress resume, and the release * notes say so in all five locales. * * v0.8.0.13 TOUCHES THE ENGINE AGAIN, AND THE WHOLE DIFF IS TWO LINES. * `git diff v0.8.0.12..v0.8.0.13 -- src/engine/` adds a `superintendentChanged` variant to the * `GameEvent` union (`events.ts`) and the one `events.push` in `advance.ts` that emits it beside * the `actorChanged` that was already there — §5's handover of the Fedora, which the log had been * dropping as turn bookkeeping. Nothing was removed and nothing was edited: `check()`, `legal.ts` * and every predicate are untouched, so no once-legal move became illegal, and a widened event * cannot invalidate anything on disk because events are DERIVED by replaying a `SavedGame` rather * than stored in one — the same argument the v0.8.0.1 and v0.8.0.11 entries above make for * theirs. Games in progress resume, and the release notes say so in all five locales. * * THE REST IS THE SCREEN AND THE LOG, none of it on disk: the Division Yard chips now obey the * catch-up queue (they did not, and a click during it submitted a real intent against a stale * board), the yard counts are read from the shown board rather than the live game, the district * picker gained your own seat and is ordered by seat rather than by player index, collisions name * the district's owner and the 5 Revenue they lose, a Depot that cannot stock an outbound * passenger explains which car it is short of, and the history panel keeps 90 lines. The * passenger case is a RULING rather than a fix — §6.3 wants a loaded coach in the Division Yard, * and Jesse kept the shortage (Gitea#2's ruling) while ending the silence about it. */ up: async ({ effects }) => {}, down: IMPOSSIBLE, }, })