feat(#52): dynamic scenario generation (v0.31.0) #53

Merged
kamilkrzywda merged 5 commits from feat/52-dynamic-scenarios into master 2026-10-15 12:29:49 +02:00
Owner

Add the additive "Create scenario" path (v0.31.0):

  • 3 light era templates (ww2 1939 / present 2026 / near-future 2035) in presets/templates/
  • Two plain-chat() LLM steps: generate (draft WorldDraft + advisor checkpoint list) and finalize (constrained to the approved checkpoints) → saved playable GameState at #/game/
  • shared/scenario.ts: dependency-free contract (WorldDraft, validateWorldDraft, relationFromScore, worldDraftToGameState, message shapes)
  • server/scenario.ts (defensive parse + nudge-retry), server/stubScenario.ts (deterministic scenario stub; the turn stub server/stub.ts is 100% unchanged)
  • Routes: GET /api/scenarios/templates, POST /api/scenarios/draft, POST /api/scenarios/commit; dynamic-game jump preset synthesis in /jump
  • Client: ScenarioForm + CheckpointReview + PresetPicker "Create scenario" entry + App home→form→review→navigate flow
  • New e2e/scenario.spec.ts (2 deterministic tests under LLM_STUB=1); e2e/sim.spec.ts unchanged
  • Version bump 0.31.0 + changelog

Local gates: typecheck, lint, 307 unit tests (14 files), build all green.

Fixes #52

Add the additive "Create scenario" path (v0.31.0): - 3 light era templates (ww2 1939 / present 2026 / near-future 2035) in presets/templates/ - Two plain-chat() LLM steps: generate (draft WorldDraft + advisor checkpoint list) and finalize (constrained to the approved checkpoints) → saved playable GameState at #/game/<id> - shared/scenario.ts: dependency-free contract (WorldDraft, validateWorldDraft, relationFromScore, worldDraftToGameState, message shapes) - server/scenario.ts (defensive parse + nudge-retry), server/stubScenario.ts (deterministic scenario stub; the turn stub server/stub.ts is 100% unchanged) - Routes: GET /api/scenarios/templates, POST /api/scenarios/draft, POST /api/scenarios/commit; dynamic-game jump preset synthesis in /jump - Client: ScenarioForm + CheckpointReview + PresetPicker "Create scenario" entry + App home→form→review→navigate flow - New e2e/scenario.spec.ts (2 deterministic tests under LLM_STUB=1); e2e/sim.spec.ts unchanged - Version bump 0.31.0 + changelog Local gates: typecheck, lint, 307 unit tests (14 files), build all green. Fixes #52
kamilkrzywda added 5 commits 2026-10-15 12:27:54 +02:00
- presets/templates/: 3 light era templates (ww2/present/near-future) + index.json
  manifest (id, name, year, eraDescription, suggestedPowers, tensions).
- server/templates.ts: createTemplateStore (mirror of createPresetStore).
- shared/scenario.ts: dependency-free contract for the dynamic scenario path —
  SuggestedPower / EraTemplate / TemplateMeta / WorldNation / ScenarioEvent /
  WorldDraft / Checkpoint types, relationFromScore, validateWorldDraft (pure,
  defensive, never throws), worldDraftToGameState (region-key territory via the
  injected expand resolver, regionStats seeded, clean turn-1 state).
- tests: server/templates.test.ts (manifest order + file shape),
  server/scenario-shared.test.ts (15 cases: validation/clamp/dedup/null +
  transform).

Fixes #52
- server/scenario.ts: buildGeneratePrompt / buildRegeneratePrompt, defensive
  parseGenerate / parseRegenerate (fence-strip + balanced-object + validate),
  generateDraft (draft world + advisor checkpoints) and finalizeWorld (final
  world constrained to approved checkpoints -> playable GameState), all over
  the injectable LlmClient with a nudge re-send. SCENARIO_* markers for the
  stub.
- server/stubScenario.ts: deterministic scenario stub transport (fixed 3-nation
  world + checkpoint list; finalize refines stability per approved checkpoint)
  + makeScenarioStubLlmClient. Distinct from and never touching turn stub.ts.
- server/regions.ts: validCountryCodes() accessor (ISO_A3 parents) for draft
  territory validation.
- shared/scenario.ts: worldDraftToGameState now guards a dangling playerNationId
  (falls back to the first nation) so a finalize that drops it stays playable.
- server/scenario.test.ts: 16 tests (prompt builders, defensive parsing,
  generateDraft/finalizeWorld over a fake client + the stub).

Fixes #52
- GET /api/scenarios/templates: the 3 era templates (templateStore from
  presets/templates/).
- POST /api/scenarios/draft: generateDraft (template + description + year) ->
  { world, checkpoints } (502 on transport failure / unusable world).
- POST /api/scenarios/commit: finalizeWorld (constrained to approved
  checkpoints, optionally based on the re-sent draft) -> saved GameState,
  returns { id } (a normal game at #/game/<id>).
- scenarioLlm: the deterministic scenario stub under LLM_STUB=1 (distinct
  from the untouched turn stub), the live client otherwise.
- jump route: synthesize a minimal Preset for dynamic games (presetId
  'dynamic', no static file) so their turns run; genuinely-missing static
  presets still 404.

Fixes #52
Additive 'Create scenario' path (v0.31): the home screen's create-scenario
card opens a ScenarioForm (pick one of 3 light era templates + a free-text
description + an editable, era-prefilled start year) whose 'Generate world'
posts the draft request; the CheckpointReview screen shows the generated
world (per-nation stats/territory/relations + backstory) and the advisor's
checkpoint list as an approve/remove checklist, then the player picks a
nation to lead and finalizes into a saved, playable game. Both LLM steps ride
the scenario client (deterministic stub under LLM_STUB=1); a failure toasts
and stays on the current screen. The turn stub and static presets are
untouched. New e2e covers the full deterministic flow (near-future 2035,
remove checkpoint 3, lead Boreas, finalize, one empty jump) and the 3-template
manifest.
chore(#52): v0.31.0 — version bump + changelog
CI / build + unit + e2e (pull_request) Successful in 1m52s
073e916767
kamilkrzywda scheduled this pull request to auto merge when all checks succeed 2026-10-15 12:28:02 +02:00
kamilkrzywda merged commit 8eea17148b into master 2026-10-15 12:29:49 +02:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OpenCode/History#53