0.7.7:0 — bundle Station Master v0.7.7

Submodule pinned to v0.7.7 (af68aac). current.ts bumped in place at
0.7.7:0 — no new version file, no migration: the change is to how the
built client is cached, not to what it does.

Every packaged build until now published ?v=nogit as its cache key (the
.s9pk Dockerfile copies the tree in without .git, so git rev-parse
fails), and serveStatic sent no Cache-Control at all — so 0.7.5's
solitaire setup screen and 0.7.6's fix to it were both installed here
correctly and neither ever reached a browser.

Verified on phoenix.local against what the server actually returns, not
just what was packed: build tag is 0.7.7-mtf7hyxc (not nogit) and the
module graph's inner imports carry the same tag; play.html is no-cache;
a ?v=-tagged module is immutable for a year; an untagged one is
no-cache. Migration 0.7.6:0 -> 0.7.7:0 ran empty and both games in
progress (WHISTLE-4086, COAL-7370) resumed with their same intent counts.

README.md records that a hard reload is NOT a sufficient check for this
class of bug — confirmed in the field on 0.7.6, where only a fresh
private window showed the new build.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AdG46Ja2PEDBkpqiDazMoX
This commit is contained in:
Jesse
2026-08-29 22:46:25 -04:00
co-authored by Claude Sonnet 5
parent 590ff5fa0b
commit 921db34b55
4 changed files with 74 additions and 46 deletions
+19 -6
View File
@@ -13,12 +13,25 @@ Station Master is a railroad operations board game with an authoritative multipl
solitaire needs no server and is not what this package is for. This package runs that server — solitaire needs no server and is not what this package is for. This package runs that server —
the browser client, the lobby, and the intent/SSE API — as a single StartOS service. the browser client, the lobby, and the intent/SSE API — as a single StartOS service.
**Bundled version: 0.7.6.** Fixes the solitaire door 0.7.5 introduced: a browser that had ever held a **Bundled version: 0.7.7.** Fixes the client caching that stopped the two releases before it from
multiplayer seat could not reach the new setup screen at all — a bare page load could not tell ever reaching a browser. `build-web.ts` stamps a build tag onto every module URL as a cache key, and
"clicked Play solitaire" apart from "reloaded mid multiplayer game", so the door lost to whatever its fallback when `git rev-parse` fails was the literal `nogit` — which is precisely the `.s9pk`
game or lobby that browser last touched. The door now marks its intent explicitly (`?solitaire`), the case, since the Dockerfile copies the working tree in without `.git`. So every packaged release
same fix `?lobby` already carries for the door on the other side. Client-side only; every game in published `./web/main.js?v=nogit`, byte-identical to the one before, and a returning browser refetched
progress carries over. nothing. `serveStatic` also sent no `Cache-Control` at all, so the pages that carry those tags were
themselves served from cache. The tag is now the package version plus the build timestamp, and a
request carrying `?v=` is `immutable` for a year while everything else is `no-cache`.
**Diagnosing "my fix did not ship".** A hard reload is NOT a sufficient check — confirmed in the
field on 0.7.6: the document refetches but ES module sub-imports keep their cached `?v=` URLs, so the
module graph stays stale. A fresh private window is the reliable test. Reading what the server
returns (`curl` inside the container) proves what was installed, never what a browser is running.
**0.7.6 fixed the solitaire door 0.7.5 introduced**: a browser that had ever held a multiplayer seat
could not reach the new setup screen at all — a bare page load could not tell "clicked Play
solitaire" apart from "reloaded mid multiplayer game", so the door lost to whatever game or lobby
that browser last touched. The door marks its intent explicitly now (`?solitaire`), the same fix
`?lobby` already carries for the door on the other side.
**0.7.5 was a client-side flow change**: a genuinely fresh visit to the solitaire page opens a setup **0.7.5 was a client-side flow change**: a genuinely fresh visit to the solitaire page opens a setup
screen and asks for the game's options before dealing, the same question the multiplayer lobby has screen and asks for the game's options before dealing, the same question the multiplayer lobby has
+7
View File
@@ -35,6 +35,13 @@ watches the table — there is nothing else to open.
Solitaire runs entirely in your own browser and needs nothing from the server, so it works here Solitaire runs entirely in your own browser and needs nothing from the server, so it works here
exactly as it does on the public Station Master site. exactly as it does on the public Station Master site.
**What changed in 0.7.7.** Updates to this service now actually reach your browser. Until now every
release published its files under the same cache name and the server never told browsers when to
look again, so returning players kept running whatever version their browser had already stored —
the 0.7.5 solitaire setup screen and its 0.7.6 fix were both installed correctly here and neither
was ever seen. If a past update seemed to do nothing, this is why, and opening the page in a private
window was the only way to see it. That should not be necessary again.
**What changed in 0.7.6.** The **Play solitaire** door on the front page now reliably reaches **What changed in 0.7.6.** The **Play solitaire** door on the front page now reliably reaches
solitaire. If this browser had ever taken a seat at a multiplayer table, the door used to lose to solitaire. If this browser had ever taken a seat at a multiplayer table, the door used to lose to
that game or lobby instead — a plain page load couldn't tell "I clicked Play solitaire" apart from that game or lobby instead — a plain page load couldn't tell "I clicked Play solitaire" apart from
+47 -39
View File
@@ -5,54 +5,62 @@ export const current = VersionInfo.of({
// (`versions.md`'s consistency checklist), so bumping the submodule pin and bumping this are one // (`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 // action. The downstream half moves only for packaging-only changes, and RESETS to `:0` when the
// upstream half moves. // upstream half moves.
version: '0.7.6:0', version: '0.7.7:0',
releaseNotes: { releaseNotes: {
en_US: en_US:
'Bundles Station Master v0.7.6 \u2014 fixes the solitaire door introduced in v0.7.5. A browser that ' + 'Bundles Station Master v0.7.7 \u2014 updates now actually reach your browser. Every packaged ' +
"had ever held a multiplayer seat could not reach solitaire's new setup screen at all: a bare " + 'release until now published its files under the same cache key, and the server never told ' +
'page load could not tell "clicked Play solitaire" apart from "reloaded mid multiplayer game", ' + 'browsers when to look again, so a returning player kept running whichever version their ' +
'so the door lost to whatever game or lobby this browser last touched. The door now marks its ' + 'browser had already stored: the v0.7.5 solitaire setup screen and the v0.7.6 fix to it were ' +
'intent explicitly, so it reaches solitaire every time. No rules changed, so games in progress ' + 'both installed correctly and neither was ever seen. Each build now carries its own key and ' +
'are unaffected.', "the pages are marked to revalidate, so this is the last update you'll have to open a private " +
'window to see. No rules changed; games in progress are unaffected.',
es_ES: es_ES:
'Incluye Station Master v0.7.6: corrige la puerta de solitario introducida en v0.7.5. Un ' + 'Incluye Station Master v0.7.7: las actualizaciones ahora llegan de verdad a tu navegador. Hasta ' +
'navegador que alguna vez ocup\u00f3 un asiento multijugador no pod\u00eda llegar en absoluto a la nueva ' + 'ahora, cada versi\u00f3n empaquetada publicaba sus archivos bajo la misma clave de cach\u00e9, y el ' +
'pantalla de configuraci\u00f3n del solitario: una carga de p\u00e1gina simple no distingu\u00eda "puls\u00e9 Jugar ' + 'servidor nunca indicaba a los navegadores cu\u00e1ndo volver a mirar, as\u00ed que quien regresaba segu\u00eda ' +
'en solitario" de "recargu\u00e9 en medio de una partida multijugador", as\u00ed que la puerta perd\u00eda ' + 'ejecutando la versi\u00f3n que su navegador ya ten\u00eda guardada: la pantalla de configuraci\u00f3n del ' +
'frente a cualquier partida o sala de espera que ese navegador hubiera tocado por \u00faltima vez. La ' + 'solitario de la v0.7.5 y su correcci\u00f3n en la v0.7.6 se instalaron correctamente y nunca se ' +
'puerta ahora marca su intenci\u00f3n expl\u00edcitamente, as\u00ed que llega al solitario siempre. Las reglas ' + 'vieron. Ahora cada compilaci\u00f3n lleva su propia clave y las p\u00e1ginas se marcan para revalidarse, ' +
'no han cambiado, as\u00ed que las partidas en curso no se ven afectadas.', 'as\u00ed que esta es la \u00faltima actualizaci\u00f3n que tendr\u00e1s que abrir en una ventana privada para ver. ' +
'Las reglas no han cambiado; las partidas en curso no se ven afectadas.',
de_DE: de_DE:
'Enth\u00e4lt Station Master v0.7.6 \u2014 behebt die in v0.7.5 eingef\u00fchrte Solo-T\u00fcr. Ein Browser, der ' + 'Enth\u00e4lt Station Master v0.7.7 \u2014 Aktualisierungen erreichen jetzt tats\u00e4chlich deinen Browser. ' +
'jemals einen Mehrspieler-Platz innehatte, konnte den neuen Solo-Einrichtungsbildschirm ' + 'Bisher ver\u00f6ffentlichte jede paketierte Version ihre Dateien unter demselben Cache-Schl\u00fcssel, ' +
'\u00fcberhaupt nicht erreichen: ein einfacher Seitenaufruf konnte "Solo spielen angeklickt" nicht ' + 'und der Server teilte Browsern nie mit, wann sie erneut nachsehen sollten. Wer zur\u00fcckkam, ' +
'von "mitten im Mehrspieler-Spiel neu geladen" unterscheiden, sodass die T\u00fcr gegen jedes Spiel ' + 'f\u00fchrte also weiter die Version aus, die sein Browser bereits gespeichert hatte: der ' +
'oder jede Lobby verlor, die dieser Browser zuletzt ber\u00fchrt hatte. Die T\u00fcr markiert ihre Absicht ' + 'Solo-Einrichtungsbildschirm aus v0.7.5 und dessen Korrektur in v0.7.6 wurden beide korrekt ' +
'jetzt ausdr\u00fccklich und erreicht damit immer den Solo-Modus. An den Regeln hat sich nichts ' + 'installiert und nie gesehen. Jeder Build tr\u00e4gt nun seinen eigenen Schl\u00fcssel, und die Seiten ' +
'ge\u00e4ndert, laufende Spiele sind nicht betroffen.', 'sind zur erneuten Pr\u00fcfung markiert \u2014 dies ist also die letzte Aktualisierung, f\u00fcr die du ein ' +
'privates Fenster \u00f6ffnen musst. Keine Regel\u00e4nderungen; laufende Spiele sind nicht betroffen.',
pl_PL: pl_PL:
'Zawiera Station Master v0.7.6 \u2014 naprawia drzwi do gry solo wprowadzone w v0.7.5. Przegl\u0105darka, ' + 'Zawiera Station Master v0.7.7 \u2014 aktualizacje naprawd\u0119 docieraj\u0105 teraz do twojej przegl\u0105darki. ' +
'kt\u00f3ra kiedykolwiek zajmowa\u0142a miejsce w grze wieloosobowej, w og\u00f3le nie mog\u0142a dotrze\u0107 do nowego ' + 'Do tej pory ka\u017cde wydanie publikowa\u0142o swoje pliki pod tym samym kluczem pami\u0119ci podr\u0119cznej, a ' +
'ekranu ustawie\u0144 gry solo: zwyk\u0142e wczytanie strony nie odr\u00f3\u017cnia\u0142o "klikni\u0119to Zagraj solo" od ' + 'serwer nigdy nie m\u00f3wi\u0142 przegl\u0105darkom, kiedy sprawdzi\u0107 ponownie, wi\u0119c powracaj\u0105cy gracz nadal ' +
'"prze\u0142adowano w trakcie gry wieloosobowej", wi\u0119c drzwi przegrywa\u0142y z dowoln\u0105 gr\u0105 lub ' + 'uruchamia\u0142 t\u0119 wersj\u0119, kt\u00f3r\u0105 jego przegl\u0105darka ju\u017c zapisa\u0142a: ekran ustawie\u0144 gry solo z v0.7.5 i ' +
'poczekalni\u0105, kt\u00f3rej ta przegl\u0105darka ostatnio dotkn\u0119\u0142a. Drzwi teraz jawnie oznaczaj\u0105 sw\u00f3j ' + 'jego poprawka z v0.7.6 zosta\u0142y poprawnie zainstalowane i nigdy nie zosta\u0142y zobaczone. Ka\u017cda ' +
'zamiar, wi\u0119c zawsze prowadz\u0105 do gry solo. Zasady si\u0119 nie zmieni\u0142y, wi\u0119c gry w toku nie s\u0105 ' + 'kompilacja ma teraz w\u0142asny klucz, a strony s\u0105 oznaczone do ponownej weryfikacji \u2014 to wi\u0119c ' +
'dotkni\u0119te.', 'ostatnia aktualizacja, kt\u00f3r\u0105 trzeba otworzy\u0107 w oknie prywatnym, \u017ceby j\u0105 zobaczy\u0107. Zasady si\u0119 ' +
'nie zmieni\u0142y; gry w toku nie s\u0105 dotkni\u0119te.',
fr_FR: fr_FR:
'Inclut Station Master v0.7.6 \u2014 corrige la porte solo introduite dans la v0.7.5. Un navigateur ' + 'Inclut Station Master v0.7.7 \u2014 les mises \u00e0 jour atteignent enfin r\u00e9ellement votre navigateur. ' +
'ayant d\u00e9j\u00e0 occup\u00e9 un si\u00e8ge en multijoueur ne pouvait pas du tout atteindre le nouvel \u00e9cran de ' + "Jusqu'ici, chaque version empaquet\u00e9e publiait ses fichiers sous la m\u00eame cl\u00e9 de cache, et le " +
'configuration du solo : un simple chargement de page ne distinguait pas \u00ab a cliqu\u00e9 sur Jouer en ' + 'serveur ne disait jamais aux navigateurs quand regarder \u00e0 nouveau : un joueur qui revenait ' +
'solo \u00bb de \u00ab a recharg\u00e9 en pleine partie multijoueur \u00bb, si bien que la porte perdait face \u00e0 la ' + "continuait donc d'ex\u00e9cuter la version que son navigateur avait d\u00e9j\u00e0 stock\u00e9e. L'\u00e9cran de " +
"derni\u00e8re partie ou salle d'attente que ce navigateur avait touch\u00e9e. La porte marque d\u00e9sormais " + 'configuration du solo de la v0.7.5 et son correctif en v0.7.6 ont tous deux \u00e9t\u00e9 install\u00e9s ' +
"son intention explicitement, elle atteint donc toujours le solo. Les r\u00e8gles n'ont pas chang\u00e9, " + 'correctement et ne furent jamais vus. Chaque build porte d\u00e9sormais sa propre cl\u00e9 et les pages ' +
'les parties en cours ne sont donc pas affect\u00e9es.', 'sont marqu\u00e9es \u00e0 revalider : voici donc la derni\u00e8re mise \u00e0 jour que vous devrez ouvrir dans une ' +
"fen\u00eatre priv\u00e9e pour la voir. Aucune r\u00e8gle n'a chang\u00e9 ; les parties en cours ne sont pas " +
'affect\u00e9es.',
}, },
migrations: { migrations: {
/** /**
* STILL NOTHING TO CARRY. v0.7.6 fixes a routing bug in the solitaire door that v0.7.5 * STILL NOTHING TO CARRY. v0.7.7 fixes how the built client is CACHED, not what it does: every
* introduced (a browser remembering a multiplayer seat could not reach the setup screen at * packaged build published the same cache key (`?v=nogit` — the `.s9pk` build has no `.git` for
* all) — client-side only, no rule the engine enforces and no save-format change. A game in * `git rev-parse`) and the server sent no `Cache-Control` at all, so two correct releases never
* progress, on the server or in a browser, resumes exactly as it would have on v0.7.5. * reached the browsers that asked for them. A build tag that varies and a revalidate header are
* both static-file concerns: no rule the engine enforces, no save-format change. A game in
* progress, on the server or in a browser, resumes exactly as it would have on v0.7.6.
*/ */
up: async ({ effects }) => {}, up: async ({ effects }) => {},
down: IMPOSSIBLE, down: IMPOSSIBLE,