v0.8.5 — housekeeping from the audit, and the playtest line retired

The third release from the audit; nothing a player sees changes. CHANGELOG has the detail.

The 0.4.9 playtest line is no longer maintained (Jesse, 2026-09-29): the deploy rule that
existed for it is gone and #85 is moot. The table test (#39 #35 #42a #40) is closed — every
line of the checklist was met at a table. #46 is done and cannot regrow: the 36 unused
declarations are removed and `noUnusedLocals`/`noUnusedParameters` are on; two of them were
dead bot functions from rejected candidates the round said it had deleted. The documents no
longer teach `trainCapSlack` (a knob that throws), point at `as-built.md` (deleted in 0.8.2),
model `officeType` (the engine says `tier`) or describe `collisionOccurred` (never emitted);
the README's account of bot flags now matches the bot's. Five playtest saves committed in
`docs/` against the repository's own rule are in the ignored `playtests/`.

What the audit found and did not fix is written down as TODO #112-#117, each with its reason.
#112 is `docs/plans/structure.md`, the proposal for `http.ts`, `main.ts` and `check`. #117 —
`/api/save` hands a seat the seed mid-game — waits on a conversation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FrCWubm9GAftYCm2hWdKwK
This commit is contained in:
Jesse.Markowitz
2026-09-29 17:02:33 -04:00
co-authored by Claude Fable 5.1
parent e47cd3d400
commit 04ca74c365
47 changed files with 370 additions and 4826 deletions
+3 -1
View File
@@ -60,7 +60,9 @@ is what makes the whole system testable without a server.
- **Where:** Engine · **When:** loaded once at startup, immutable thereafter
- **Does:** the 52-card deck composition, freight and passenger facility profiles, Modifier effects,
train consist specs, track geometries. Source: [`../rules/card-reference.md`](../rules/card-reference.md)
train consist specs, track geometries. Source: `docs/Deck cards5.xlsx` and the PDFs, transcribed
into `content.ts`; the generated tables in [`../home-deck.md`](../home-deck.md) and
[`../mainline-deck.md`](../mainline-deck.md) are what it deals today
- **MVP: S** · **Final: S** · ~200 → ~350 LOC
- **Size driver:** it is data, not logic. Grows only if card variants are added.
- **Note:** every number here is provisional and will be retuned repeatedly. Keep it as data files,
+12 -10
View File
@@ -131,24 +131,24 @@ Office; everything else is Secondary Track.
```
OfficeArea
owner : PlayerIndex
officeType : whistlePost | depot | station | terminal
tier : whistlePost | depot | station | terminal -- `OfficeTier`; `officeType` in early drafts
grid : Map<GridCoord, TrackCard> -- sparse; cards are placed during play
officeCoord : GridCoord -- where the Office card sits
runningRow : integer -- the grid row that is the Running Track
limitsWest : GridCoord -- moves outward as the Running Track grows
limitsEast : GridCoord
adOccupancy : CrewTrayId[] -- length ≤ adTrackCount(officeType)
adOccupancy : CrewTrayId[] -- length ≤ adTrackCount(tier)
```
```
adTrackCount: whistlePost 1 | depot 2 | station 3 | terminal 4 -- §11.1
isControlPoint = officeType != whistlePost
isPassengerFacility = officeType != whistlePost
isControlPoint = tier != whistlePost
isPassengerFacility = tier != whistlePost
```
**Office cards are geometrically interchangeable** (§11.3). All four tiers carry the same track
footprint — a through track plus a plain junction stub above and below — and differ *only* in the
three properties above. An upgrade therefore changes `officeType` and nothing else; it must never
three properties above. An upgrade therefore changes `tier` and nothing else; it must never
touch `grid`, `connections`, or anything attached to the Office card. The junction stubs carry no
directional restriction: §A.1's turnout rule governs drawn turnout cards only.
@@ -187,7 +187,7 @@ These are the ones that will be got wrong if they are not written down explicitl
9. **An Office upgrade must preserve every connection** (§11.3). All four tiers share identical
geometry precisely so the upgrade is a property change, not a card swap. An implementation that
models the upgrade as "remove old card, place new card" will silently orphan any Secondary Track
hanging off the Office — mutate `officeType` in place instead.
hanging off the Office — mutate `tier` in place instead.
10. **The Office card's stubs are not turnouts.** Do not route them through the §A.1 directional
logic; a train may pass between the Running Track and either Secondary row freely.
@@ -258,8 +258,9 @@ Facility
modifiers : ModifierRef[] -- adjacent cards raising capacity, track length or workers
```
All of these are populated from [`../rules/card-reference.md`](../rules/card-reference.md), which is
the authoritative per-card catalogue.
All of these are populated from `content.ts`, transcribed from the catalogue spreadsheets; the
generated tables in [`../home-deck.md`](../home-deck.md) and [`../mainline-deck.md`](../mainline-deck.md)
are the per-card reference (`card-reference.md` is the superseded placeholder).
**`industryTrack` is where cars are spotted for loading and unloading**, and it is the field that
stops being Operational Rail while `menAtWork` holds any load (constraint 4 below). Its `length` is
@@ -275,8 +276,9 @@ the car (§9.3). A Porter earns a point in one action. This asymmetry is deliber
per Stage, and stocking a green box or clearing a red one consumes the whole of it (§6.3). That
one-action-per-Stage budget is what actually limits Revenue — roughly one point per action, ceiling
12 per Day. Worker counts mostly determine how often a facility idles. See
[`../rules/card-reference.md`](../rules/card-reference.md#7-economy-summary) for the full model; it is
what §3's targets are calibrated against.
[`../rules/card-reference.md`](../rules/card-reference.md#7-economy-summary) for the model as it was
first worked out (that document is otherwise superseded); the figures §3's targets were calibrated
against are the measurements in `TODO.md`'s Play balance section.
---
+2 -1
View File
@@ -96,7 +96,8 @@ or a replay viewer has to reconstruct the whole board to draw one frame.
Two consequences worth knowing before adding an event:
- **`collisionOccurred` carries `faultPlayer` explicitly** rather than leaving clients to derive it.
- **`trainsDestroyed` carries the player at fault (`player`) explicitly** rather than leaving clients to
derive it (the event was called `collisionOccurred` in this design; the engine emits `trainsDestroyed`).
Fault depends on *where* the wreck happened — Superintendent for a Mainline card, the local player
between their Limits (§10) — and getting it wrong misattributes a −5 and, in Competitive, feeds a
floor that ends the game.