refactor(#74) P6: bump to v0.40.0 + changelog
CI / build + unit + e2e (pull_request) Successful in 2m44s

- 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
This commit is contained in:
2026-10-16 07:23:11 +00:00
parent 4f8e3534ff
commit a46efcc1cb
2 changed files with 16 additions and 1 deletions
+15
View File
@@ -2,6 +2,21 @@
All notable changes to **History** are listed here, newest first.
## v0.40.0 — 2026-10-16 07:14
- **#67: Modular LLM-ability system — the 8 per-feature LLM abilities are extracted from `server/engine.ts` into an ordered `ABILITIES` registry (`server/abilities/`).** A pure, **behavior-preserving** refactor: each per-ability concern (validate / schema-line / constraint / payload / meaningfulness / apply / stub-default / durable-state-field) moves into one file per ability, assembled into a single ordered registry that the engine iterates uniformly. **Nothing observable changes** — the system prompt, user prompt, `applyTurnResult` world-state mutations, and the deterministic stub are all byte-for-byte identical to v0.39.0 (pinned by new golden fixtures + registry tests), and `server/engine.ts` re-exports every name its existing callers import, so `server/index.ts` and all tests work with zero edits.
- **New `server/abilities/` package.**
- `types.ts` — the `Ability<K extends AbilityField>` interface (a self-contained per-field module: `field`, `default()`, `validate()`, `apply()`, `schemaLine()`, `constraint()`, `isMeaningful()`, `stub()`, `payload()`, optional `stateFields`) plus the `AbilityField` union (the closed set of the 8 fields), `AbilityCtx`, and `UserPromptCtx` type surfaces.
- `shared.ts` — the cross-ability pure helpers (`resolveNationRef`, `resolveRegionKey`, `freshNationId`, `freshWarId`, `NEW_NATION_STATS`).
- `apply-helpers.ts` — the war-relation sync helpers (`activeWarPairs`, `atWar`, `syncWarRelations`).
- One file per ability — `takeovers.ts`, `wars.ts`, `hybrid.ts`, `economy.ts`, `industry.ts`, `nuclear.ts`, `leaders.ts`, `ideology.ts` — each owning its `Ability<K>` const (validator, applier, schema line, constraint bullet, meaningfulness predicate, inert stub default, user-payload keys, and — for wars / economy / industry — the `GameState` fields it owns).
- `index.ts` — assembles the ordered `ABILITIES` registry (`takeovers → wars → hybrid → economy → industry → nuclear → leaders → ideology`) and re-exports every public name the engine and its tests need.
- **Engine (`server/engine.ts`).** Every per-ability code path now iterates `ABILITIES` instead of hand-enumerating the eight fields: `buildSystemPrompt` (schema lines + constraint bullets), `buildUserPrompt` (payload keys at their exact historical positions), `validateTurnResult` (per-field validate), `applyTurnResult` (the ordered apply block), `isMeaningful`, and the `runTurn` "unclear" fallback. The ~1,249 lines of moved ability bodies are deleted; the 21 names its callers import are re-exported from `./abilities` (5 shared primitives + 16 ability-module exports).
- **Stub (`server/stub.ts`).** `deterministicTurn` fills the 8 ability fields from the registry (each contributes its inert default) between the core fields and `markers` — the JSON output stays byte-identical to v0.39.0 (verified against `origin/master`).
- **Persistence (`server/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.
- **Tests.** New `server/abilities/registry.test.ts` (11 tests): the committed **golden prompts** (`server/abilities/goldens/` — system no-tool / system tool / user) are asserted **byte-identical** to the pre-refactor engine's output (a `REGEN_GOLDENS=1` mode regenerates them after an intentional prompt change — verified a no-op); the registry is the canonical 8-ability order with 8 distinct `TurnResult` fields; each `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 registry composes (a hypothetical 9th ability appends cleanly to every uniform loop); and the engine still re-exports every name its callers import.
- **Guarantees.** No client (`src/`) changes, no new runtime deps, no `@turf`/`@types/geojson` in any `.ts`, and `shared/` keeps zero runtime deps. All 522 unit tests green; e2e (CI, `LLM_STUB=1`) unaffected.
- **Version** bumped `0.39.0 → 0.40.0`.
## v0.39.0 — 2026-10-15 20:51
- **#62: Ideology / form of government — a per-nation `ideology` + a `TurnResult.ideology` lever (coups / revolutions / reforms / constitutional changes) with a deterministic −5 stability dip and political narration.** Each nation may carry a free-form `ideology` label — its form of government ("democracy", "fascism", "military junta", …). The model changes it each turn via `ideology.changes`; each change names a target nation (id or name) and the new government type. The engine sets the label and applies a **deterministic** stability dip (a regime shift always unsettles a nation) — **no hidden randomness**, **additive** (no `db.ts` change — `ideology` is an optional `Nation` field; a pre-v0.39 row loads with it absent, i.e. "no recorded government"). It is a third, independent axis next to `leader` (who is in charge) and the v0.32.0 takeover (which renames the nation): the id, name, territory, relations, and the leader all stay valid — only the form of government and stability move.
- **Types (`shared/types.ts`).** `Nation.ideology?: string` (optional; `undefined`/absent = no recorded government).
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "history",
"version": "0.39.0",
"version": "0.40.0",
"private": true,
"type": "module",
"description": "AI-driven alternate-history simulator — the LLM is the world engine (Pax Historia style).",