v0.40.0 — Modular LLM-ability system (ordered registry) #75

Merged
kamilkrzywda merged 6 commits from refactor/ability-modules into master 2026-10-16 09:27:43 +02:00
Owner

What this does

Behavior-preserving refactor of server/engine.ts: the 8 per-feature LLM abilities
(takeovers, wars, hybrid, economy, industry, nuclear, leaders, ideology)
are extracted into server/abilities/ as an ordered ABILITIES registry. Each ability
owns its own Ability<K> — field, default(), validate, apply, schemaLine,
constraint, isMeaningful, zero-arg stub(), payload, and optional stateFields —
and engine.ts becomes a thin orchestrator that loops the registry instead of
hand-enumerating the eight fields.

Why it is safe (all verified)

  • Identical buildSystemPrompt (no-tool + tool) — byte-for-byte vs committed goldens.
  • Identical buildUserPrompt JSON — byte-for-byte vs committed goldens.
  • Identical applyTurnResult world-state mutations (same load-bearing order).
  • Deterministic stub.ts — deterministicTurn JSON byte-identical to origin/master.
  • All 522 unit tests green (16 files) + typecheck + lint + build.
  • server/engine.ts re-exports every name it used to, so server/index.ts and all
    tests work with zero edits.
  • No new runtime deps; no @turf/@types/geojson in any .ts; shared/ stays
    dependency-free.
  • Client (src/) untouched.

New tests

server/abilities/registry.test.ts pins the contract: 3 golden-prompt tests (byte-identical
system/user prompts, with a REGEN_GOLDENS=1 rewrite mode), registry order + distinct
fields, default() equality and freshness, isMeaningful, the stateFields set, registry
composition, and a backward-compatibility check that the engine still re-exports every
caller-imported name.

Notes

  • Ships as v0.40.0 (version bump + CHANGELOG.md entry included).
  • Not deployed — CI only. Production deploy happens manually after merge + tag, on request.

Fixes #74

## What this does Behavior-preserving refactor of `server/engine.ts`: the 8 per-feature LLM abilities (`takeovers`, `wars`, `hybrid`, `economy`, `industry`, `nuclear`, `leaders`, `ideology`) are extracted into `server/abilities/` as an **ordered `ABILITIES` registry**. Each ability owns its own `Ability<K>` — `field`, `default()`, `validate`, `apply`, `schemaLine`, `constraint`, `isMeaningful`, zero-arg `stub()`, `payload`, and optional `stateFields` — and `engine.ts` becomes a thin orchestrator that loops the registry instead of hand-enumerating the eight fields. ## Why it is safe (all verified) - **Identical `buildSystemPrompt`** (no-tool + tool) — byte-for-byte vs committed goldens. - **Identical `buildUserPrompt`** JSON — byte-for-byte vs committed goldens. - **Identical `applyTurnResult`** world-state mutations (same load-bearing order). - **Deterministic `stub.ts`** — `deterministicTurn` JSON byte-identical to `origin/master`. - **All 522 unit tests green** (16 files) + `typecheck` + `lint` + `build`. - `server/engine.ts` **re-exports every name** it used to, so `server/index.ts` and all tests work with **zero edits**. - **No new runtime deps**; no `@turf`/`@types/geojson` in any `.ts`; `shared/` stays dependency-free. - **Client (`src/`) untouched.** ## New tests `server/abilities/registry.test.ts` pins the contract: 3 golden-prompt tests (byte-identical system/user prompts, with a `REGEN_GOLDENS=1` rewrite mode), registry order + distinct fields, `default()` equality and freshness, `isMeaningful`, the `stateFields` set, registry composition, and a backward-compatibility check that the engine still re-exports every caller-imported name. ## Notes - Ships as **v0.40.0** (version bump + `CHANGELOG.md` entry included). - **Not deployed** — CI only. Production deploy happens manually after merge + tag, on request. Fixes #74
kamilkrzywda added 6 commits 2026-10-16 09:24:44 +02:00
Adds deterministic golden fixtures (server/abilities/goldens/) pinning the
EXACT output of buildSystemPrompt (no-tool + tool modes) and buildUserPrompt
for a fixed wwii-1939 preset plus a crafted state that exercises every
optional payload branch (regions, frontier, frontierMilitary, takeoverRisks,
unowned, markers, wars, relations, trades, embargoes, resources, industry).

