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.6:0', releaseNotes: { en_US: 'Bundles Station Master v0.8.6 — four releases from a code audit of the game (engine, server, ' + 'browser, tests), every finding checked against the code before it was acted on. **GAMES ALREADY ' + 'RUNNING ON THIS SERVER: the three that 0.8.2 promised would resume now do, and the ten it refused ' + 'stay refused.** 0.8.2 had in fact stranded all thirteen — the Second Section card went into the deck ' + "after that release's save check, and a deck one card larger shuffles into a different order from the " + 'same seed — so this build deals an older save from the deck it was dealt from. **The engine:** a ' + "player could switch a rival's train, and the rival paid the Moves; a rival's crew standing in their " + 'own district blocked yours; drawing the last card off a Department could put the drawn card in two ' + "places and lose the refill; the Freight Agent's unjam cleared the first load rather than the one you " + 'named; the collision limit could not fire in Stage 12; an Expedite fault was charged once per ' + 'clearance question rather than once per phase; a train the Interlocking held at the Limits was only ' + 'released by another arrival, never by a departure. **The table:** a player who left a lobby kept a ' + 'token that could watch and move for whoever took the seat next; the first move after reloading the ' + 'page could be silently swallowed; two moves at once could tear the save file, and one torn file took ' + 'every game on the server down at start; an error on a stream could stop the whole service; a ' + 'double-click did the thing twice. **Saving:** in a Competitive game the save file — which carries ' + "the seed, and so every rival's hand — can be downloaded once the game is over; in Co-op at any time. " + '**Also:** the documentation pages render nested lists properly, the make-up panel only lists cars ' + 'the train can still take, and the unused-code check is now part of the build.', es_ES: 'Incluye Station Master v0.8.6 — cuatro versiones salidas de una auditoría del código del juego ' + '(motor, servidor, navegador, pruebas), cada hallazgo comprobado contra el código antes de actuar. ' + '**PARTIDAS YA EN MARCHA EN ESTE SERVIDOR: las tres que 0.8.2 prometió reanudar ahora se reanudan, y ' + 'las diez que rechazó siguen rechazadas.** En realidad 0.8.2 había dejado colgadas las trece — la ' + 'carta Segunda Sección entró en el mazo después de la comprobación de partidas guardadas de esa ' + 'versión, y un mazo con una carta más se baraja en otro orden con la misma semilla — así que esta ' + 'versión reparte una partida antigua con el mazo con el que se repartió. **El motor:** un jugador ' + 'podía maniobrar el tren de un rival, y el rival pagaba los Movimientos; una cuadrilla rival en su ' + 'propio distrito bloqueaba el tuyo; robar la última carta de un Departamento podía duplicarla y ' + 'perder la de relleno; el desatasco del Agente de Carga retiraba la primera carga y no la señalada; ' + 'el límite de colisiones no podía dispararse en la Etapa 12; una falta de Expedición se cobraba por ' + 'cada pregunta de vía libre y no una vez por fase; un tren retenido en los Límites solo lo liberaba ' + 'otra llegada, nunca una salida. **La mesa:** quien abandonaba una sala conservaba una ficha con la ' + 'que podía ver y mover por quien ocupara después su asiento; la primera jugada tras recargar la ' + 'página podía perderse en silencio; dos jugadas a la vez podían corromper el archivo guardado, y un ' + 'archivo corrupto tumbaba todas las partidas al arrancar; un error en un flujo podía parar todo el ' + 'servicio; un doble clic hacía la cosa dos veces. **Guardar:** en una partida Competitiva el archivo ' + 'guardado — que lleva la semilla, y con ella la mano de cada rival — se puede descargar cuando la ' + 'partida ha terminado; en Cooperativa, en cualquier momento.', de_DE: 'Enthält Station Master v0.8.6 — vier Versionen aus einer Code-Prüfung des Spiels (Engine, Server, ' + 'Browser, Tests), jeder Befund vor dem Handeln am Code geprüft. **BEREITS LAUFENDE PARTIEN AUF DIESEM ' + 'SERVER: die drei, die 0.8.2 als fortsetzbar versprach, sind es jetzt; die zehn abgelehnten bleiben ' + 'abgelehnt.** Tatsächlich hatte 0.8.2 alle dreizehn gestrandet — die Karte Zweiter Abschnitt kam nach ' + 'der Spielstand-Prüfung dieser Version in den Stapel, und ein um eine Karte größerer Stapel mischt ' + 'sich aus demselben Startwert anders — daher teilt diese Version einen älteren Spielstand aus dem ' + 'Stapel aus, aus dem er stammte. **Die Engine:** ein Spieler konnte den Zug eines Gegners rangieren, ' + 'und der Gegner zahlte die Züge; eine gegnerische Mannschaft im eigenen Bezirk blockierte deinen; das ' + 'Ziehen der letzten Karte einer Abteilung konnte sie verdoppeln und die Nachschubkarte verlieren; das ' + 'Freiräumen des Frachtagenten nahm die erste Ladung statt der benannten; das Kollisionslimit konnte ' + 'in Etappe 12 nicht greifen; ein Eilzug-Vergehen wurde je Freigabefrage statt einmal je Phase ' + 'berechnet; ein an der Grenze gehaltener Zug wurde nur durch eine weitere Ankunft freigegeben, nie ' + 'durch eine Abfahrt. **Der Tisch:** wer eine Lobby verließ, behielt ein Token, mit dem er für den ' + 'nächsten Sitzinhaber zusehen und ziehen konnte; der erste Zug nach dem Neuladen der Seite konnte ' + 'stillschweigend verschluckt werden; zwei Züge zugleich konnten die Spielstanddatei zerreißen, und ' + 'eine zerrissene Datei legte beim Start jede Partie lahm; ein Fehler auf einem Stream konnte den ' + 'ganzen Dienst anhalten; ein Doppelklick tat die Sache zweimal. **Speichern:** in einer ' + 'Wettbewerbspartie lässt sich die Spielstanddatei — sie enthält den Startwert und damit jede ' + 'gegnerische Hand — erst nach Spielende herunterladen; im Koop-Modus jederzeit.', pl_PL: 'Zawiera Station Master v0.8.6 — cztery wydania z audytu kodu gry (silnik, serwer, przeglądarka, ' + 'testy), każde ustalenie sprawdzone w kodzie przed działaniem. **GRY JUŻ TRWAJĄCE NA TYM SERWERZE: ' + 'trzy, które 0.8.2 obiecało wznowić, teraz się wznawiają; dziesięć odrzuconych pozostaje ' + 'odrzuconych.** W istocie 0.8.2 unieruchomiło wszystkie trzynaście — karta Druga Sekcja trafiła do ' + 'talii po kontroli zapisów tego wydania, a talia większa o jedną kartę tasuje się inaczej z tego ' + 'samego ziarna — więc to wydanie rozdaje starszy zapis z talii, z której został rozdany. **Silnik:** ' + 'gracz mógł manewrować pociągiem rywala, a rywal płacił Ruchy; załoga rywala w jego własnym okręgu ' + 'blokowała twój; dobranie ostatniej karty z Działu mogło ją zdublować i zgubić kartę uzupełniającą; ' + 'odblokowanie przez Agenta Towarowego usuwało pierwszy ładunek zamiast wskazanego; limit kolizji nie ' + 'mógł zadziałać w Etapie 12; kara za Ekspres była naliczana za każde pytanie o zwolnienie toru ' + 'zamiast raz na fazę; pociąg zatrzymany na Granicy zwalniało tylko kolejne przybycie, nigdy odjazd. ' + '**Stół:** kto opuścił poczekalnię, zachowywał token pozwalający oglądać i grać za tego, kto zajął ' + 'miejsce po nim; pierwszy ruch po przeładowaniu strony mógł zostać po cichu połknięty; dwa ruchy ' + 'naraz mogły uszkodzić plik zapisu, a jeden uszkodzony plik wyłączał przy starcie wszystkie gry; błąd ' + 'w strumieniu mógł zatrzymać całą usługę; podwójne kliknięcie robiło rzecz dwa razy. **Zapis:** w ' + 'grze Rywalizacyjnej plik zapisu — zawierający ziarno, a więc rękę każdego rywala — można pobrać po ' + 'zakończeniu gry; w Kooperacji w każdej chwili.', fr_FR: "Inclut Station Master v0.8.6 — quatre versions issues d'un audit du code du jeu (moteur, serveur, " + "navigateur, tests), chaque constat vérifié dans le code avant d'agir. **PARTIES DÉJÀ EN COURS SUR CE " + 'SERVEUR : les trois que 0.8.2 promettait de reprendre reprennent désormais, et les dix refusées ' + 'restent refusées.** En réalité 0.8.2 les avait toutes bloquées — la carte Seconde Section est entrée ' + "dans le paquet après le contrôle des sauvegardes de cette version, et un paquet d'une carte de plus " + 'se mélange autrement à partir de la même graine — cette version distribue donc une ancienne ' + "sauvegarde avec le paquet dont elle vient. **Le moteur :** un joueur pouvait manœuvrer le train d'un " + 'rival, et le rival payait les Mouvements ; une équipe rivale dans son propre district bloquait le ' + "vôtre ; piocher la dernière carte d'un Département pouvait la dupliquer et perdre la carte de " + "remplacement ; le déblocage de l'Agent de fret retirait le premier chargement et non celui désigné ; " + "la limite de collisions ne pouvait pas se déclencher à l'Étape 12 ; une faute d'Express était " + 'comptée à chaque question de voie libre et non une fois par phase ; un train retenu à la Limite ' + "n'était libéré que par une autre arrivée, jamais par un départ. **La table :** qui quittait un salon " + 'gardait un jeton permettant de voir et de jouer pour celui qui prenait ensuite le siège ; le premier ' + 'coup après rechargement de la page pouvait être avalé en silence ; deux coups à la fois pouvaient ' + 'déchirer le fichier de sauvegarde, et un fichier déchiré mettait toutes les parties hors service au ' + 'démarrage ; une erreur sur un flux pouvait arrêter tout le service ; un double clic faisait la chose ' + 'deux fois. **Sauvegarde :** en partie Compétitive, le fichier — qui porte la graine, donc la main de ' + 'chaque rival — se télécharge une fois la partie terminée ; en Coopération, à tout moment.', }, 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. * * 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. * * v0.8.3 THROUGH v0.8.6 ARE THE AUDIT, AND THE RESUME QUESTION WAS MEASURED ON THIS SERVER'S OWN * SAVES rather than argued from the diff — because the argument had already failed once. 0.8.2's * entry says three of thirteen resume; the boot log on the box refused thirteen of thirteen, at * move 3, CARD_NOT_IN_HAND. The Second Section card had gone into the deck after 0.8.2's save * check ran, and a deck one card larger shuffles into a different order from the same seed. v0.8.3 * adds `secondSectionCard` to the house rules — no dial, a fact about the deal — and `withSavedDeal` * sets it false for a saved config that predates `startingOffice`. Result, replayed with * `tryResumeSession` (never `fromSave`, which skips the legality check): 3 resume / 10 refuse, the * same ten at the same moves for the same rules 0.8.2 named. So this update RESTORES three games * 0.8.2 lost and un-refuses none of the ten. * * THE ENGINE DIFF DOES CHANGE PREDICATES, and each is the narrowing kind in principle: `check` now * refuses a switching intent on a tray outside the actor's district; the collision floor is judged * before the Day rolls over; the Department-draw reshuffle collects a different set; a held train is * released at the end of a Mainline phase. None of the thirteen saves met any of them — measured, * above — and a save that did would refuse at the move with its code and be left untouched, as * always. Events gained `index` (unjam) and `freedBy: number | null` (release); events are derived, * not stored. v0.8.4 is the HTTP layer and the browser and touches nothing on disk except HOW * `game.json` is written (unique temp names, one writer per path) — the format is unchanged, and a * torn file from before is now logged and skipped at boot rather than fatal. v0.8.5 is * documentation and dead code. v0.8.6 refuses `/api/save` to a Competitive seat until the game is * finished (Jesse's ruling) — a read-only route, nothing on disk. No migration. */ up: async ({ effects }) => {}, down: IMPOSSIBLE, }, })