feat(#62): ideology / government-type change (v0.39.0)
CI / build + unit + e2e (pull_request) Successful in 2m33s
CI / build + unit + e2e (pull_request) Successful in 2m33s
Add a per-nation free-form ideology (form of government) field and a
TurnResult.ideology lever (coups / revolutions / reforms / constitutional
changes) with a deterministic -5 stability dip and political narration.
- shared/types.ts: Nation.ideology?: string (optional/additive)
- shared/protocol.ts: IdeologyChange / IdeologyOps, IDEOLOGY_STABILITY_EFFECT
= -5, TurnResult.ideology (required, between leaders and markers),
nationForPrompt now carries ideology
- server/engine.ts: validateIdeologyOps (exported, never throws),
applyIdeologyChange (resolve -> set field -> -5 dip mirrored to regions +
re-derived -> political event), ideology block in applyTurnResult after
leaders / before markers, isMeaningful + validateTurnResult + system prompt
+ runTurn unclear fallback
- server/stub.ts: StubPayload gains ideology; deterministicTurn returns
ideology: { changes: [] } (never changes one, e2e unaffected)
- Tests: engine.test.ts (validate / isMeaningful / applyTurnResult /
buildUserPrompt), protocol.test.ts (constant + nationForPrompt),
db.test.ts (legacy load + round-trip); every TurnResult fixture updated
- package.json -> 0.39.0; CHANGELOG v0.39.0 entry
This commit is contained in:
@@ -4,7 +4,9 @@ import {
|
||||
CONQUEST_MILITARY_MARGIN,
|
||||
deriveNationResources,
|
||||
deriveNationStats,
|
||||
IDEOLOGY_STABILITY_EFFECT,
|
||||
migrateRegionStats,
|
||||
nationForPrompt,
|
||||
seedRegionStats,
|
||||
weightedMedian,
|
||||
} from '../shared/protocol';
|
||||
@@ -337,3 +339,30 @@ describe('canConquer', () => {
|
||||
expect(canConquer(49, 50, 0)).toBe(false); // one below → repel
|
||||
});
|
||||
});
|
||||
|
||||
// --- IDEOLOGY_STABILITY_EFFECT (v0.39, #62) ----------------------------------
|
||||
|
||||
describe('IDEOLOGY_STABILITY_EFFECT (v0.39.0 #62)', () => {
|
||||
it('a regime shift is a deterministic −5 stability dip', () => {
|
||||
expect(IDEOLOGY_STABILITY_EFFECT).toBe(-5);
|
||||
});
|
||||
});
|
||||
|
||||
// --- nationForPrompt — ideology (v0.39, #62) ---------------------------------
|
||||
|
||||
describe('nationForPrompt — ideology (v0.39.0 #62)', () => {
|
||||
it('stamps the ideology when a nation has one', () => {
|
||||
const n = nation('uk', 'UK', S(50, 50, 50, 50), ['k1']);
|
||||
n.ideology = 'fascism';
|
||||
expect(nationForPrompt(n).ideology).toBe('fascism');
|
||||
// and it survives JSON serialisation (the user prompt path)
|
||||
expect(JSON.stringify(nationForPrompt(n))).toContain('"ideology":"fascism"');
|
||||
});
|
||||
|
||||
it('omits the ideology from the JSON when a nation has none (undefined key dropped)', () => {
|
||||
const n = nation('uk', 'UK', S(50, 50, 50, 50), ['k1']);
|
||||
expect(n.ideology).toBeUndefined();
|
||||
// JSON.stringify drops undefined-valued keys → no "ideology" in the prompt
|
||||
expect(JSON.stringify(nationForPrompt(n))).not.toContain('ideology');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user