batched round: queue actions/diplomacy, jump forward commits one turn (v0.2.0)
CI / build + unit + e2e (pull_request) Successful in 1m51s

This commit is contained in:
2026-10-11 14:36:35 +00:00
parent 3297658c81
commit 9bce14254d
16 changed files with 563 additions and 246 deletions
+8 -6
View File
@@ -1,11 +1,11 @@
# History
A self-contained, AI-driven **alternate-history simulator** in the spirit of _Pax Historia_ — the
LLM is the world engine. Pick one of five seed scenarios, choose a nation to lead, then take
actions, send diplomatic messages, or consult an advisor. Every move is sent to the model, which
returns a structured `TurnResult` (events + stat / relation / territory deltas) that the engine
validates, clamps, and applies to the world state. A MapLibre choropleth renders who owns what;
everything persists to SQLite and streams over SSE on a single port.
LLM is the world engine. Pick one of five seed scenarios, choose a nation to lead, then queue
actions and diplomatic messages into a pending round; a single "Jump forward" sends the whole batch
to the model, which returns a structured `TurnResult` (events + stat / relation / territory deltas)
that the engine validates, clamps, and applies to the world state. A MapLibre choropleth renders
who owns what; everything persists to SQLite and streams over SSE on a single port.
```
┌────────────┐ /api + /sse ┌──────────────────────────────┐ OpenAI chat ┌──────────────┐
@@ -91,7 +91,9 @@ before auto-merging. After merge, tag the version and deploy manually (`docker c
## Design notes
- **Server is authoritative.** All world state lives in SQLite; the client only renders state and
sends intents (`turn` / `jump` / `advisor`). The engine never trusts raw model output — it strips
sends intents (`action` / `diplomacy` / `jump` / `advisor`). Actions and diplomatic messages are
queued into `game.pending` (no LLM call, no turn advance); a single `jump` ("Jump forward")
commits the whole batch in one LLM turn. The engine never trusts raw model output — it strips
fences, extracts the first balanced `{…}`, `JSON.parse`s, ignores invalid ops, clamps stats to
0–100, retries once with a nudge, and if still unusable emits a generic "unclear" event and
advances. Bad model output never crashes; only a network failure (model down) surfaces as a