Commit Graph
25 Commits
Author SHA1 Message Date
JesseandClaude Opus 5 4e1b255c0f 0.8.0.8:0 — bundle Station Master v0.8.0.8
The Salvage Yard names the card on top of it at last: it was face up all along and
had nothing to say, because a scheduled train was recorded there twice — once as a
card and once as a marker matching no card. Phase names along the top hold long
enough to read at high playback speeds, and playback goes to twenty times.

A RULES DECISION comes with it (Gitea#23): a regularly scheduled train that has
been PLAYED is already on the timetable, so a reshuffle leaves it out rather than
returning it to be run twice. One that was DISCARDED was never played and comes
back as before; an Extra always comes back, being a single run rather than a
standing slot.

This is the first of the 0.8.0.x releases to touch the engine, so the migration
note says why games in progress still resume: a save is a seed plus the moves
played, replayed through the current rules, and nothing here makes a once-legal
move illegal. check() is untouched and a draw is a draw whatever is on top. What
differs is the Yard's depth, which was double-counting, and what a reshuffle
returns — and no reshuffle occurred across eight games driven to 4000 moves.

Release notes updated in all five locales.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6cF1iYvJ1kNmzYBzu4QX6
2026-09-10 07:27:43 -04:00
JesseandClaude Opus 5 1abd5a8a9d 0.8.0.6:0 — bundle Station Master v0.8.0.6
The lit pile keeps pulsing for as long as that move is on screen, rather than
flashing once and settling where it was easy to miss — measured at just under seven
seconds lit at 10x, so it was never brief, only quiet.

Your own move is put away while the board is catching up: it is behind the game at
that moment, so anything offered would be a move against a position that has
already changed. Skip is always one click away. That change could have locked a
player out of their own game where requestAnimationFrame is missing and the queue
therefore never drains, so there are now two fallbacks and a test pinning that the
queue can always be emptied.

And playback can be slowed to twenty times, because ten was the top of the ladder
and was reported still a bit fast.

Release notes updated in all five locales. Still no migration —
git diff v0.8.0.2..v0.8.0.6 -- src/engine/ is empty. Games in progress resume.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6cF1iYvJ1kNmzYBzu4QX6
2026-09-10 05:51:28 -04:00
JesseandClaude Opus 5 a1f30ac727 0.8.0.5:0 — bundle Station Master v0.8.0.5
Somewhere to look while another player moves. The Home Office deck is drawn at
last — face down with its count, a number the Frame has carried since it existed
and nothing ever read — and whichever pile a move just touched lights up for as
long as that move is on screen: the deck when somebody draws from it, the
Department a card is taken from or discarded onto, the Salvage Yard when a played
card ends there.

What lights follows what is public, measured across four seeds rather than
reasoned about: a card drawn face down stays private and only the deck getting
shorter is shown, while a Department pile is face up so the card taken from it is
not a secret.

Release notes updated in all five locales. Still no migration —
git diff v0.8.0.2..v0.8.0.5 -- src/engine/ is empty; the highlight is a diff of two
frames the client already holds, so nothing joined the protocol either. Games in
progress resume.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6cF1iYvJ1kNmzYBzu4QX6
2026-09-10 04:49:16 -04:00
JesseandClaude Opus 5 cf148129d7 0.8.0.4:0 — bundle Station Master v0.8.0.4
Skip moved to the left of the waiting row, where the eye already is. The row now
says who you are waiting on and what they are doing, rather than only how far
behind you are — half the waiting is automatic phases, which narrate as "Mainline"
and answer nobody's question about who is acting. And the speed setting stretches
other players' moves without stretching the clock, so finishing your own turn no
longer leaves you waiting on the phases behind it.

Plus housekeeping: the test server's hostname is out of the tracked comments and
docs, both repositories being publicly clonable.

Release notes updated in all five locales. Still no migration —
git diff v0.8.0.2..v0.8.0.4 -- src/engine/ is empty. Games in progress resume.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6cF1iYvJ1kNmzYBzu4QX6
2026-09-09 21:46:11 -04:00
JesseandClaude Opus 5 d72ad24a17 0.8.0.2:0 — bundle Station Master v0.8.0.2
Playback speed is a control on the play screen now, next to the zoom. It was only
ever settable through the address bar, and arriving from the front page replaces
the query string — so it never took effect for anyone who reached the game the
ordinary way, and a whole session was played at 1x believing it was at 7x.

Also the end-of-Day summary reported "0 today" beside a collision total that
disagreed with it, because the Day had already ticked over by the time it was read.

Release notes updated in all five locales. Still no migration, and this time the
engine IS touched, so the note says why that is still nothing to carry: three added
lines for a counter no predicate reads, and a SavedGame holds { seed, config,
history } rather than a serialised GameState — resuming calls createGame and
replays, so a new field is filled on the way past. Games in progress resume.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6cF1iYvJ1kNmzYBzu4QX6
2026-09-09 20:33:45 -04:00
JesseandClaude Opus 5 0814b90507 0.8.0.1:0 — bundle Station Master v0.8.0.1
Pacing fixes from the first game played on v0.8.0: the last move in a run used to
vanish the instant it appeared, snapping the board back to your own district, and
bot turns went by far too fast to follow. Playback speed now works in both
directions, up to three times slower.

Release notes updated in all five locales. Still no migration, and v0.8.0.1 adds
nothing to that argument: the engine diff against v0.8.0 is empty — the changes
are how long a step is shown and when the queue calls itself idle, which touches
neither the rules nor anything on disk. Games in progress resume.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6cF1iYvJ1kNmzYBzu4QX6
2026-09-09 17:23:30 -04:00
JesseandClaude Opus 5 3145e8543c 0.8.0:0 — bundle Station Master v0.8.0
Watching the table: every accepted move, and every automatic phase that does
anything, is replayed on a player's own screen in the order it happened. The
Office Area follows whoever is acting, so another player's switching moves step
across their board instead of the position simply having changed.

Release notes in all five locales. No migration: the whole engine diff between
v0.7.9.8 and v0.8.0 is four GameEvent types gaining a `player` field, no
predicate changed its answer, and events are not persisted — so games in progress
on the server resume normally.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6cF1iYvJ1kNmzYBzu4QX6
2026-09-09 16:02:12 -04:00
JesseandClaude Opus 5 8f2f733a66 0.7.9.8:0 — bundle Station Master v0.7.9.8
Eight releases of fixes since v0.7.9, most of them multiplayer, which is
what this package is for. Submodule pin and `startos/versions/current.ts`
only — no Dockerfile, main.ts or manifest change, as an ordinary bump
should be.

Bumped in place rather than spun off: v0.7.9's migration body is empty,
which is `versions.md`'s common case, and git history keeps its release
notes.

FOUR-COMPONENT VERSION, CHECKED. Every example in `versions.md` has
three, so `0.7.9.8:0` was parsed with the SDK's own ExVer parser before
being written down: upstream `[0,7,9,8]`, which sorts above `[0,7,9]`, so
a box on 0.7.9:0 takes this as an update rather than refusing it as a
downgrade.

GAMES IN PROGRESS ON THE BOX RESUME, and that was established rather than
hoped for. v0.7.9 broke solitaire saves because it changed a RULE, and
the rule behind that is that a save is a seed plus the moves played,
replayed through the CURRENT rules — so the question for any bump is
whether something became illegal. `git diff v0.7.9..HEAD -- src/engine/`
is, in full: `isExpedited` exported and widened to a structural parameter
type, `isFreight` exported, one new read-only helper
(`freightRuleSpentHere`, which only the blocked panel asks), and
`check('draw.end')`'s inline hand-limit test replaced by a call to
`overHandLimit(state, player)` holding the identical expression. No
predicate changed its answer. The release notes say so in all five
locales rather than leaving it to be discovered.

UPDATING.md CORRECTED: it said a running server's saves "are stamped with
`engineVersion` and refuse to resume under a mismatched one". They are
stamped with it and it is reported, but `src/server/index.ts` decides by
attempting the replay and names the versions only inside the failure
message — its own comment says why, that "some version differs" was never
enough to act on. A doc that sends the next person to schedule downtime
for a bump that needs none costs about as much as the reverse. It now
carries the diff command to ask the question of the code instead.

What players get, briefly: two multiplayer information leaks closed (the
seed in the shared log, and a blind Home Office draw naming its card); a
history panel that came back EMPTY after a mid-game browser reload; the
extended-play vote no longer naming an arbitrary player when the vote is
open to everyone at once; a train held at your Limits by an Interlocking
no longer vanishing off the board until a track frees; trains queued for
a Crew Tray saying so with the free count; a Red Flag drawn on the map; a
spent Telegraph/Telephone/Radio struck through; the Campaign Train saying
whether its speeches are made; and two bugs from the last playtest — the
Express's one-freight-car-per-location rule explained where it refuses
you, and westbound trains drawn in the correct half of a card.

README.md and instructions.md updated in the same change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y5boPxP6JHRYMm8adXaF5R
2026-09-08 03:56:40 -04:00
JesseandClaude Opus 5 bd802cb512 0.7.9:0 — bundle Station Master v0.7.9
Submodule pinned to v0.7.9 (6058f6c), current.ts bumped in place, release
notes rewritten in all five locales, README.md and instructions.md
updated. No new version file and no migration: the outgoing 0.7.8:0's
`up` was empty, which is versions.md's common case.

GAMES IN PROGRESS DO NOT CARRY OVER, and both docs lead with it.

v0.7.9 changes a rule. advance.ts gated §3.4's collision check on
`mode === 'competitive' || mode === 'coop'`, while SOLO_CONFIG carried
both limits (3 a Day, 5 total) and the setup screen offered them as live
settings with "the game ends in a loss" printed beside them. A solitaire
player could set a limit of 1 and crash all game. Jesse's ruling was that
the settings do what they say, so the gate went rather than the controls.

A solitaire save is { seed, history, rules? } where `rules` is the house
rules only — the collision caps are NOT in the save, so a restore takes
today's defaults. A 0.7.8 save therefore replays under live caps and
stops at the move that crossed one.

MEASURED WITH A CONTROL, because two earlier attempts at measuring it
were wrong. With the caps disabled, 1200 of 1200 generated solitaire
saves replay every intent — that is the control. Of those, 74 games
crossed the 5-collision total and 70 of the 74 truncate once the caps
are enforced; one replays 141 of its 532 intents and stops. So it is
"will break for anyone who crashed", not "may break". The earlier run
that reported no breakage had in fact been failing every replay at
intent 2 on an unrelated house-rules mismatch, and said nothing at all —
the control is the only reason that was caught.

It fails safe: the save is left untouched and the game declines to open
rather than loading a position the rules could not have produced, so a
player who cares can put 0.7.8 back and finish. Nothing in this package
can act on it either way — a solitaire save lives in the player's own
browser, not on this server. MULTIPLAYER GAMES ARE UNAFFECTED:
competitive and coop have been inside that gate all along.

TWO THINGS FIXED IN PASSING, both pre-existing and both in the way of
the release check. UPDATING.md was not prettier-formatted. And there was
no .prettierignore, so `prettier --check .` walked the bundled
application — a separate repo with its own style, which this package does
not own and does not commit — and reported 171 warnings it could do
nothing about. That check now passes clean, which is the point of having
it in the release sequence at all.

Verified: npm run check clean, prettier clean, make x86 packs as
v0.7.9:0. NOT yet installed on a box and NOT played.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YTaNBL1jVxNqgFdjHkHoo3
2026-08-30 20:41:27 -04:00
JesseandClaude Sonnet 5 3b7b334968 0.7.8:0 — bundle Station Master v0.7.8
Submodule pinned to v0.7.8 (193800a). current.ts bumped in place at
0.7.8:0 — no new version file, no migration: the change is which screen
a solitaire visit lands on, plus the splash footer's wording. Both are
client-side, and a solitaire save lives in the browser rather than on
this server, so nothing here is involved either way.

0.7.5 skipped the setup screen whenever the browser held a save, and any
browser that has ever played solitaire holds one — so the door could
never reach the screen again. The door outranks a save now; a bare
reload still resumes; #ss-resume keeps the game in progress reachable
since Deal clears it.

Verified on phoenix.local against what the server returns: the door
carries ?solitaire, play.html carries #ss-resume and #ss-saved-note, the
footer names both ways to play, and the build tag is 0.7.8-mtfaojtv.
Migration 0.7.7:0 -> 0.7.8:0 ran empty and both multiplayer games in
progress (WHISTLE-4086, COAL-7370) resumed with their same intent counts.

The behaviour itself is covered by four new tests upstream that
reproduce the reported state (door + existing save) rather than by
reading served output — which is what the two previous attempts did, and
why they were reported as verified while still being wrong.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AdG46Ja2PEDBkpqiDazMoX
2026-08-30 00:16:07 -04:00
JesseandClaude Sonnet 5 921db34b55 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
2026-08-29 22:46:25 -04:00
JesseandClaude Sonnet 5 590ff5fa0b 0.7.6:0 — bundle Station Master v0.7.6
Submodule pinned to v0.7.6 (b4f09f0). current.ts bumped in place at
0.7.6:0 — no new version file, no migration: fixes a client-side
routing bug in the solitaire door that v0.7.5 introduced (a browser
that had ever held a multiplayer seat could not reach the new setup
screen at all), so every game in progress carries over without
exception.

Verified: npm run check clean, prettier clean, make x86 packs as
v0.7.6:0, installed on phoenix.local — logs show the empty migration
running and both games in progress (WHISTLE-4086, COAL-7370) resuming
with their same intent counts, and the served index.html confirmed to
link the solitaire door to ./play.html?solitaire.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AdG46Ja2PEDBkpqiDazMoX
2026-08-29 21:52:56 -04:00
JesseandClaude Sonnet 5 3fc56de203 0.7.5:0 — bundle Station Master v0.7.5
Submodule pinned to v0.7.5 (3e96149). current.ts bumped in place at
0.7.5:0 — no new version file, no migration: the change is a client-side
flow change (solitaire asks for its options before dealing, the same
question the multiplayer lobby already asks) that touches no rule the
server enforces and no save format, so every game in progress carries
over without exception.

Verified: npm run check clean, prettier clean, make x86 packs as
v0.7.5:0, installed on phoenix.local — logs show the empty migration
running and both games in progress (WHISTLE-4086, COAL-7370) resuming
with their same intent counts, and the served play.html confirmed to
contain the new #solitairesetup screen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AdG46Ja2PEDBkpqiDazMoX
2026-08-29 19:27:35 -04:00
JesseandClaude Opus 5 085b88bb38 0.7.4:0 — bundle Station Master v0.7.4
Submodule pinned to v0.7.4 (19a6a47), `current.ts` at `0.7.4:0`, release notes
rewritten in all five locales, README.md and instructions.md updated. No new version
file and no migration: the outgoing 0.7.3:0's `up` is empty, `versions.md`'s common
case, so `current.ts` bumps in place.

Three rules corrections, all places where the code and the cards disagreed. The Yard
Office is offered rather than imposed, must be reachable in one move, and cars on the
lead collide — none of which it checked. Circus, Campaign and Military trains run
loaded where the yard can supply it, and the per-stop point is earned once per Office
Area by a fully loaded train. Red Flags becomes a directional flag on your own Limits,
spent on the train it stops, playable at the moment the engine sees a certain
collision.

GAMES IN PROGRESS MAY NOT SURVIVE THIS ONE, and the notes lead with it in every locale
— the opposite of 0.7.3 and for a different reason from 0.7.2. Nothing about the save
format changed; what changed is what the rules accept. The Red Flags intent changed
shape, a make-up that was legal may now be refused, and a Yard Office arrival asks a
question no older history has an answer for. It fails safe: the server declines a save
the rules reject, names the move, and leaves the file untouched. A game that never
meets one of the three carries on, which is why this is "may not" rather than 0.7.2's
"will not".

README.md's diagnosing note is extended: the Mainline Phase now stops to ask three
different questions of three different players, and a game sitting on one is waiting
on a person rather than stuck — it shows as active with nobody to wait for, because an
interruption is not a turn.

Verified: `npm run check` clean, prettier clean, `make x86` packs as v0.7.4:0.
NOT verified: not yet installed on a box, and none of the three rules has been played
by a human — upstream TODO.md #35 still stands for extended play too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAgJSmeV8zrMh55Mj85ESb
2026-08-29 08:20:30 -04:00
JesseandClaude Opus 5 74aea24d74 0.7.3:0 — bundle Station Master v0.7.3
Submodule pinned to v0.7.3 (45580d8), `current.ts` at `0.7.3:0`, release notes
rewritten in all five locales, README.md and instructions.md updated. No new
version file and no migration: the outgoing 0.7.2:0's `up` is empty, which is
`versions.md`'s common case, so `current.ts` bumps in place and `index.ts` is
untouched.

Two features, both about the end of a game. A game no longer stops dead when the
timetable runs out — it enters a fourth state, `awaitingExtension`, and asks the
table whether to play one more Day, unanimously in multiplayer with one refusal
decisive. The official result is frozen at the original `config.days` and never
rewritten, so playing on is explicitly an exhibition, and a §3.4 collision breach
is neither extendable nor able to overwrite a recorded result. And the end of a
game renders a full results screen in place of the raw `outcome.reason` enum the
page used to print.

GAMES IN PROGRESS SURVIVE THIS ONE, which is the opposite of the last release and
is why the notes lead with it in every locale. No card data changed and the engine
changes are additive, so every intent in a 0.7.2 save is still legal: the save
replays intact and the game simply pauses on the new question at the end. Upstream
proves it rather than asserting it — the suite replays the three recorded games in
`public/replays`, all made under an older ruleset, and asserts every intent still
applies. Anyone updating from 0.7.1 or earlier is still in the old boat, and both
docs say so.

README.md gains a short diagnosing note, since `awaitingExtension` is a state an
administrator can meet and misread: such a game is persisted and listed as
`active`, resumes on restart like any live game, and shows nobody to wait for —
because a vote is not a turn.

Verified: `npm run check` clean, prettier clean, `make x86` packs as v0.7.3:0.
NOT verified: not installed on a box and not played. Extended play has never been
exercised against a running service — upstream `TODO.md` #35.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAgJSmeV8zrMh55Mj85ESb
2026-08-29 05:07:03 -04:00
Jesse 1bfea8d49f 0.7.2:0 — bundle Station Master v0.7.2
Two engine bugs and two rules corrections, plus the Division map redrawn. The submodule pin moves to
v0.7.2 and the version follows it; the downstream digit resets to :0.

- Backing into a cut through a curve's 45° leg no longer couples it back to front, and a crew pulling
  out through one takes its own cut with it instead of leaving it standing.
- The card deck is the published counts exactly, which halves the track and drops two long-standing
  density multipliers — 206 cards to 121.
- Crossing a Mainline card counts the regions printed on it rather than the printed speed, which was
  only ever scenery. Most trains cross faster, slow trains much faster, and Fast/Slow now matters on
  the Hilly card alone.
- The Division map is one row read left to right, so east is always to the right.

GAMES IN PROGRESS DO NOT SURVIVE THIS UPDATE, and this is the first release of which that is true. A
save is a seed plus the moves played, and the deck changing size means a card id recorded under 0.7.1
refers to a different card or to none — so a save stops replaying at its first card.play. There is no
migration because there is no data to move: those moves were made against a deck that no longer
exists, and re-dealing would invent a game nobody played.

It fails safe. The server refuses to resume a save the rules reject, logs which move it stopped at,
and leaves the file untouched, so an operator can put 0.7.1 back on to finish a game that matters.
The release notes lead with this, in all five locales.

Packed and verified as v0.7.2:0 on x86_64.
2026-08-26 15:32:26 -04:00
JesseandClaude Opus 5 ea8e9dbcb8 0.7.1:0 — bundle Station Master v0.7.1
Four playtest fixes off the Gitea tracker. The submodule pin moves to v0.7.1 and the version follows
it; the downstream digit resets to :0, since v0.7.0 only ended on :3 because three test packs were
played before it was released.

- An empties-only train may couple a caboose again — a caboose carries the crew, not freight.
- The end of a Day is a modal carrying the standings, the Days left and the combined target, instead
  of passing between one click and the next inside the automatic phases.
- A Timetabled train may be discarded to a Department pile for a rival to pick up, governed by a new
  discardTimetabled setting that appears in both the New Game dialog and the lobby, on by default. An
  Extra never may.
- A blocked passenger platform now gives its reason, including the coach shortage and what ends it.

No new version file and no migration: current.ts's migrations.up is empty and nothing is carried from
0.7.0. GAMES IN PROGRESS SURVIVE THIS UPDATE — all three engine-visible changes only widen what is
legal, so every intent a 0.7.0 game recorded still replays, and the server resumes a save by replaying
it rather than by comparing version strings (src/server/index.ts in the game repo).

README.md and instructions.md both named 0.7.0 and carry the three user-visible changes now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FLnYR4XtXQNamYJXGYT8oC
2026-08-25 10:58:14 -04:00
JesseandClaude Opus 5 d26d44d9e2 0.7.0:3 — bundle Station Master v0.7.0
The submodule pin is the version; nothing else about the package changed. Four game types in the
lobby, the whole rule set readable before taking a seat, leaving and rejoining a game, and sound in
a multiplayer game for the first time — all upstream, all through the same server this package has
always run.

- startos/versions/current.ts — 0.7.0:3, with release notes in all five languages.
- instructions.md — how a game is set up now, and what Leave / Forget mean for a seat.
- README.md — the bundled version, and a limitation spelled out: a player's identity lives in their
  browser, so a lost token cannot be recovered from this package.
- UPDATING.md — how to pack a test build from work that is not pushed yet, which is how v0.7.0 was
  played before release, and why the downstream digit moves for each one.

The downstream digit is :3 rather than :0 because three test packs were installed on the box while
v0.7.0 was being played; publishing :0 now would be a downgrade it would refuse.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016JczK5i33ZNSf2PtzZqdhS
2026-08-23 05:50:38 -04:00
Jesse e84c640067 Bundle Station Master v0.6.0: updates stop destroying games in progress
Pin moves to v0.6.0, package version to 0.6.0:0.

README and instructions.md both promised the opposite of what now happens
and had to be rewritten: an update keeps games in progress unless the rules
actually changed, and a game that genuinely cannot replay is named in the
log rather than described as a version mismatch.

Verified on phoenix.local by doing the thing the release is about. A game
(MAIL-4571) was created on the installed 0.5.6:0, then this build was
installed over it. All four saved games on the volume came back:

  Resuming 4 saved game(s)…
  Resumed HOPPER-4607  — 37 intents replayed.   (written under 0.5.2)
  Resumed TRESTLE-5109 —  5 intents replayed.   (written under 0.5.3)
  Resumed TENDER-8092  — 38 intents replayed.   (written under 0.5.5)
  Resumed MAIL-4571    —  0 intents replayed.   (written under 0.5.6)

The first three had been refused on every boot since the release that
stranded them. They were never damaged, only unreachable, and replaying
them is all it took to get them back — which is the clearest evidence that
the version comparison was answering the wrong question. All four now show
in Games in Progress with the correct Day, Stage and waiting-on player.
2026-08-21 21:56:58 -04:00
Jesse 5bd06af932 Bundle Station Master v0.5.6, and count seats from 1 in Games in Progress
Pin moves to v0.5.6, package version to 0.5.6:0.

One packaging change of its own: Games in Progress reported "seat 0" while
the game's own screens now say "Seat 1", so an administrator and a player
would have been describing different chairs. Same +1, same reason.

Verified on phoenix.local as an update from 0.5.5:0. The served client
carries all three of the release's fixes: the lobby's seat numbers go
through seatLabel, .bs-name.bs-turn has lost the font-weight that made the
current player's name unreadable at 11px, and the west-to-east seating line
is gated to Day 1 Stage 1.
2026-08-21 21:01:24 -04:00
Jesse dbda1a7a02 Bundle Station Master v0.5.4: the Division map names its districts
Pin moves to v0.5.4, package version to 0.5.4:0. No packaging changes —
everything in this release is in the game, which is what the submodule
split is for.

Verified on phoenix.local as an update from 0.5.3:0: migrates, starts, and
the served client carries the new lobby and map — the generic
button:disabled rule, the copy button and game-code block, the seating
chain slot, the D12 explanation, and board-svg's owner/turn/(you) marks.
The old and incorrect "in the order everyone joined" claim is gone from the
served page.

The update refused to resume both saved games, as designed: HOPPER-4607
was written under 0.5.2 and TRESTLE-5109 under 0.5.3. Both files are left
untouched on disk.
2026-08-21 17:16:47 -04:00
Jesse c95620d566 Bundle Station Master v0.5.3: games in progress, and a way to end one
Pin moves to v0.5.3, package version to 0.5.3:0.

The health check no longer just probes a port. It fetches the server's own
/api/health and reports what it says — "Multiplayer server is ready — 3
games in progress", with ", 1 waiting to start" appended only when a lobby
exists and "no games in progress" on an idle server. A server that doesn't
answer is reported as STARTING, never failed: the server replays its saved
games before binding its port, so a boot legitimately looks like nothing is
listening, and calling that a failure makes an ordinary restart look like a
crash.

Two new actions, both only-running. Games in Progress is read-only and
lists every game and lobby with players, Day/Stage/phase, who it waits on,
when it started and when it last moved. Manage Game picks one from a
dropdown built live from the server and either exports it or ends it —
ending being the only way a game finishes other than being played out,
since an abandoned game otherwise stays active and is resumed on every
restart forever. Ending always returns the deleted game's save, so nothing
is destroyed without being handed back first.

They are only-running because none of it is readable from disk: a save is a
seed plus a list of moves, so whose-turn-it-is exists only after a replay
through the engine, which lives in the game repo rather than here. The
running server has already done that work and is asked for the answer.
startos/serverApi.ts is the single place that asks.

store.json gained adminSecret (32 chars) beside joinSecret, seeded on
install and backfilled on update for a volume written before the field
existed. It is deliberately NOT the join secret: every player holds that
one, so gating a delete with it would let anyone at the table destroy
anyone else's game. init/generateJoinSecret.ts is renamed generateSecrets.ts
now that it mints both.

Also brought getJoinSecret's result strings under i18n(). They shipped as
plain strings two commits ago, which actions.md is explicit about — every
user-facing string including result titles, messages and thrown errors.
The new actions follow it, so the old one shouldn't be the odd one out.

Verified on phoenix.local, installed as an UPDATE from 0.5.2:0 rather than
a fresh install, which exercised three things at once: the boot log line
("Resuming 1 saved game(s)…"), the engine-version refusal firing for real
on a game recorded under 0.5.2, and the adminSecret backfill (store.json
came out with both secrets, 24 and 32 chars). Then, from inside the
container: the package-generated admin secret authenticating against
/api/games (200) while a wrong one is refused (403), health counts tracking
0 -> lobby 1 -> active 1 through a create/bot/start, and every field the
actions render present and correct on the listing.

NOT verified: the actions' own forms and result rendering. `start-cli
package action run` fails with a client-side deserialization error on every
action on this box — including the already-shipped get-join-secret and
actual-budget's equivalent — so it is a start-cli problem, not this
package's. The data path underneath them is verified above; the SDK
form/result rendering needs the web UI.

A test game (TRESTLE-3221, Alice + a bot) is left running on the box so
there is something for Games in Progress to show.
2026-08-21 16:00:05 -04:00
Jesse ea012f1329 Bundle Station Master v0.5.2, and fix the package version scheme
Submodule pin moves to v0.5.2, which carries the working splash-page
multiplayer door and the GET /api/health probe behind it.

The interface goes back to serving `/` — the splash, which is the front
door to all three ways to play. It pointed at /play.html only because the
splash's multiplayer door was hardcoded to "Coming soon", making the front
page a dead end on a real server; v0.5.2 removes the reason for the
workaround. The splash now probes /api/health on load, so served from here
it opens the lobby and served as a plain static site it says plainly that
it needs a server.

The package version was wrong and is corrected: 1.0.0:0 -> 0.5.2:0. That
1.0.0 was the scaffold's placeholder, left in by mistake — versions.md's
consistency checklist requires the upstream half to match the bundled tag,
which is what makes "bump the pin, bump the version" one mechanical action
rather than a judgement call. No historical version file is needed: the
placeholder carried no migration and was never published anywhere.

The correction is a downgrade in ExVer terms, so StartOS refused to install
over the existing copy ("uninit target range `!` is unsatisfiable"). The
installed package was therefore removed and reinstalled — checked first
that the volume held only an auto-generated join secret and no games. Two
consequences, both one-time and both recorded in UPDATING.md: the join
secret is regenerated, and the host's public domain binding was dropped
with the uninstall and has to be re-added.

Verified on phoenix.local: builds as v0.5.2:0, installs, starts, and serves
/api/health 200 with the right body; the splash carries all three doors
including a live ./play.html?lobby, no "Coming soon" anywhere, all three
ids the probe addresses present, and #lobby rendering on play.html?lobby.
2026-08-21 11:13:40 -04:00
Jesse 278bbcf51c Real icon, and open straight into the game instead of the splash
icon.png replaces the scaffold placeholder — resized to 512x512 and
palette-quantized (1.9MB source -> 52.9KB) to stay close to the packaging
guide's 40 KiB guidance with no visible quality loss at icon size. Verified
station-master_x86_64.s9pk builds clean with a single icon.* file (the
packer errors on more than one) and `s9pk inspect` confirms icon.png is
packed correctly.

The `ui` interface now opens at `/play.html` instead of `/`. Root serves
index.html, the game's marketing splash — identical to the public static
solitaire site, and with no lobby or Multiplayer button on it; both live on
play.html (src/web/main.ts's `start()`, reached from index.html's door
link). That page is right for a stranger landing on the public site, wrong
for someone who just installed a dedicated multiplayer server and found
what looked like the same solitaire page with no way to host or join a
game. Verified: fetching /play.html from inside the container and over the
service's public address both return the page with the Multiplayer button
present.

Reinstalled on phoenix.local after each change.
2026-08-21 09:09:04 -04:00
Jesse de28b30eea Initial StartOS package for Station Master (v0.5.1)
Built from source via a git submodule pinned to a tag, not a published image
— the Dockerfile, main.ts, and manifest should never need to change for an
ordinary version bump, only the submodule pin (see UPDATING.md). One volume,
one interface serving the browser client + lobby/intent API + SSE stream
from a single origin, no dependencies. The server-wide join secret (D14) is
seeded on install, exposed via the Get Join Secret action, and blocks start
behind a critical task until retrieved — the same first-set/rotation pattern
as actual-budget-startos's admin password.

Verified on phoenix.local: installs, the critical task correctly blocks an
ordinary start, force-starting confirms the daemon binds its port and serves
the client, and store.json correctly holds the install-seeded join secret.
Not verified: the Get Join Secret action's execution end-to-end — start-cli's
`package action run` fails with a client-side deserialization error that
reproduces identically against actual-budget's already-shipped equivalent
action, so this looks like a start-cli issue rather than a defect here.

icon.svg is still the scaffold's hello-world placeholder — no real Station
Master icon exists yet to ship in its place.
2026-08-21 08:44:07 -04:00