These are the target: the v0.40.0 modular ability registry (P2-P5) must
reproduce these byte-for-byte. Part of #74.
Move the 5 shared reference/id primitives (resolveNationRef, resolveRegionKey,
freshNationId, freshWarId, NEW_NATION_STATS) from engine.ts into the new
server/abilities/shared.ts (breaking the engine<->ability import cycle), and
add server/abilities/types.ts with the Ability<K> interface + AbilityCtx /
UserPromptCtx types that the 8 per-feature ability modules will implement.
engine.ts imports the helpers and re-exports them so existing callers
(server/index.ts, the unit tests) are untouched. Behavior is identical; all
511 unit tests, lint and typecheck stay green.
Each per-feature LLM ability is now a self-contained module in
server/abilities/ with its own Ability<K> const (schema line, constraint
bullet, validator, applier, isMeaningful, stub default, user-payload
field). The ordered ABILITIES registry (index.ts) assembles them in the
canonical order: takeovers → wars → hybrid → economy → industry → nuclear
→ leaders → ideology.

All function bodies are moved VERBATIM from engine.ts. The engine is
untouched in this commit — P4 will rewire it to use the registry.
Behavior-preserving rewire: every per-ability code path now iterates the
ordered ABILITIES registry instead of hand-enumerating the eight fields.

- engine.ts: buildSystemPrompt (schema lines + constraint bullets),
  buildUserPrompt (payload keys at their historical positions),
  validateTurnResult (per-field validate), applyTurnResult (ordered apply
  block), isMeaningful, and the runTurn "unclear" fallback all drive off
  ABILITIES. The eight moved ability bodies (~1,249 lines) are deleted;
  the names the engine and its tests import are re-exported from
  ./abilities.
- abilities/types.ts: Ability.stub is a zero-arg method (the stub has no
  GameState/AbilityCtx to pass; it needs none) — all eight implementations
  were already arg-less.
- stub.ts: deterministicTurn fills the eight ability fields from the
  registry (each contributes its inert default), between the core fields
  and markers — JSON output stays byte-identical (verified against
  origin/master).
- db.ts: the legacy-row migration for ability-owned GameState fields
  (wars/trades/embargoes/industry) iterates the registry's stateFields,
  only-if-undefined, instead of a hardcoded field list.

No client changes, no new deps, no shared/ runtime deps. typecheck + lint
+ 511 unit tests green; stub JSON byte-identical to origin/master.

Fixes #74
Adds server/abilities/registry.test.ts, the safety net for the refactor:

- GOLDEN PROMPTS: the refactored engine's system prompt (both tool
  variants) and user prompt are asserted BYTE-IDENTICAL to the committed
  fixtures in server/abilities/goldens/ (captured in P1 from the
  pre-refactor engine). The state builder the goldens were captured from
  is re-embedded here, with a REGEN_GOLDENS=1 mode to rewrite the
  fixtures after an intentional prompt change (verified a no-op — the
  refactored output is exactly stable).
- REGISTRY CONTRACT: ABILITIES is the canonical 8-ability order with 8
  distinct TurnResult fields; each ability's default() equals the
  canonical TurnResult default and is a FRESH value (never a shared
  reference); the default is never "meaningful" while a populated value
  is; the registry declares exactly the durable GameState fields the db
  migration must default (wars/trades/embargoes/industry); the registry
  composes (a hypothetical extra ability appends cleanly to every
  uniform loop); and the engine still re-exports every name its callers
  import from it (backward compatibility).

The throwaway _golden-capture.ts is deleted (its logic is now the
REGEN mode of this test).

Fixes #74
refactor(#74) P6: bump to v0.40.0 + changelog
CI / build + unit + e2e (pull_request) Successful in 2m44s
a46efcc1cb
- package.json: 0.39.0 → 0.40.0.
- CHANGELOG.md: new v0.40.0 entry documenting the modular LLM-ability
  system (the behavior-preserving extraction of the 8 per-feature
  abilities into the ordered ABILITIES registry), with the guarantees
  (byte-identical prompts/mutations/stub, zero client changes, no new
  deps, shared/ stays dep-free) and the new registry/golden tests.

Verified: typecheck + lint + 522 unit tests + build all green.

Fixes #74
kamilkrzywda scheduled this pull request to auto merge when all checks succeed 2026-10-16 09:27:41 +02:00
kamilkrzywda merged commit df52261835 into master 2026-10-16 09:27:43 +02:00
kamilkrzywda deleted branch refactor/ability-modules 2026-10-16 09:27:44 +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#75