Add The Frontier, a second setting, and a picker to choose between them
Cold Fork is a freight yard: 29 events across two stages, a four-day cycle, and its own relationship and skill vocabulary. src/engine/ was not touched. Adding it needed three changes outside the engine: one save slot per pack, app.js holding the current pack, and turn.js no longer naming the mailroom (DECISIONS #34). build.js now handles imports wrapped across lines. A conformance suite runs over every registered pack. DECISIONS #35 records three calls on open questions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RxdJFbLq1rBKsUeticGV1g
This commit is contained in:
co-authored by
Claude Opus 5
parent
4957c24e31
commit
7f20be83e8
@@ -421,3 +421,129 @@ Spans and pair-counts are now accumulated per run and pooled, so the figure is a
|
||||
proper weighted average of within-run gaps. It is worth stating the general
|
||||
form: any statistic over a log has to respect the run boundary, because a log is
|
||||
not one sequence.
|
||||
|
||||
## 31. A pack declares the numbers it must hit
|
||||
|
||||
Decision #29 added a test that asserts Corporate Ladder's economic baseline
|
||||
directly, because a tuning change made without re-deriving what it meant had
|
||||
trapped a real playtester. That test named the pack, named its stages and
|
||||
hard-coded its ranges, which meant a second pack got none of it — the guard
|
||||
that exists precisely because a number went unchecked would itself have gone
|
||||
uncopied.
|
||||
|
||||
So the numbers moved into the pack, as `tuning.baselinePerCycle`:
|
||||
|
||||
```js
|
||||
tuning: {
|
||||
baselinePerCycle: { hand: [0, 12], deputy: [10, 30] },
|
||||
},
|
||||
```
|
||||
|
||||
The conformance suite reads that and holds each pack to its own declared range.
|
||||
A new setting is checked the moment it is in the registry, and what it is
|
||||
checked against is its designer's stated intent rather than another setting's
|
||||
numbers. The pack is required to declare it: a missing `tuning` block fails,
|
||||
because the silent failure mode here is a pack that is never economically
|
||||
tested at all.
|
||||
|
||||
The figure is **derived, not simulated**. An upkeep entry worth `v` every `n`
|
||||
turns is worth `v * cycleLength / n` per cycle, exactly. Simulating it over a
|
||||
fixed window reads differently depending on where the last rent lands: Corporate
|
||||
Ladder measures −19 a fortnight over 280 turns and −42 over 560, and only the
|
||||
second is the steady state. The original test used the 280-turn window and was
|
||||
reading a number 23 dollars off the truth — inside its own tolerance, so it
|
||||
never said so.
|
||||
|
||||
## 32. A skill gate is never gated behind itself
|
||||
|
||||
Corporate Ladder gates four options on `skills.negotiation >= 15`, and several
|
||||
of its largest negotiation gains sit behind that same gate — `+4` on
|
||||
`marlene_confides.ask_how_to_leave`, `+3` on `trevor_favor.trade`. Its logs show
|
||||
players meeting the wall repeatedly through the first thirty turns.
|
||||
|
||||
Measured, the gate is not unreachable: a player steering directly for the skill
|
||||
is through it by turn 11. What the logs actually show is ordinary players not
|
||||
prioritising it and having no incidental route, which is a pacing failure rather
|
||||
than a reachability one — and the reachability test could never have caught it,
|
||||
because reachability is exactly what holds.
|
||||
|
||||
The Frontier is built to a rule instead, asserted in `test/frontier.test.js`:
|
||||
for every skill threshold in the pack, the growth available *without* passing
|
||||
that threshold must be able to reach it, from repeatable events — a single
|
||||
one-off award is not a route. A second test measures it, requiring a player who
|
||||
steers for a skill to be through its lowest gate inside twenty turns.
|
||||
|
||||
The rule is deliberately not in the conformance suite, because Corporate Ladder
|
||||
does not satisfy it and retuning that pack is a separate decision from adding
|
||||
this one. It is written down here so the choice is visible rather than implied.
|
||||
|
||||
## 33. One save slot per pack, and the picker is the front door
|
||||
|
||||
`theladder.save.v1` was a single global slot. The app guarded it by comparing
|
||||
the packId inside the save against the pack it was about to run, and started a
|
||||
new game when they differed — so with two settings, the first switch would have
|
||||
silently destroyed the other career.
|
||||
|
||||
Slots are now `theladder.save.v1.<packId>`, one per setting, and switching is
|
||||
non-destructive in both directions: each run is written to its own slot every
|
||||
turn, and coming back resumes rather than restarts. `theladder.pack.v1` holds
|
||||
the last setting played, so a returning player lands back where they were.
|
||||
|
||||
The pre-packs slot is still read once, for Corporate Ladder only, and only when
|
||||
it really holds a Corporate Ladder save. It is never written. Someone mid-career
|
||||
when this shipped finds their career.
|
||||
|
||||
A first-time player — nothing saved, nothing remembered — gets the setting
|
||||
picker rather than being dropped into whichever pack is first in the registry.
|
||||
A build shipping one pack skips it entirely.
|
||||
|
||||
## 34. What a second setting proved, and what it cost
|
||||
|
||||
The engine/content split was a claim until there were two settings. The
|
||||
Frontier is 29 events across two stages, a four-day cycle instead of a
|
||||
seven-day one, a different relationship vocabulary (`trusts_you`, `owes_you`,
|
||||
`wary_of_you`), a different skill pair, and money about fifteen times smaller
|
||||
that arrives from work rather than from a wage. `src/engine/` was not touched.
|
||||
|
||||
Three things outside the engine did have to change, and none of them was about
|
||||
the setting:
|
||||
|
||||
- **`src/io/save.js`** — the single save slot, above.
|
||||
- **`src/ui/app.js`** — it named one pack; it now holds the current one. The
|
||||
screens, the engine and the rest of the io layer still take the pack as an
|
||||
argument and are no less pack-agnostic than before.
|
||||
- **`src/ui/screens/turn.js:40`** — hardcoded `'The mailroom'` as the heading
|
||||
over the people panel. That was the interface naming a setting, against
|
||||
decision #14, and it had gone unnoticed while only one setting existed. It is
|
||||
now `pack.peopleHeading`.
|
||||
|
||||
`tools/build.js` needed one fix, unrelated to packs but exposed by them: its
|
||||
module scan used `.*?` where `transform` used a pattern that spans lines, so a
|
||||
destructured import wrapped across several lines was rewritten into a
|
||||
`__require` for a module the scan had never collected. The bundle built clean
|
||||
and failed only when run. Both now use `[\s\S]*?`.
|
||||
|
||||
The honest summary is that adding a setting required no engine change and three
|
||||
interface changes, two of which were latent defects that a second pack merely
|
||||
made visible.
|
||||
|
||||
## 35. Three open questions, closed by the designer
|
||||
|
||||
Recorded 2026-09-11, alongside 0.3.1.
|
||||
|
||||
**Corporate Ladder's negotiation gate stays.** A gate that takes deliberate work
|
||||
to get past is acceptable, and so is an option that needs groundwork first. #32
|
||||
remains the rule the Frontier is built to and tested against; it is a choice a
|
||||
pack may make, not a standard every pack must meet, and Corporate Ladder is not
|
||||
being retuned toward it.
|
||||
|
||||
**The feedback widget stays exactly as it is.** Zero ratings across three
|
||||
sessions says nothing about the widget: those playtests were rapid, and feedback
|
||||
came back directly rather than through it. It is not evidence either way until a
|
||||
playtest runs without that direct channel.
|
||||
|
||||
**Money has to matter late, through the cost of standing.** A careful player
|
||||
ends around $8,300 over 200 turns, and money stops being a constraint. The
|
||||
direction is that rising standing brings rising expenses — the car, the place to
|
||||
live, the clothes a position expects. Upkeep entries are already gated
|
||||
conditions over state, so this is content work, not an engine change.
|
||||
|
||||
Reference in New Issue
Block a user