Commit Graph
13 Commits
Author SHA1 Message Date
kamilkrzywda 1c64aa1da4 Tests: region-key contract (map rewrite + regions loader + engine) (v0.19 P1 of #16)
- map.test.ts: rewrite the fixture for region keys; the #6 regression guard now
  proves the owner resolves via properties.regionKey while the repacked numeric
  feature.id and the sub-region regionId do not.
- regions.test.ts (new): the loader sees 1,796 valid keys; FRA expands to its
  42 sub-region keys (all valid, all labelled '… (FRA)'); allTerritoryKeys
  unions correctly; and with no data it degrades to the raw input.
- engine.test.ts: buildUserPrompt includes a regions table only when provided;
  applyTurnResult with a validKeys set applies real keys and ignores bogus ones,
  and without one applies all (the legacy/unit path).
2026-10-14 09:14:24 +00:00
kamilkrzywda 325bdc5255 Flip ownership to region keys: shared contract + server (v0.19 P1 of #16)
The single source of truth now keys ownership by region key (one per owned
sub-region) instead of a parent-country ISO_A3:

- shared/types.ts, shared/protocol.ts: TerritoryChange / RegionMap / regionOwner
  doc flips; mapFeatureOwner now reads the feature's stamped properties.regionKey
  (never the repacked numeric feature.id, nor the sub-region id — the #6 guard).
- server/regions.ts (new, turf-free): loads region-keys.json + regions.geo.json
  once at boot; expands a nation's ISO_A3 list to its sub-regions' keys, builds
  the LLM 'regions' reference table (key -> "name (parent)"), and exposes the
  set of valid keys. Degrades gracefully when the files are missing.
- server/engine.ts: buildSystemPrompt instructs the model to use region keys;
  buildUserPrompt gains an optional regionReference table; applyTurnResult gains
  an optional validKeys set so hallucinated keys are ignored.
- server/index.ts: loads region data at boot; expands each nation's country list
  to region keys at game creation; the jump route passes the reference + valid
  keys through runTurn.

The stub is unchanged (it emits an empty, region-key-conformant territoryChanges).
2026-10-14 09:14:10 +00:00
kamilkrzywda 06cdbf3d81 Switch map to sub-national regions (v0.12.0)
CI / build + unit + e2e (pull_request) Successful in 1m18s
Part of #16
2026-10-12 01:07:33 +00:00
kamilkrzywda e38cc5933f Reduce turn latency: cap recent events (10→5) + concise-reasoning constraint (v0.9.0)
CI / build + unit + e2e (pull_request) Successful in 2m9s
Fixes #22
2026-10-11 20:59:18 +00:00
kamilkrzywda 17d6cecab9 feat: remove a saved game & redirect on missing/deleted game (#20)
CI / build + unit + e2e (pull_request) Successful in 1m14s
- DELETE /api/games/:id route + Db.delete() (404 when absent)
- PresetPicker: per-saved-game trash button + confirm dialog (Cancel/Delete)
- App: runDeleteGame; useSse onError -> goHome on 'deleted'/'not found'
  (a deleted/missing game now returns the live tab and any deep link home)
- DeleteGameResponse in shared/protocol.ts; api.deleteGame()
- unit test (Db.delete) + 2 e2e tests (home-screen delete; in-flight delete
  returns the active tab home and a fresh deep link lands on home)
- version 0.7.0 -> 0.8.0 + changelog

Fixes #20
2026-10-11 20:32:55 +00:00
kamilkrzywda eadae4dc4f map: click a region to switch the dossier to its owner; fix "no owner" (v0.7.0)
CI / build + unit + e2e (pull_request) Successful in 1m58s
2026-10-11 19:22:33 +00:00
kamilkrzywda ef27b59762 0.4.0 — fix queue-add race + per-item remove button (#10, #11) 2026-10-11 16:09:29 +00:00
kamilkrzywda 9bce14254d batched round: queue actions/diplomacy, jump forward commits one turn (v0.2.0)
CI / build + unit + e2e (pull_request) Successful in 1m51s
2026-10-11 14:36:35 +00:00
kamilkrzywda 3297658c81 reliability: low reasoning_effort, retry-with-backoff, LLM call logging
- send top-level reasoning_effort: "low" (LLM_REASONING_EFFORT) to cut the qwen3.8-27b CoT ~85%, so content is no longer truncated to null -> the ~50% 502s
- new env: LLM_MAX_TOKENS (default 4000, per-call opts.maxTokens still overrides), LLM_REASONING_EFFORT (default "low", off/empty omits the field)
- chat() retries up to 3 attempts: timeout/abort, 5xx, network errors (ECONNRESET/ECONNREFUSED/socket closed), null/empty content; backoff 2s->3s (2000*1.5^(n-1), +/-20% jitter); 4xx throws immediately
- logging: '[llm] chat ok' {ms, finishReason} on success; '[llm] chat failed' {attempts, lastStatus, message, totalMs} on final failure
- tests: 12 in llm.test.ts (retry success/failure, no 4xx retry, null-content retry, logging spies); AGENTS.md env table + gotchas updated
2026-10-11 13:46:18 +00:00
kamilkrzywda 2ed1b15318 Fix save/resume after refresh + map basemap
Map (basemap was rendering black):
- Inline MapLibre style (dark-navy background + subtle OpenFreeMap ne2sr
  raster) instead of the remote "dark" style, whose vector tiles are empty at
  world zoom and gated the choropleth on a remote styledata fetch.
- Explicit prod worker URL (maplibre-gl-worker via ?url) so GeoJSON sources
  process in the production bundle.
- Hardened the choropleth against geo load-order races (empty sources up
  front, ownership stamped once data lands) and bumped fill opacity so the
  vivid per-nation colors are the star on the non-black background.

Save/resume (a refresh used to drop users to the home screen):
- Persist the active game id in localStorage (history.activeGameId) on create
  and resume; clear it when explicitly starting a new game.
- Auto-resume the last game on a page refresh (GET /api/games/:id + /map),
  re-attaching the SSE stream; fall back to home gracefully if it's gone.
- Home screen: prominent "Resume last game" button + a "Saved games" section
  (newest first). New GET /api/games list endpoint.

e2e: two new deterministic tests — auto-resume-on-reload and explicit resume
from the home screen (both under LLM_STUB=1).

Quality gates: typecheck, lint, 21 unit tests, 3 e2e tests — all pass.
2026-10-11 07:18:11 +00:00
kamilkrzywda 913d094e11 Full-screen map with draggable, collapsible floating HUD windows
- MapLibre map is now a fixed, full-viewport background layer (z-0)
- New FloatingWindow: drag by title bar (pointer capture, viewport-clamped,
  position persisted to localStorage), collapsible to title bar, bring-to-front
  z-index; semi-transparent slate window with amber/cyan accents
- Game screen re-laid out as floating windows over the map: slim top header
  (scenario, turn + date, player nation chip, Idle/Simulating status, Focus map
  collapse-all), dossier (top-left), event feed (right), Your move deck
  (bottom-center), legend (bottom-left), tiny zoom control (bottom-right)
- MapLibre default nav controls removed; custom floating zoom-in/out control
- Panels gain an inWindow mode (no card chrome, same testids)
- server: honor HISTORY_API_PORT as PORT fallback (dev proxy/e2e agreement)
- css: keep maplibre canvas base color and Radix portals above HUD windows
2026-10-10 22:47:08 +00:00
kamilkrzywda e327e9aea0 Polish map & UI into a command-center war-room; rewire live LLM endpoint
Map (WorldMap):
- Keep the near-black OpenFreeMap `dark` basemap (verified HTTP 200; the
  suggested dark-matter / dark-outdoors / liberty-dark variants 404 on that
  host) so the choropleth nation colors pop
- Stable per-nation fills at 0.8 opacity, thin subtle borders, and a brighter
  thicker amber border for the player nation so "this is me" is always obvious
- Hover brightens a region and shows a color-chipped owner + key stats popup;
  click opens a full dossier popup and focuses the left panel on that nation
- Transient, self-cleaning flash (pulsing amber outline) over regions whose
  ownership changed this turn
- Bottom-left legend auto-generated from the region map; per-scenario initial
  camera (Europe for the WWII scenarios, wider for Cold War / Modern / Aegis)

Command-center UI:
- New header bar: scenario title, prominent turn + date, the player nation
  (name + color chip), and a live Idle / "Simulating…" status indicator
- 3-column operations-console grid: nation dossier (left) / dominant map
  (center) / command deck + event feed (right); stacks map-first on mobile
- EventFeed: one card per event with a category icon, a colored outcome badge
  (success/failure/partial/neutral), a nation-colored accent edge, and the
  turn + date; new events slide in and flash at the top
- NationPanel: value-colored animated stat bars (red → green), territory as
  color-coded region chips, and diplomacy as a list with a colored indicator
  per relation (ally/rival/war/neutral)
- ActionPanel: a "Your move" composer with one prominent Send button and a
  spinner + "turns can take a few seconds" hint while a turn resolves
- sonner error toasts: a 502 / model-down surfaces as "AI temporarily
  unavailable — try again"

Live LLM rewire (server, previously uncommitted):
- Default endpoint https://ai.kkhost.pl/v1 (vLLM, no API key); reasoning model
  qwen3.8-27b; always send a generous max_tokens (4000) so it finishes its
  chain-of-thought before emitting content; read only `content`, ignore
  `reasoning`. docker-compose + AGENTS.md updated to match.

Tests: the deterministic Playwright e2e gains assertions for the dossier stat
bars, the map legend (all nations), and the event-card type icon + outcome
badge; it stays fully deterministic under LLM_STUB=1.
2026-10-10 22:15:05 +00:00
kamilkrzywdaandOpenCode e2936e85bb History: AI-driven alternate-history simulator
Pax Historia-style sim where the LLM is the world engine.

- React 19 + Vite 6 + Tailwind v4 + shadcn/ui frontend
- Express single-port backend (/api + /sse + built client + SPA fallback)
- SQLite (better-sqlite3) persistence, one row per game
- MapLibre choropleth map (OpenFreeMap dark + ISO_A3 regions)
- Defensive turn loop: strip fences, balanced-{} extract, parse, clamp 0-100,
  retry-once nudge, generic 'unclear' fallback; never crashes on bad output
- 5 seed presets (wwii-1939, coldwar-1955, modern-2025, no-wwii-1940, aegis-2400)
- LLM client over native fetch w/ injectable transport (mesh-bot pattern);
  LLM_STUB=1 deterministic transport for tests/e2e
- Unit (vitest, 19) + deterministic Playwright e2e
- Multi-stage node:22-slim Docker + Traefik (history.dev.kkhost.pl:4321)

Co-Authored-By: OpenCode <opencode@users.noreply.github.com>
2026-10-10 21:20:23 +00:00