The bundled release is a playtest read back against its own save file. Three bugs, three rules that were working and undocumented, and three decisions — all from one three-Day game. GAMES IN PROGRESS: MOST WILL NOT RESUME, and that was measured before release rather than discovered after it. All thirteen saves on the test box were replayed through `tryResumeSession` — the server's own boot check — against this build: three resume, ten refuse. Six of the ten are one new rule, that a passenger Modifier may not be played at a Whistle Post. A refused save stops at a named move and is left untouched, so a game worth finishing can be finished by putting 0.8.1.0 back. THE OPENING CHANGED AND STRANDED NOTHING, which is the part that took care. Every district now opens on a Depot rather than a Whistle Post — two A/D tracks and passengers from Stage 1 — with the Whistle Post opening offered as the harder game at creation. That is the one house rule which changes how a game is DEALT rather than how it plays, so replaying an older save under today's default would produce a different railroad from intent one, silently. The game fills the setting on its replay paths only; every save that resumed before still resumes. THIS PACKAGE'S OWN CHANGE IS instructions.md, WHICH WAS A CHANGELOG. It had accumulated twenty "What changed in …" blocks going back to v0.7.2 — 380 of its 469 lines. A StartOS operator opening Instructions wants to know how to run the service, not read eighteen months of archaeology, and StartOS already shows what changed in a release: `releaseNotes`, which this package fills in five locales. The blocks are deleted; the file is 109 lines and is a manual. THE DOCUMENTATION LINK POINTED AT A `.local` ADDRESS on one LAN — a private Gitea URL serving raw Markdown, unreachable by anyone who installs this package. It points at the pages the package itself serves now, which are available wherever it is installed, need no external network, and always describe the installed build. Also in instructions.md: "What you get on StartOS" says that solitaire and the replay viewer come from the same web interface, since one address covers all three; and "Leaving, and coming back" now carries the seat rules that belong there — that a seat lives in one browser, that Forget cannot be undone by the player, and that the operator can put them back with Restore a Seat. The save warning the game now carries in four places is stated here too. No action, route, file model or interface changed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUizFYCMHRWhbWwXhp7WPR
354 lines
30 KiB
TypeScript
354 lines
30 KiB
TypeScript
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.2:0',
|
|
releaseNotes: {
|
|
en_US:
|
|
'Bundles Station Master v0.8.2 — a playtest read back against its own save file. **The change you ' +
|
|
'notice first: every district now opens on a DEPOT rather than a Whistle Post.** A Depot has two ' +
|
|
'arrival/departure tracks and works passengers from the first Stage; a Whistle Post has one track ' +
|
|
'and is not a passenger facility at all, so the opening of every game used to be spent unable to ' +
|
|
'earn from a passenger and one arrival away from a wreck. A table that wants the harder game can ' +
|
|
'tick "Players start with Whistle Posts, not Depots" when the game is created. **GAMES ALREADY ' +
|
|
'RUNNING ON THIS SERVER: MOST WILL NOT RESUME.** Measured before release rather than discovered ' +
|
|
'after: of the thirteen saved games on the test server, three resume and ten stop. Six of those ' +
|
|
'ten are one new rule — a Waiting Area, Restaurant or Hotel may no longer be played at a Whistle ' +
|
|
'Post, because a Whistle Post is not a passenger facility and the card did nothing there. A game ' +
|
|
'that stops says which move it stopped on and its file is left untouched, so anything worth ' +
|
|
'finishing can be finished by putting 0.8.1.0 back. **An Office could hold two trains on one ' +
|
|
'track.** The train an Interlocking had been holding at the Limits took the track that freed, and ' +
|
|
'the train arriving at that moment was let in after it without anyone checking there was still ' +
|
|
'room — so the collision the rules call for simply did not happen. **Your history could freeze ' +
|
|
'part-way through a game** and never gain another line, at a different moment for each player. ' +
|
|
'**Trains were released when they should have been held**, twice over: a card that says "trains ' +
|
|
'may pass" was clearing the whole line ahead rather than just itself, and a train standing at a ' +
|
|
'full Office was invisible to the check although it was about to pull out again. **Things that ' +
|
|
'happened silently now say so** — a train held against one coming the other way, a train released ' +
|
|
'from the Limits, and the train an Interlocking is holding, which now draws differently on the ' +
|
|
'board instead of only explaining itself in a tooltip. **When a move is refused, the board says ' +
|
|
'which rule refused it.** **The documentation is a set of proper pages now**, served by this ' +
|
|
'package and reachable from inside a game under This Game, instead of five files of plain text ' +
|
|
'where every table came out as rows of pipes. **Smaller:** a Modifier must sit square against ' +
|
|
'what it serves rather than on a diagonal; ordering a Second Section costs the card, which was ' +
|
|
'declared in the rules and never actually dealt; a train may always recover its own caboose; and ' +
|
|
'an industry may be built over a straight already laid.',
|
|
es_ES:
|
|
'Incluye Station Master v0.8.2 — una partida de prueba revisada contra su propio archivo ' +
|
|
'guardado. **El cambio que más se nota: cada distrito empieza ahora con un DEPÓSITO en vez de un ' +
|
|
'Apeadero.** Un Depósito tiene dos vías de llegada/salida y trabaja viajeros desde la primera ' +
|
|
'Etapa; un Apeadero tiene una vía y no es una instalación de viajeros, así que el comienzo de ' +
|
|
'cada partida se pasaba sin poder ganar con un viajero y a una llegada de un choque. Una mesa que ' +
|
|
'quiera el juego más difícil puede marcar "Los jugadores empiezan con Apeaderos, no Depósitos" al ' +
|
|
'crear la partida. **LAS PARTIDAS EN CURSO EN ESTE SERVIDOR: LA MAYORÍA NO SE REANUDARÁN.** ' +
|
|
'Medido antes de publicar, no descubierto después: de las trece partidas guardadas en el servidor ' +
|
|
'de pruebas, tres se reanudan y diez se detienen. Seis de esas diez se deben a una regla nueva: ' +
|
|
'una Sala de Espera, un Restaurante o un Hotel ya no pueden jugarse en un Apeadero, porque no es ' +
|
|
'una instalación de viajeros y la carta no hacía nada allí. Una partida que se detiene indica en ' +
|
|
'qué movimiento lo hizo y su archivo queda intacto, así que lo que merezca la pena terminar puede ' +
|
|
'terminarse reinstalando 0.8.1.0. **Una Oficina podía albergar dos trenes en una sola vía.** El ' +
|
|
'tren que el Enclavamiento retenía en los Límites ocupaba la vía que quedaba libre, y el que ' +
|
|
'llegaba en ese momento entraba detrás sin que nadie comprobara que aún hubiera sitio, así que el ' +
|
|
'choque que exigen las reglas simplemente no ocurría. **El historial podía congelarse a mitad de ' +
|
|
'partida** y no añadir ni una línea más, en un momento distinto para cada jugador. **Se soltaban ' +
|
|
'trenes que debían retenerse**, por dos motivos: una carta que dice "los trenes pueden cruzarse" ' +
|
|
'despejaba toda la línea por delante en vez de solo a sí misma, y un tren parado en una Oficina ' +
|
|
'llena era invisible para la comprobación aunque estuviera a punto de salir otra vez. **Lo que ' +
|
|
'antes ocurría en silencio ahora se dice** — un tren retenido ante otro de frente, un tren ' +
|
|
'liberado de los Límites, y el tren que retiene un Enclavamiento, que ahora se dibuja distinto en ' +
|
|
'el tablero en lugar de explicarse solo al pasar el ratón. **Cuando se rechaza un movimiento, el ' +
|
|
'tablero dice qué regla lo rechazó.** **La documentación son páginas de verdad**, servidas por ' +
|
|
'este paquete y accesibles desde dentro de la partida en Esta Partida, en lugar de cinco archivos ' +
|
|
'de texto plano donde cada tabla salía como filas de barras. **Menor:** un Modificador debe ir ' +
|
|
'pegado en ortogonal a lo que sirve, no en diagonal; ordenar una Segunda Sección cuesta su carta, ' +
|
|
'que estaba en las reglas y nunca se repartía; un tren siempre puede recuperar su propio furgón ' +
|
|
'de cola; y una industria puede construirse sobre una vía recta ya colocada.',
|
|
de_DE:
|
|
'Enthält Station Master v0.8.2 — ein Testspiel, gegen seine eigene Speicherdatei nachgelesen. ' +
|
|
'**Die auffälligste Änderung: jeder Bezirk beginnt jetzt mit einem DEPOT statt einem ' +
|
|
'Haltepunkt.** Ein Depot hat zwei Ankunfts-/Abfahrtsgleise und arbeitet Fahrgäste ab der ersten ' +
|
|
'Etappe ab; ein Haltepunkt hat ein Gleis und ist gar keine Personenanlage, sodass der Anfang ' +
|
|
'jeder Partie ohne Einnahmen aus Fahrgästen verging und eine Ankunft von einem Unfall entfernt ' +
|
|
'war. Wer das härtere Spiel will, hakt beim Erstellen "Spieler starten mit Haltepunkten statt ' +
|
|
'Depots" an. **LAUFENDE PARTIEN AUF DIESEM SERVER: DIE MEISTEN WERDEN NICHT FORTGESETZT.** Vor ' +
|
|
'der Freigabe gemessen, nicht hinterher entdeckt: von dreizehn Spielständen auf dem Testserver ' +
|
|
'werden drei fortgesetzt und zehn brechen ab. Sechs davon wegen einer neuen Regel — ein ' +
|
|
'Wartesaal, ein Restaurant oder ein Hotel darf nicht mehr an einem Haltepunkt gespielt werden, ' +
|
|
'der keine Personenanlage ist und an dem die Karte nichts bewirkte. Eine abgebrochene Partie ' +
|
|
'nennt den Zug, an dem sie stehen blieb, und ihre Datei bleibt unangetastet — was sich zu Ende ' +
|
|
'spielen lohnt, lässt sich mit 0.8.1.0 zu Ende spielen. **Ein Büro konnte zwei Züge auf einem ' +
|
|
'Gleis halten.** Der vom Stellwerk an der Grenze gehaltene Zug nahm das frei gewordene Gleis, und ' +
|
|
'der gerade ankommende wurde dahinter eingelassen, ohne dass noch einmal nach Platz gefragt wurde ' +
|
|
'— der von den Regeln geforderte Zusammenstoß blieb also aus. **Der Verlauf konnte mitten im ' +
|
|
'Spiel einfrieren** und keine Zeile mehr bekommen, bei jedem Spieler zu einem anderen Zeitpunkt. ' +
|
|
'**Züge fuhren los, die hätten warten müssen**, gleich zweifach: eine Karte mit "Züge können sich ' +
|
|
'begegnen" gab die ganze Strecke frei statt nur sich selbst, und ein an einem vollen Büro ' +
|
|
'stehender Zug war für die Prüfung unsichtbar, obwohl er gleich wieder ausfahren würde. **Was ' +
|
|
'vorher stumm geschah, wird jetzt gesagt** — ein gegen einen entgegenkommenden Zug gehaltener ' +
|
|
'Zug, ein von der Grenze freigegebener Zug, und der vom Stellwerk gehaltene Zug, der nun auf dem ' +
|
|
'Plan anders gezeichnet wird, statt sich nur im Tooltip zu erklären. **Wird ein Zug abgelehnt, ' +
|
|
'nennt der Plan die Regel.** **Die Dokumentation besteht jetzt aus richtigen Seiten**, von diesem ' +
|
|
'Paket ausgeliefert und aus der Partie heraus unter Diese Partie erreichbar, statt aus fünf ' +
|
|
'Textdateien, in denen jede Tabelle als Reihen von Strichen erschien. **Kleineres:** ein ' +
|
|
'Modifikator muss rechtwinklig an dem liegen, was er bedient, nicht diagonal; eine Zweite Sektion ' +
|
|
'kostet ihre Karte, die in den Regeln stand und nie ausgeteilt wurde; ein Zug darf seinen eigenen ' +
|
|
'Zugschlusswagen immer zurückholen; und eine Industrie darf über ein bereits verlegtes gerades ' +
|
|
'Gleis gebaut werden.',
|
|
pl_PL:
|
|
'Zawiera Station Master v0.8.2 — rozgrywkę testową odczytaną z jej własnego zapisu. **Zmiana, ' +
|
|
'którą widać najpierw: każdy okręg zaczyna teraz od DWORCA, a nie od przystanku.** Dworzec ma dwa ' +
|
|
'tory przyjazdowo-odjazdowe i obsługuje pasażerów od pierwszego etapu; przystanek ma jeden tor i ' +
|
|
'wcale nie jest obiektem pasażerskim, więc początek każdej partii mijał bez możliwości zarobku na ' +
|
|
'pasażerze i o jeden przyjazd od katastrofy. Stół, który chce trudniejszej gry, zaznacza przy ' +
|
|
'tworzeniu "Gracze zaczynają od przystanków, nie dworców". **PARTIE TRWAJĄCE NA TYM SERWERZE: ' +
|
|
'WIĘKSZOŚĆ SIĘ NIE WZNOWI.** Zmierzone przed wydaniem, a nie odkryte później: z trzynastu zapisów ' +
|
|
'na serwerze testowym trzy się wznawiają, a dziesięć zatrzymuje. Sześć z nich przez jedną nową ' +
|
|
'zasadę — poczekalni, restauracji ani hotelu nie można już zagrać na przystanku, bo nie jest on ' +
|
|
'obiektem pasażerskim i karta nic tam nie robiła. Zatrzymana partia podaje ruch, na którym ' +
|
|
'stanęła, a jej plik pozostaje nietknięty, więc to, co warto dokończyć, można dokończyć ' +
|
|
'przywracając 0.8.1.0. **Biuro mogło pomieścić dwa pociągi na jednym torze.** Pociąg trzymany ' +
|
|
'przez blokadę na granicy zajmował zwolniony tor, a przyjeżdżający w tej chwili wjeżdżał za nim, ' +
|
|
'choć nikt nie sprawdzał, czy jest jeszcze miejsce — więc wymagana zasadami kolizja po prostu nie ' +
|
|
'następowała. **Historia mogła zamarznąć w połowie partii** i nie dostać już ani jednej linii, u ' +
|
|
'każdego gracza w innym momencie. **Pociągi ruszały, choć powinny czekać**, z dwóch powodów: ' +
|
|
'karta mówiąca "pociągi mogą się mijać" zwalniała całą drogę przed sobą zamiast tylko siebie, a ' +
|
|
'pociąg stojący na pełnym biurze był niewidoczny dla sprawdzenia, choć za chwilę miał znowu ' +
|
|
'wyjechać. **To, co działo się po cichu, teraz jest mówione** — pociąg wstrzymany przed ' +
|
|
'nadjeżdżającym z naprzeciwka, pociąg zwolniony z granicy, oraz pociąg trzymany przez blokadę, ' +
|
|
'który rysuje się teraz inaczej zamiast tłumaczyć się tylko w dymku. **Gdy ruch zostaje ' +
|
|
'odrzucony, plansza podaje zasadę.** **Dokumentacja to teraz prawdziwe strony**, serwowane przez ' +
|
|
'ten pakiet i dostępne z wnętrza gry w karcie Ta Partia, zamiast pięciu plików zwykłego tekstu, w ' +
|
|
'których każda tabela wychodziła jako rzędy kresek. **Drobne:** modyfikator musi przylegać ' +
|
|
'prostopadle do tego, czemu służy, nie po skosie; zamówienie drugiej sekcji kosztuje kartę, która ' +
|
|
'była w zasadach i nigdy nie trafiała do talii; pociąg zawsze może odzyskać własny wagon ' +
|
|
'służbowy; a przemysł można zbudować na już położonym torze prostym.',
|
|
fr_FR:
|
|
'Contient Station Master v0.8.2 — une partie de test relue contre sa propre sauvegarde. **Le ' +
|
|
'changement que l\'on voit d\'abord : chaque district commence désormais avec un DÉPÔT et non un ' +
|
|
'arrêt.** Un dépôt a deux voies d\'arrivée et de départ et traite les voyageurs dès la première ' +
|
|
'étape ; un arrêt n\'a qu\'une voie et n\'est pas une installation voyageurs, si bien que le début ' +
|
|
'de chaque partie se passait sans pouvoir gagner sur un voyageur et à une arrivée d\'un accident. ' +
|
|
'Une table qui veut le jeu plus difficile coche « Les joueurs commencent avec des arrêts, pas des ' +
|
|
'dépôts » à la création. **PARTIES EN COURS SUR CE SERVEUR : LA PLUPART NE REPRENDRONT PAS.** ' +
|
|
'Mesuré avant la publication, et non découvert après : sur les treize sauvegardes du serveur de ' +
|
|
'test, trois reprennent et dix s\'arrêtent. Six de ces dix tiennent à une règle nouvelle — une ' +
|
|
'salle d\'attente, un restaurant ou un hôtel ne peut plus être joué sur un arrêt, qui n\'est pas ' +
|
|
'une installation voyageurs et où la carte ne faisait rien. Une partie qui s\'arrête indique à ' +
|
|
'quel coup, et son fichier reste intact : ce qui mérite d\'être terminé peut l\'être en ' +
|
|
'réinstallant 0.8.1.0. **Un bureau pouvait accueillir deux trains sur une seule voie.** Le train ' +
|
|
'que l\'enclenchement retenait aux limites prenait la voie libérée, et celui qui arrivait à cet ' +
|
|
'instant entrait derrière lui sans que personne ne revérifie la place — la collision exigée par ' +
|
|
'les règles n\'avait donc pas lieu. **L\'historique pouvait se figer en pleine partie** et ne plus ' +
|
|
'recevoir une seule ligne, à un moment différent pour chaque joueur. **Des trains partaient alors ' +
|
|
'qu\'ils auraient dû attendre**, pour deux raisons : une carte disant « les trains peuvent se ' +
|
|
'croiser » dégageait toute la voie devant elle au lieu d\'elle seule, et un train à l\'arrêt dans ' +
|
|
'un bureau plein était invisible au contrôle bien qu\'il fût sur le point de repartir. **Ce qui se ' +
|
|
'passait en silence se dit maintenant** — un train retenu face à un autre, un train libéré des ' +
|
|
'limites, et le train que retient un enclenchement, désormais dessiné différemment sur le plan au ' +
|
|
'lieu de ne s\'expliquer qu\'en infobulle. **Quand un mouvement est refusé, le plateau dit quelle ' +
|
|
'règle l\'a refusé.** **La documentation est faite de vraies pages**, servies par ce paquet et ' +
|
|
'accessibles depuis la partie sous Cette Partie, au lieu de cinq fichiers de texte brut où chaque ' +
|
|
'tableau sortait en rangées de barres. **Mineur :** un modificateur doit être accolé ' +
|
|
'orthogonalement à ce qu\'il dessert, jamais en diagonale ; commander une deuxième section coûte ' +
|
|
'sa carte, prévue par les règles et jamais distribuée ; un train peut toujours récupérer son ' +
|
|
'propre fourgon de queue ; et une industrie peut être bâtie sur une voie droite déjà posée.',
|
|
},
|
|
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.
|
|
*
|
|
* 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/<id>/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.
|
|
*
|
|
* v0.8.0.14 IS THE SECOND BUMP THAT BREAKS RESUMES, AND LIKE v0.8.0.10 IT DOES SO DELIBERATELY.
|
|
* A Modifier card may no longer be placed outside a player's Limits (§2.1). That REVERSES an
|
|
* earlier ruling of Jesse's — Modifiers were exempt because a Facility standing at the limit has
|
|
* three of its nine spots beyond the sign — and it is exactly the shape of change this package's
|
|
* `UPDATING.md` says to look for: a predicate in `apply.ts` changed its answer, so a move that
|
|
* was legal when it was played is refused on replay.
|
|
*
|
|
* MEASURED, NOT ESTIMATED. The Day 3 playtest save stops at intent 528 of 539 —
|
|
* `card.play c55 → (-2,4)` with the sign at column 3 — eleven moves short of where the table had
|
|
* got to. `tryResumeSession` names the move, `index.ts` logs the refusal, and the file is left
|
|
* untouched, so re-installing v0.8.0.13 finishes any game worth finishing. Jesse's call, knowing
|
|
* it strands the game that was on the box.
|
|
*
|
|
* THE REST OF THE ENGINE DIFF STRANDS NOTHING, and is worth separating from the part that does.
|
|
* `makeUpShort` is a new event reporting that the make-up round could give a train nothing;
|
|
* `switchingEnded` closes a switching turn; `trayMoved` carries the Stage's Move allowance and
|
|
* `consistSorted` carries where the engine ended up; `switch.sortConsist` gained an OPTIONAL
|
|
* `engineAt` (absent means the nose, which is what every sort did before, so an older save
|
|
* replays to the same train). Events are derived by replaying a save rather than stored in one,
|
|
* and an optional intent field defaulting to the old behaviour changes no past move.
|
|
*
|
|
* THE SMALL YARD RULE CHANGED IN THE PERMISSIVE DIRECTION, which cannot strand a save either: a
|
|
* train there may now be sorted with cars ahead of its engine (`implications.md` against the
|
|
* v0.4.5 card text, Jesse's ruling). Nothing once legal became illegal; §8.2's departure check is
|
|
* untouched and still holds a broken-backed train at the Office.
|
|
*
|
|
* v0.8.0.15 AND v0.8.0.16 CHANGE NO RULE AND STRAND NOTHING.
|
|
* `git diff v0.8.0.14..v0.8.0.16 -- src/engine/` is one sentence of card TEXT and one doc
|
|
* comment. The Interchange stopped telling players "Cars may be sorted into any new order here"
|
|
* — a printed capability that has never been implemented, reaching the board through
|
|
* `mainlineDescription`, which `view.ts` renders as a Mainline card's `what`. Nothing reads
|
|
* `sortsCars` to permit a sort; its only live use is marking the card an Extra may be made up
|
|
* on. No predicate was touched, so no once-legal move became illegal.
|
|
*
|
|
* The rest of those two releases is documentation inside the repository — a Quickstart for a new
|
|
* player, and the four hand-written references brought up to the game as it runs. None of it is
|
|
* served by the site or read by the service; it rides along in the image because the Dockerfile
|
|
* copies the game's tree in.
|
|
*
|
|
* SO THE RESUME WARNING IN THESE RELEASE NOTES IS ENTIRELY v0.8.0.14's, and it still applies:
|
|
* a box on 0.8.0.13:0 takes this update in one hop and meets the Modifier rule on the way.
|
|
*/
|
|
up: async ({ effects }) => {},
|
|
down: IMPOSSIBLE,
|
|
},
|
|
})
|