From d6802ba6c96089e2a3b085dc39051b87e23422f7 Mon Sep 17 00:00:00 2001 From: Ben Richards Date: Sun, 6 Sep 2026 22:42:33 +0300 Subject: [PATCH] =?UTF-8?q?W104=20=E2=80=94=20Compatibility=20sweep:=20man?= =?UTF-8?q?ifest,=20v0.10.0=20baseline=20diff,=20real=20save=20loads?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes W104.1, W104.2, W104.3, continuing #447's partial W104: - W104.1: the manifest now also names every 0.10 built-in campaign and its recorded version (scripts/w104-manifest.test.ts), plus the new representative save and v0.10.0 compat-baseline fixtures below. - W104.2: fixtures/compat/v0.10.0/*.json freezes, per built-in campaign registry, the canonical initial Scene (projection + actions + status) and Tier-1/2 findings captured by running capture-compat-baseline.ts against the v0.10.0 tag. capture-compat-baseline.test.ts reruns the same capture at HEAD and diffs — byte-identical across all five entries, since none of these campaigns ever populated a W93-W103 additive field. scripts/capture-compat-baseline.ts is the one producer, run at both revisions. - W104.3: fixtures/saves/*.json are real SaveEnvelopes (story-graph, simulation, world-graph x active/ended), captured by running committed replay fixtures through the real engine. capture-save-fixtures.test.ts loads each through an actual SessionStore.loadGame and confirms the resulting scene matches an independent re-run of the same fixture, plus one negative case proving a tampered blob is rejected with no partial write. Every load takes the no-migration branch — 20-contract.md already states no shipped campaign has moved version yet, so migration itself stays proven only against the synthetic case in envelope.test.ts. W104.5 (client/host/Adventures/service-contract parity against one archive) stays unmet: SubZeroDev.Adventures and the service-contract generator are not present in this repository, so it cannot be exercised from here — same disposition #447 recorded for the other deferred criteria. Co-Authored-By: Claude Sonnet 5 --- build/Test-CompatibilitySweep.ps1 | 9 +- .../compat/v0.10.0/bulgaria-bureaucracy.json | 69 +++ .../compat/v0.10.0/long-horizon-win.json | 184 ++++++++ .../v0.10.0/stable-life-bulgaria-pack.json | 422 ++++++++++++++++++ .../fixtures/compat/v0.10.0/stable-life.json | 173 +++++++ .../compat/v0.10.0/world-graph-mvp.json | 143 ++++++ .../fixtures/saves/simulation-active.json | 1 + .../fixtures/saves/simulation-ended.json | 1 + .../fixtures/saves/story-graph-active.json | 1 + .../fixtures/saves/story-graph-ended.json | 1 + .../fixtures/saves/world-graph-active.json | 1 + .../fixtures/saves/world-graph-ended.json | 1 + .../scripts/capture-compat-baseline.test.ts | 27 ++ src/engine/scripts/capture-compat-baseline.ts | 143 ++++++ .../scripts/capture-save-fixtures.test.ts | 111 +++++ src/engine/scripts/capture-save-fixtures.ts | 122 +++++ src/engine/scripts/w104-manifest.test.ts | 77 ++++ 17 files changed, 1483 insertions(+), 3 deletions(-) create mode 100644 src/engine/fixtures/compat/v0.10.0/bulgaria-bureaucracy.json create mode 100644 src/engine/fixtures/compat/v0.10.0/long-horizon-win.json create mode 100644 src/engine/fixtures/compat/v0.10.0/stable-life-bulgaria-pack.json create mode 100644 src/engine/fixtures/compat/v0.10.0/stable-life.json create mode 100644 src/engine/fixtures/compat/v0.10.0/world-graph-mvp.json create mode 100644 src/engine/fixtures/saves/simulation-active.json create mode 100644 src/engine/fixtures/saves/simulation-ended.json create mode 100644 src/engine/fixtures/saves/story-graph-active.json create mode 100644 src/engine/fixtures/saves/story-graph-ended.json create mode 100644 src/engine/fixtures/saves/world-graph-active.json create mode 100644 src/engine/fixtures/saves/world-graph-ended.json create mode 100644 src/engine/scripts/capture-compat-baseline.test.ts create mode 100644 src/engine/scripts/capture-compat-baseline.ts create mode 100644 src/engine/scripts/capture-save-fixtures.test.ts create mode 100644 src/engine/scripts/capture-save-fixtures.ts create mode 100644 src/engine/scripts/w104-manifest.test.ts diff --git a/build/Test-CompatibilitySweep.ps1 b/build/Test-CompatibilitySweep.ps1 index a067a34..2f4f7c7 100644 --- a/build/Test-CompatibilitySweep.ps1 +++ b/build/Test-CompatibilitySweep.ps1 @@ -8,9 +8,12 @@ the current tree can call itself a compatibility candidate against a prior release tag (W104.7): 1. typecheck, lint, and the full test suite from src/engine -- includes the - W96.2/W104.1 regression-evidence manifest (regressionManifest.test.ts), - which fails the moment a named suite, fixture, host, or packed-consumer - file goes missing. + W96.2/W104.1 regression-evidence manifest (regressionManifest.test.ts and + scripts/w104-manifest.test.ts), which fails the moment a named suite, fixture, + host, built-in campaign/version, or packed-consumer file goes missing; the v0.10.0 + compatibility baseline comparison (W104.2, scripts/capture-compat-baseline.test.ts); + and the representative-save load-through-SessionStore proof (W104.3, + scripts/capture-save-fixtures.test.ts). 2. The replay regression oracle (07-replay.md), run locally against the baseline tag's own committed fixtures and outcomes -- the same REPLAY_BASELINE_DIR mechanism .github/workflows/ci.yml's diff --git a/src/engine/fixtures/compat/v0.10.0/bulgaria-bureaucracy.json b/src/engine/fixtures/compat/v0.10.0/bulgaria-bureaucracy.json new file mode 100644 index 0000000..503a0b8 --- /dev/null +++ b/src/engine/fixtures/compat/v0.10.0/bulgaria-bureaucracy.json @@ -0,0 +1,69 @@ +{ + "label": "bulgaria-bureaucracy", + "campaignId": "bulgaria-bureaucracy", + "validation": { + "errors": [], + "warnings": [] + }, + "initialScene": { + "gameId": "counting-game-id-0", + "status": "active", + "body": { + "textKey": "bureaucracy.municipality.text", + "text": "You arrive at the municipality at 08:03. The office opened at 08:00. A handwritten note says the meeting ends at 11:30; three nearby doors disagree about the year." + }, + "actions": [ + { + "id": "wait", + "labelKey": "bureaucracy.municipality.choice_wait", + "available": true + }, + { + "id": "ask_guard", + "labelKey": "bureaucracy.municipality.choice_ask_guard", + "available": true + }, + { + "id": "coffee", + "labelKey": "bureaucracy.municipality.choice_coffee", + "available": true + }, + { + "id": "try_another_entrance", + "labelKey": "bureaucracy.municipality.choice_try_another_entrance", + "available": true + } + ], + "view": { + "gameId": "counting-game-id-0", + "status": "active", + "kindView": { + "turn": 0, + "stats": [ + { + "var": "connections", + "labelKey": "bureaucracy.var_connections.label", + "value": 0, + "min": 0, + "max": 12 + }, + { + "var": "preparation", + "labelKey": "bureaucracy.var_preparation.label", + "value": 0, + "min": 0, + "max": 12 + }, + { + "var": "pressure", + "labelKey": "bureaucracy.var_pressure.label", + "value": 0, + "min": 0, + "max": 12 + } + ], + "unlockedAchievements": [] + } + } + } +} diff --git a/src/engine/fixtures/compat/v0.10.0/long-horizon-win.json b/src/engine/fixtures/compat/v0.10.0/long-horizon-win.json new file mode 100644 index 0000000..7e14fd4 --- /dev/null +++ b/src/engine/fixtures/compat/v0.10.0/long-horizon-win.json @@ -0,0 +1,184 @@ +{ + "label": "long-horizon-win", + "campaignId": "long-horizon-win", + "validation": { + "errors": [], + "warnings": [ + { + "code": "unreachable_content", + "messageKey": "simulation.reason.unreachable_content", + "path": "housing-upgraded" + }, + { + "code": "unreachable_content", + "messageKey": "simulation.reason.unreachable_content", + "path": "housing-upgraded" + } + ] + }, + "initialScene": { + "gameId": "counting-game-id-0", + "status": "active", + "body": { + "textKey": "long-horizon.scene.status", + "text": "Week 1 of Year 1. Cash: $200.00. Health 80 · Energy 50 · Happiness 60 · Stress 20 · Satiety 80." + }, + "actions": [ + { + "id": "plan.add", + "labelKey": "long-horizon.action.plan-add.label", + "available": true + }, + { + "id": "plan.remove", + "labelKey": "long-horizon.action.plan-remove.label", + "available": true + }, + { + "id": "plan.clear", + "labelKey": "long-horizon.action.plan-clear.label", + "available": true + }, + { + "id": "end_week", + "labelKey": "long-horizon.action.end-week.label", + "available": true + } + ], + "view": { + "gameId": "counting-game-id-0", + "status": "active", + "kindView": { + "calendar": { + "currentWeek": 1, + "currentYear": 1, + "totalTimeUnits": 14, + "committedTimeUnits": 0, + "availableTimeUnits": 14 + }, + "identity": { + "actorId": "player", + "name": "Alex", + "age": 28, + "backgroundId": "background-default" + }, + "currentLocationId": "home", + "finances": { + "cashCents": 20000, + "savingsCents": 0, + "debtCents": 0, + "weeklyIncomeCents": 0, + "weeklyExpensesCents": 0, + "overdueBalanceCents": 0, + "accounts": [] + }, + "needs": { + "health": 80, + "energy": 50, + "happiness": 60, + "stress": 20, + "satiety": 80 + }, + "attributes": { + "intelligence": 50, + "discipline": 50, + "charisma": 50, + "creativity": 50, + "resilience": 50, + "wisdom": 50 + }, + "education": { + "enrollments": [], + "credentials": [], + "completedCourseIds": [], + "failedCourseIds": [] + }, + "career": { + "history": [], + "totalWeeksEmployed": 0, + "pendingApplications": [], + "highestTierAchieved": "entry" + }, + "housing": { + "definitionId": "housing-modest", + "movedInWeek": 1, + "ownership": "renting", + "damage": 0, + "weeklyCostCents": 4000, + "depositPaidCents": 0, + "rentDueWeek": 1, + "overdueRentCents": 0, + "missedPayments": 0, + "evictionStage": "none" + }, + "inventory": [], + "relationships": [], + "skills": { + "bookkeeping": 0 + }, + "traits": [], + "reputation": {}, + "activeEffects": [], + "activeOpportunities": [], + "pendingEventResponses": [], + "goals": [ + { + "definitionId": "goal-established", + "status": "active", + "satisfiedThisWeek": false, + "consecutiveWeeksSatisfied": 0, + "progressNotes": [] + } + ], + "plan": { + "week": 1, + "actions": [], + "availableActionTypes": [ + "work", + "work_overtime", + "search_for_work", + "apply_for_job", + "negotiate_job_terms", + "attend_class", + "study", + "enroll_course", + "withdraw_course", + "shop", + "eat", + "rest", + "exercise", + "socialize", + "travel", + "maintain_item", + "repair_item", + "sell_item", + "pay_bills", + "borrow_money", + "repay_debt", + "deposit_savings", + "invest", + "move_housing", + "start_project", + "work_on_project", + "start_business", + "operate_business", + "accept_opportunity", + "decline_opportunity", + "respond_to_event" + ] + }, + "world": { + "locations": [], + "jobMarket": { + "openings": [] + }, + "economy": { + "sectorDemand": {}, + "marketPrices": {}, + "indicators": {} + } + } + } + } + } +} diff --git a/src/engine/fixtures/compat/v0.10.0/stable-life-bulgaria-pack.json b/src/engine/fixtures/compat/v0.10.0/stable-life-bulgaria-pack.json new file mode 100644 index 0000000..eee8673 --- /dev/null +++ b/src/engine/fixtures/compat/v0.10.0/stable-life-bulgaria-pack.json @@ -0,0 +1,422 @@ +{ + "label": "stable-life-bulgaria-pack", + "campaignId": "stable-life", + "validation": { + "errors": [], + "warnings": [ + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.campaign.title" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.effect.rakia-lek.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.event.power-cut.message" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.event.baba-letter.choice.open" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.event.baba-letter.choice.call" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.event.baba-letter.message.opened" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.event.baba-letter.message.called" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.campaign.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.job.vendor.title" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.job.vendor.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.job.bookkeeper.title" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.job.bookkeeper.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.course.schetovodstvo.name" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.course.schetovodstvo.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.housing.panelka.name" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.housing.panelka.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.item.moped.name" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.item.moped.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.event.power-cut.title" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.event.power-cut.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.event.baba-letter.title" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.event.baba-letter.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.npc.komshiyka.name" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.npc.komshiyka.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.goal.well-rested.label" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.goal.well-rested.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.scenario.name" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.scenario.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.opportunity.coffee.name" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.opportunity.coffee.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.achievement.first-week.name" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.achievement.first-week.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.headline.quiet" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.headline.strange" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.employer.bazaar.name" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.location.home.name" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.location.home.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.location.bazaar.name" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.location.bazaar.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.background.nachalo.name" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.background.nachalo.description" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.scene.status" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.action.plan-add.label" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.action.plan-remove.label" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.action.plan-clear.label" + }, + { + "code": "pack_override_unexpected", + "messageKey": "core.reason.pack_override_unexpected", + "path": "bulgaria-stable-life.action.end-week.label" + } + ] + }, + "initialScene": { + "gameId": "counting-game-id-0", + "status": "active", + "body": { + "textKey": "bulgaria-stable-life.scene.status", + "text": "Седмица 1, година 1. Пари: 200.00 лв. Здраве 80 · Енергия 60 · Щастие 60 · Стрес 20 · Ситост 80." + }, + "actions": [ + { + "id": "plan.add", + "labelKey": "bulgaria-stable-life.action.plan-add.label", + "available": true + }, + { + "id": "plan.remove", + "labelKey": "bulgaria-stable-life.action.plan-remove.label", + "available": true + }, + { + "id": "plan.clear", + "labelKey": "bulgaria-stable-life.action.plan-clear.label", + "available": true + }, + { + "id": "end_week", + "labelKey": "bulgaria-stable-life.action.end-week.label", + "available": true + } + ], + "view": { + "gameId": "counting-game-id-0", + "status": "active", + "kindView": { + "calendar": { + "currentWeek": 1, + "currentYear": 1, + "totalTimeUnits": 14, + "committedTimeUnits": 0, + "availableTimeUnits": 14 + }, + "identity": { + "actorId": "player", + "name": "Alex", + "age": 28, + "backgroundId": "background-nachalo" + }, + "currentLocationId": "home", + "finances": { + "cashCents": 20000, + "savingsCents": 0, + "debtCents": 0, + "weeklyIncomeCents": 0, + "weeklyExpensesCents": 0, + "overdueBalanceCents": 0, + "accounts": [] + }, + "needs": { + "health": 80, + "energy": 60, + "happiness": 60, + "stress": 20, + "satiety": 80 + }, + "attributes": { + "intelligence": 50, + "discipline": 50, + "charisma": 50, + "creativity": 50, + "resilience": 50, + "wisdom": 50 + }, + "education": { + "enrollments": [], + "credentials": [], + "completedCourseIds": [], + "failedCourseIds": [] + }, + "career": { + "history": [], + "totalWeeksEmployed": 0, + "pendingApplications": [], + "highestTierAchieved": "entry" + }, + "housing": { + "definitionId": "housing-panelka", + "movedInWeek": 1, + "ownership": "renting", + "damage": 0, + "weeklyCostCents": 5000, + "depositPaidCents": 0, + "rentDueWeek": 1, + "overdueRentCents": 0, + "missedPayments": 0, + "evictionStage": "none" + }, + "inventory": [ + { + "instanceId": "item-0", + "definitionId": "item-moped", + "quantity": 1, + "acquiredWeek": 1, + "purchasePriceCents": 8000, + "condition": 100, + "weeksSinceMaintenance": 0, + "broken": false + } + ], + "relationships": [], + "skills": { + "schetovodstvo": 0 + }, + "traits": [], + "reputation": {}, + "activeEffects": [ + { + "id": "effect-rakia-lek", + "sourceKind": "system", + "descriptionKey": "bulgaria-stable-life.effect.rakia-lek.description", + "expiresAtWeek": 2 + } + ], + "activeOpportunities": [], + "pendingEventResponses": [], + "goals": [ + { + "definitionId": "goal-well-rested", + "status": "active", + "satisfiedThisWeek": false, + "consecutiveWeeksSatisfied": 0, + "progressNotes": [] + } + ], + "plan": { + "week": 1, + "actions": [], + "availableActionTypes": [ + "work", + "work_overtime", + "search_for_work", + "apply_for_job", + "negotiate_job_terms", + "attend_class", + "study", + "enroll_course", + "withdraw_course", + "shop", + "eat", + "rest", + "exercise", + "socialize", + "travel", + "maintain_item", + "repair_item", + "sell_item", + "pay_bills", + "borrow_money", + "repay_debt", + "deposit_savings", + "invest", + "move_housing", + "start_project", + "work_on_project", + "start_business", + "operate_business", + "accept_opportunity", + "decline_opportunity", + "respond_to_event" + ] + }, + "world": { + "locations": [], + "jobMarket": { + "openings": [] + }, + "economy": { + "sectorDemand": {}, + "marketPrices": {}, + "indicators": {} + } + } + } + } + } +} diff --git a/src/engine/fixtures/compat/v0.10.0/stable-life.json b/src/engine/fixtures/compat/v0.10.0/stable-life.json new file mode 100644 index 0000000..c4822ab --- /dev/null +++ b/src/engine/fixtures/compat/v0.10.0/stable-life.json @@ -0,0 +1,173 @@ +{ + "label": "stable-life", + "campaignId": "stable-life", + "validation": { + "errors": [], + "warnings": [] + }, + "initialScene": { + "gameId": "counting-game-id-0", + "status": "active", + "body": { + "textKey": "stable-life.scene.status", + "text": "Week 1 of Year 1. Cash: $200.00. Health 80 · Energy 50 · Happiness 60 · Stress 20 · Satiety 80." + }, + "actions": [ + { + "id": "plan.add", + "labelKey": "stable-life.action.plan-add.label", + "available": true + }, + { + "id": "plan.remove", + "labelKey": "stable-life.action.plan-remove.label", + "available": true + }, + { + "id": "plan.clear", + "labelKey": "stable-life.action.plan-clear.label", + "available": true + }, + { + "id": "end_week", + "labelKey": "stable-life.action.end-week.label", + "available": true + } + ], + "view": { + "gameId": "counting-game-id-0", + "status": "active", + "kindView": { + "calendar": { + "currentWeek": 1, + "currentYear": 1, + "totalTimeUnits": 14, + "committedTimeUnits": 0, + "availableTimeUnits": 14 + }, + "identity": { + "actorId": "player", + "name": "Alex", + "age": 28, + "backgroundId": "background-default" + }, + "currentLocationId": "home", + "finances": { + "cashCents": 20000, + "savingsCents": 0, + "debtCents": 0, + "weeklyIncomeCents": 0, + "weeklyExpensesCents": 0, + "overdueBalanceCents": 0, + "accounts": [] + }, + "needs": { + "health": 80, + "energy": 50, + "happiness": 60, + "stress": 20, + "satiety": 80 + }, + "attributes": { + "intelligence": 50, + "discipline": 50, + "charisma": 50, + "creativity": 50, + "resilience": 50, + "wisdom": 50 + }, + "education": { + "enrollments": [], + "credentials": [], + "completedCourseIds": [], + "failedCourseIds": [] + }, + "career": { + "history": [], + "totalWeeksEmployed": 0, + "pendingApplications": [], + "highestTierAchieved": "entry" + }, + "housing": { + "definitionId": "housing-default", + "movedInWeek": 1, + "ownership": "renting", + "damage": 0, + "weeklyCostCents": 5000, + "depositPaidCents": 0, + "rentDueWeek": 1, + "overdueRentCents": 0, + "missedPayments": 0, + "evictionStage": "none" + }, + "inventory": [], + "relationships": [], + "skills": { + "bookkeeping": 0 + }, + "traits": [], + "reputation": {}, + "activeEffects": [], + "activeOpportunities": [], + "pendingEventResponses": [], + "goals": [ + { + "definitionId": "goal-well-rested", + "status": "active", + "satisfiedThisWeek": false, + "consecutiveWeeksSatisfied": 0, + "progressNotes": [] + } + ], + "plan": { + "week": 1, + "actions": [], + "availableActionTypes": [ + "work", + "work_overtime", + "search_for_work", + "apply_for_job", + "negotiate_job_terms", + "attend_class", + "study", + "enroll_course", + "withdraw_course", + "shop", + "eat", + "rest", + "exercise", + "socialize", + "travel", + "maintain_item", + "repair_item", + "sell_item", + "pay_bills", + "borrow_money", + "repay_debt", + "deposit_savings", + "invest", + "move_housing", + "start_project", + "work_on_project", + "start_business", + "operate_business", + "accept_opportunity", + "decline_opportunity", + "respond_to_event" + ] + }, + "world": { + "locations": [], + "jobMarket": { + "openings": [] + }, + "economy": { + "sectorDemand": {}, + "marketPrices": {}, + "indicators": {} + } + } + } + } + } +} diff --git a/src/engine/fixtures/compat/v0.10.0/world-graph-mvp.json b/src/engine/fixtures/compat/v0.10.0/world-graph-mvp.json new file mode 100644 index 0000000..f1f153a --- /dev/null +++ b/src/engine/fixtures/compat/v0.10.0/world-graph-mvp.json @@ -0,0 +1,143 @@ +{ + "label": "world-graph-mvp", + "campaignId": "world-graph-mvp", + "validation": { + "errors": [], + "warnings": [] + }, + "initialScene": { + "gameId": "counting-game-id-0", + "status": "active", + "body": { + "textKey": "world-graph.scene.summary", + "text": "Tick 0 • cash 20.00 • guests 0 • buildings 1 • objectives clean-litter:0/1" + }, + "actions": [ + { + "id": "build", + "labelKey": "world-graph.action.build", + "available": true + }, + { + "id": "demolish", + "labelKey": "world-graph.action.demolish", + "available": true + }, + { + "id": "hire_staff", + "labelKey": "world-graph.action.hire_staff", + "available": true + }, + { + "id": "fire_staff", + "labelKey": "world-graph.action.fire_staff", + "available": false, + "reasonKey": "world-graph.reason.unknown_entity" + }, + { + "id": "assign_staff", + "labelKey": "world-graph.action.assign_staff", + "available": false, + "reasonKey": "world-graph.reason.unknown_entity" + }, + { + "id": "set_price", + "labelKey": "world-graph.action.set_price", + "available": true + }, + { + "id": "open_building", + "labelKey": "world-graph.action.open_building", + "available": false, + "reasonKey": "world-graph.reason.unknown_entity" + }, + { + "id": "close_building", + "labelKey": "world-graph.action.close_building", + "available": true + }, + { + "id": "dismiss_alert", + "labelKey": "world-graph.action.dismiss_alert", + "available": false, + "reasonKey": "world-graph.reason.unknown_entity" + }, + { + "id": "advance_ticks", + "labelKey": "world-graph.action.advance_ticks", + "available": true + } + ], + "view": { + "gameId": "counting-game-id-0", + "status": "active", + "kindView": { + "tick": 0, + "finances": { + "cashCents": 2000, + "revenueTodayCents": 0, + "expensesTodayCents": 0 + }, + "map": { + "width": 5, + "height": 3, + "revision": 0, + "spawnPoints": [ + { + "x": 0, + "y": 1 + } + ], + "exits": [ + { + "x": 4, + "y": 1 + } + ], + "zones": [], + "buildingCount": 1, + "guestCount": 0, + "staffCount": 0 + }, + "buildOptions": [ + { + "definitionId": "hut", + "canBuild": true, + "blockedBy": [] + }, + { + "definitionId": "kiosk", + "canBuild": true, + "blockedBy": [] + }, + { + "definitionId": "stall", + "canBuild": true, + "blockedBy": [] + } + ], + "buildings": [ + { + "id": "building:0", + "definitionId": "kiosk", + "status": "open", + "queueLength": 0, + "cleanliness": 80, + "wear": 90 + } + ], + "staff": [], + "objectives": [ + { + "id": "clean-litter", + "state": "active", + "value": 0, + "target": 1 + } + ], + "alerts": [], + "queuedGuests": 0 + } + } + } +} diff --git a/src/engine/fixtures/saves/simulation-active.json b/src/engine/fixtures/saves/simulation-active.json new file mode 100644 index 0000000..dc5d0d4 --- /dev/null +++ b/src/engine/fixtures/saves/simulation-active.json @@ -0,0 +1 @@ +{"campaignId":"stable-life","campaignVersion":"1.0.0","checksum":"c82f3ce0e29356b9f2fd7c2c3a3654802c9f9983a2a64b91a59f551a666ff74d","engineVersion":"0.10.0","kindId":"simulation","kindVersion":"1.0.0","replayCompatible":true,"saveFormatVersion":1,"serializationVersion":1,"state":{"actionLog":[{"actionId":"plan.add","params":{"actionType":"enroll_course","targetId":"course-bookkeeping"},"seq":0},{"actionId":"plan.add","params":{"actionType":"attend_class","targetId":"course-bookkeeping"},"seq":1},{"actionId":"plan.add","params":{"actionType":"study","targetId":"course-bookkeeping"},"seq":2},{"actionId":"end_week","seq":3},{"actionId":"plan.add","params":{"actionType":"attend_class","targetId":"course-bookkeeping"},"seq":4},{"actionId":"plan.add","params":{"actionType":"study","targetId":"course-bookkeeping"},"seq":5},{"actionId":"end_week","seq":6},{"actionId":"plan.add","params":{"actionType":"search_for_work"},"seq":7},{"actionId":"plan.add","params":{"actionType":"apply_for_job","targetId":"job-accountant"},"seq":8},{"actionId":"end_week","seq":9}],"campaignId":"stable-life","campaignVersion":"1.0.0","formatVersion":1,"gameId":"fixed-game-id","kindId":"simulation","kindState":{"activeEffects":[],"activeOpportunities":[],"calendar":{"committedTimeUnits":0,"currentWeek":4,"currentYear":1,"spentTimeUnits":0,"totalTimeUnits":14},"economy":{"flags":{},"inflation":200,"interestRate":300,"marketPrices":{},"publishedIndicators":[],"sectorDemand":{},"unemploymentRate":500},"goals":[{"consecutiveWeeksSatisfied":0,"definitionId":"goal-well-rested","progressNotes":[],"satisfiedThisWeek":false,"status":"active"}],"pendingEventResponses":[],"plan":{"actions":[],"week":4},"player":{"attributes":{"charisma":50,"creativity":50,"discipline":50,"intelligence":50,"luck":50,"resilience":50,"wisdom":50},"businesses":[],"career":{"highestTierAchieved":"entry","history":[],"pendingApplications":[{"contested":false,"jobId":"job-accountant","outcome":"pending","resolvesWeek":4,"submittedWeek":3}],"totalWeeksEmployed":0},"counters":{"action_apply_for_job":3,"action_attend_class":2,"action_enroll_course":10,"action_search_for_work":5,"action_study":4,"education_course_completed":1,"education_credential_awarded":1,"education_skill_awarded":1,"eviction_advanced":1,"need_drift":15,"rent_charged":3,"rent_overdue":2},"currentLocationId":"home","education":{"completedCourseIds":["course-bookkeeping"],"credentials":[{"awardedWeek":2,"courseId":"course-bookkeeping","id":"course-bookkeeping-credential-2","labelKey":"stable-life.course.bookkeeping.name","level":"certificate"}],"enrollments":[{"attendedUnits":2,"courseId":"course-bookkeeping","missedSessions":0,"retainedProgress":0,"startedWeek":1,"status":"completed","studyUnits":2,"tuitionOutstandingCents":0,"tuitionPaidCents":10000,"weeksCompleted":2}],"failedCourseIds":[]},"finances":{"accounts":[],"cashCents":-5000,"debtCents":0,"overdueBalanceCents":0,"savingsCents":0,"weeklyExpensesCents":0,"weeklyIncomeCents":0},"flags":{"attendedClass:course-bookkeeping":false},"housing":{"damage":0,"definitionId":"housing-default","depositPaidCents":0,"evictionStage":"warning","missedPayments":1,"movedInWeek":1,"overdueRentCents":5500,"ownership":"renting","rentDueWeek":1,"weeklyCostCents":5000},"identity":{"actorId":"player","age":28,"backgroundId":"background-default","name":"Alex"},"inventory":[],"needs":{"energy":41,"happiness":54,"health":77,"satiety":68,"stress":26},"projects":[],"relationships":[],"reputation":{},"skills":{"bookkeeping":50},"traits":[]},"resolution":null,"scheduledEvents":[],"world":{"agents":[],"chainStates":[],"eventCooldowns":{},"firedUniqueEvents":[],"flags":{},"headlinePool":{"cyclesCompleted":0,"remainingIds":[]},"jobMarket":{"openings":[{"contested":false,"jobId":"job-cashier","postedWeek":3},{"contested":false,"jobId":"job-accountant","postedWeek":3}]},"locations":[],"npcs":[],"strangenessBase":0}},"seed":"stable-life-education-seed","status":"active"}} diff --git a/src/engine/fixtures/saves/simulation-ended.json b/src/engine/fixtures/saves/simulation-ended.json new file mode 100644 index 0000000..16c24ea --- /dev/null +++ b/src/engine/fixtures/saves/simulation-ended.json @@ -0,0 +1 @@ +{"campaignId":"stable-life","campaignVersion":"1.0.0","checksum":"ea7c805b70c7e97366c3c9512a690c0a68cddf86350b353328e46f638b700408","engineVersion":"0.10.0","kindId":"simulation","kindVersion":"1.0.0","replayCompatible":true,"saveFormatVersion":1,"serializationVersion":1,"state":{"actionLog":[{"actionId":"plan.add","params":{"actionType":"rest"},"seq":0},{"actionId":"end_week","seq":1},{"actionId":"plan.add","params":{"actionType":"rest"},"seq":2},{"actionId":"end_week","seq":3},{"actionId":"plan.add","params":{"actionType":"rest"},"seq":4},{"actionId":"end_week","seq":5}],"campaignId":"stable-life","campaignVersion":"1.0.0","formatVersion":1,"gameId":"fixed-game-id","kindId":"simulation","kindState":{"activeEffects":[],"activeOpportunities":[],"calendar":{"committedTimeUnits":0,"currentWeek":4,"currentYear":1,"spentTimeUnits":0,"totalTimeUnits":14},"economy":{"flags":{},"inflation":200,"interestRate":300,"marketPrices":{},"publishedIndicators":[],"sectorDemand":{},"unemploymentRate":500},"goals":[{"completedWeek":3,"consecutiveWeeksSatisfied":2,"definitionId":"goal-well-rested","firstSatisfiedWeek":2,"progressNotes":[],"satisfiedThisWeek":true,"status":"completed"}],"pendingEventResponses":[],"plan":{"actions":[],"week":4},"player":{"attributes":{"charisma":50,"creativity":50,"discipline":50,"intelligence":50,"luck":50,"resilience":50,"wisdom":50},"businesses":[],"career":{"highestTierAchieved":"entry","history":[],"pendingApplications":[],"totalWeeksEmployed":0},"counters":{"action_rest":6,"need_drift":15,"rent_charged":3},"currentLocationId":"home","education":{"completedCourseIds":[],"credentials":[],"enrollments":[],"failedCourseIds":[]},"finances":{"accounts":[],"cashCents":5000,"debtCents":0,"overdueBalanceCents":0,"savingsCents":0,"weeklyExpensesCents":0,"weeklyIncomeCents":0},"flags":{},"housing":{"damage":0,"definitionId":"housing-default","depositPaidCents":0,"evictionStage":"none","missedPayments":0,"movedInWeek":1,"overdueRentCents":0,"ownership":"renting","rentDueWeek":1,"weeklyCostCents":5000},"identity":{"actorId":"player","age":28,"backgroundId":"background-default","name":"Alex"},"inventory":[],"needs":{"energy":97,"happiness":54,"health":77,"satiety":68,"stress":11},"projects":[],"relationships":[],"reputation":{},"skills":{"bookkeeping":0},"traits":[]},"resolution":{"goalsFailed":[],"goalsMet":["goal-well-rested"],"resolution":"goals_met","resolvedAtWeek":3},"scheduledEvents":[],"world":{"agents":[],"chainStates":[],"eventCooldowns":{},"firedUniqueEvents":[],"flags":{},"headlinePool":{"cyclesCompleted":0,"remainingIds":[]},"jobMarket":{"openings":[]},"locations":[],"npcs":[],"strangenessBase":0}},"seed":"stable-life-win-seed","status":"ended"}} diff --git a/src/engine/fixtures/saves/story-graph-active.json b/src/engine/fixtures/saves/story-graph-active.json new file mode 100644 index 0000000..8ec6880 --- /dev/null +++ b/src/engine/fixtures/saves/story-graph-active.json @@ -0,0 +1 @@ +{"campaignId":"bulgaria-bureaucracy","campaignVersion":"2.0.0","checksum":"04be988b67541cd902ab769a3c89059b077677828c50f28d81032f393ebeb58e","engineVersion":"0.10.0","kindId":"story-graph","kindVersion":"1.0.0","replayCompatible":true,"saveFormatVersion":1,"serializationVersion":1,"state":{"actionLog":[{"actionId":"wait","seq":0},{"actionId":"registry_route_listen","seq":1}],"campaignId":"bulgaria-bureaucracy","campaignVersion":"2.0.0","formatVersion":1,"gameId":"fixed-game-id","kindId":"story-graph","kindState":{"currentNodeId":"registry_route_event_1a","turn":3,"unlockedAchievements":["found_registry_route_event_1a"],"variables":{"connections":1,"preparation":1,"pressure":0,"remembered_archive_route":false,"remembered_registry_route":true,"remembered_supervisor_route":false,"route":"registry_route"},"visitedCounts":{"municipality":1,"registry_route_1":1,"registry_route_event_1":1,"registry_route_event_1a":1}},"seed":"bureaucracy-seed-1","status":"active"}} diff --git a/src/engine/fixtures/saves/story-graph-ended.json b/src/engine/fixtures/saves/story-graph-ended.json new file mode 100644 index 0000000..87870bd --- /dev/null +++ b/src/engine/fixtures/saves/story-graph-ended.json @@ -0,0 +1 @@ +{"campaignId":"bulgaria-bureaucracy","campaignVersion":"2.0.0","checksum":"3e26b8703cf0e090a7be428fde7c9788c30594cc752713f881ad20d5b9038b49","engineVersion":"0.10.0","kindId":"story-graph","kindVersion":"1.0.0","replayCompatible":true,"saveFormatVersion":1,"serializationVersion":1,"state":{"actionLog":[{"actionId":"wait","seq":0},{"actionId":"registry_route_listen","seq":1},{"actionId":"registry_route_event_1a_proceed","seq":2},{"actionId":"registry_route_prepare","seq":3},{"actionId":"registry_route_event_2a_proceed","seq":4},{"actionId":"registry_route_steady","seq":5},{"actionId":"choose_document_obtained","seq":6}],"campaignId":"bulgaria-bureaucracy","campaignVersion":"2.0.0","formatVersion":1,"gameId":"fixed-game-id","kindId":"story-graph","kindState":{"currentNodeId":"ending_document_obtained","endingId":"document_obtained","turn":9,"unlockedAchievements":["found_registry_route_event_1a","found_registry_route_event_2a","it_builds_character"],"variables":{"connections":2,"preparation":3,"pressure":0,"remembered_archive_route":false,"remembered_registry_route":true,"remembered_supervisor_route":false,"route":"registry_route"},"visitedCounts":{"ending_document_obtained":1,"municipality":1,"registry_route_1":1,"registry_route_2":1,"registry_route_3":1,"registry_route_4":1,"registry_route_event_1":1,"registry_route_event_1a":1,"registry_route_event_2":1,"registry_route_event_2a":1}},"seed":"bureaucracy-seed-1","status":"ended"}} diff --git a/src/engine/fixtures/saves/world-graph-active.json b/src/engine/fixtures/saves/world-graph-active.json new file mode 100644 index 0000000..fa1b12b --- /dev/null +++ b/src/engine/fixtures/saves/world-graph-active.json @@ -0,0 +1 @@ +{"campaignId":"world-graph-mvp","campaignVersion":"1.0.0","checksum":"bedb3fc78a060cb61fa800d868a25870d7d97101c268f7c8bf07b5ae86bac5d4","engineVersion":"0.10.0","kindId":"world-graph","kindVersion":"1.0.0","replayCompatible":true,"saveFormatVersion":1,"serializationVersion":1,"state":{"actionLog":[{"actionId":"build","params":{"definitionId":"stall","rotation":0,"x":3,"y":1},"seq":0},{"actionId":"hire_staff","params":{"definitionId":"restocker"},"seq":1},{"actionId":"advance_ticks","params":{"ticks":6},"seq":2}],"campaignId":"world-graph-mvp","campaignVersion":"1.0.0","formatVersion":1,"gameId":"fixed-game-id","kindId":"world-graph","kindState":{"activePolicyIds":[],"alerts":[{"clearedAtTick":null,"dismissedAtTick":null,"entityId":"incident:7","id":"alert:8","issuedAtTick":3,"messageKey":"world-graph-mvp.incident-litter.description","semanticKey":"incident:incident:7","severity":"warning","titleKey":"world-graph-mvp.incident-litter.name","type":"incident_active"}],"buildings":[{"buildStartTick":0,"cleanliness":77,"definitionId":"kiosk","height":1,"id":"building:0","inventory":{"water":null},"pricesCents":{"water":100},"queue":{"guestIds":[],"id":"queue:1","serviceStartedAtTick":null},"rotation":0,"status":"open","wear":90,"width":1,"x":1,"y":1},{"buildStartTick":0,"cleanliness":100,"definitionId":"stall","height":1,"id":"building:2","inventory":{"water":3},"pricesCents":{"water":100},"queue":{"guestIds":[],"id":"queue:3","serviceStartedAtTick":null},"rotation":0,"status":"open","wear":100,"width":1,"x":3,"y":1}],"constructionSites":[],"counters":{"buildingsCompleted":0,"guestsDeparted":0,"guestsDissatisfied":0,"guestsEntered":1,"incidentsRaised":1,"litterCleaned":0,"litterCreated":1,"servicesCompleted":1},"failures":[{"id":"bankrupt","satisfiedSinceTick":null,"state":"active","updatedAtTick":5}],"finances":{"cashCents":1649,"expensesTodayCents":51,"expensesTotalCents":451,"loan":null,"revenueTodayCents":100,"revenueTotalCents":100},"guests":[{"archetypeId":"guest","cashCents":0,"conditions":{},"drawCount":6,"id":"guest:5","intent":{"exit":{"x":4,"y":1},"kind":"leave","reason":"unreachable","selectedAtTick":5},"lastServedTick":3,"lifecycle":"seeking","needs":{"thirst":44},"opinions":{"price":0},"path":[{"x":1,"y":0},{"x":2,"y":0},{"x":3,"y":0},{"x":4,"y":0},{"x":4,"y":1}],"pathIndex":1,"patienceCapacityTicks":5,"patienceRemainingTicks":3,"preferences":{},"satisfaction":50,"spentTicks":6,"stayDurationTicks":10,"tickEntered":0,"x":2,"y":0}],"incidents":[{"amount":1,"buildingId":"building:0","definitionId":"litter","expiresAtTick":null,"guestId":null,"id":"incident:7","position":{"x":0,"y":1},"resolvedAtTick":null,"startedAtTick":3,"zoneId":null}],"map":{"exits":[{"x":4,"y":1}],"height":3,"paths":[{"allowed":true,"edgeCost":0,"from":{"x":0,"y":0},"to":{"x":1,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":0,"y":0},"to":{"x":0,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":0},"to":{"x":0,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":0},"to":{"x":2,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":0},"to":{"x":1,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":0},"to":{"x":1,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":0},"to":{"x":3,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":0},"to":{"x":2,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":0},"to":{"x":2,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":0},"to":{"x":4,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":0},"to":{"x":3,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":4,"y":0},"to":{"x":3,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":4,"y":0},"to":{"x":4,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":0,"y":1},"to":{"x":0,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":0,"y":1},"to":{"x":1,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":0,"y":1},"to":{"x":0,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":1},"to":{"x":1,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":1},"to":{"x":0,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":1},"to":{"x":2,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":1},"to":{"x":1,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":1},"to":{"x":2,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":1},"to":{"x":1,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":1},"to":{"x":3,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":1},"to":{"x":2,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":1},"to":{"x":3,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":1},"to":{"x":2,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":1},"to":{"x":4,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":1},"to":{"x":3,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":4,"y":1},"to":{"x":4,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":4,"y":1},"to":{"x":3,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":4,"y":1},"to":{"x":4,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":0,"y":2},"to":{"x":0,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":0,"y":2},"to":{"x":1,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":2},"to":{"x":1,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":2},"to":{"x":0,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":2},"to":{"x":2,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":2},"to":{"x":2,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":2},"to":{"x":1,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":2},"to":{"x":3,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":2},"to":{"x":3,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":2},"to":{"x":2,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":2},"to":{"x":4,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":4,"y":2},"to":{"x":4,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":4,"y":2},"to":{"x":3,"y":2}}],"revision":1,"scenery":[],"spawnPoints":[{"x":0,"y":1}],"terrain":[{"terrainId":"sand","x":0,"y":0},{"terrainId":"sand","x":1,"y":0},{"terrainId":"sand","x":2,"y":0},{"terrainId":"sand","x":3,"y":0},{"terrainId":"sand","x":4,"y":0},{"terrainId":"sand","x":0,"y":1},{"terrainId":"sand","x":1,"y":1},{"terrainId":"sand","x":2,"y":1},{"terrainId":"sand","x":3,"y":1},{"terrainId":"sand","x":4,"y":1},{"terrainId":"sand","x":0,"y":2},{"terrainId":"sand","x":1,"y":2},{"terrainId":"sand","x":2,"y":2},{"terrainId":"sand","x":3,"y":2},{"terrainId":"sand","x":4,"y":2}],"width":5,"zones":[]},"nextEntityOrdinal":9,"objectives":[{"id":"clean-litter","satisfiedSinceTick":null,"state":"active","target":1,"updatedAtTick":5,"value":0}],"resolution":null,"staff":[{"assignedBuildingId":null,"assignedZoneId":null,"drawCount":0,"id":"staff:4","moveProgressTicks":0,"path":[{"x":4,"y":1},{"x":4,"y":0},{"x":3,"y":0},{"x":2,"y":0},{"x":2,"y":1}],"pathIndex":4,"roleId":"restocker","status":"idle","task":null,"tasksCompleted":1,"x":2,"y":1}],"tick":6,"unlockedAchievementIds":[],"unlockedContent":[{"id":"hut","kind":"building"},{"id":"kiosk","kind":"building"},{"id":"stall","kind":"building"},{"id":"builder","kind":"staff_role"},{"id":"cleaner","kind":"staff_role"},{"id":"restocker","kind":"staff_role"}]},"seed":"world-graph-mvp-restock-seed","status":"active"}} diff --git a/src/engine/fixtures/saves/world-graph-ended.json b/src/engine/fixtures/saves/world-graph-ended.json new file mode 100644 index 0000000..e317795 --- /dev/null +++ b/src/engine/fixtures/saves/world-graph-ended.json @@ -0,0 +1 @@ +{"campaignId":"world-graph-mvp","campaignVersion":"1.0.0","checksum":"0315fe97266f1c2db297d3a0cb45771004b871cc4c4e70e8ee631814a5697d47","engineVersion":"0.10.0","kindId":"world-graph","kindVersion":"1.0.0","replayCompatible":true,"saveFormatVersion":1,"serializationVersion":1,"state":{"actionLog":[{"actionId":"hire_staff","params":{"definitionId":"cleaner"},"seq":0},{"actionId":"advance_ticks","params":{"ticks":10},"seq":1}],"campaignId":"world-graph-mvp","campaignVersion":"1.0.0","formatVersion":1,"gameId":"fixed-game-id","kindId":"world-graph","kindState":{"activePolicyIds":[],"alerts":[{"clearedAtTick":9,"dismissedAtTick":null,"entityId":"incident:4","id":"alert:6","issuedAtTick":3,"messageKey":"world-graph-mvp.incident-litter.description","semanticKey":"incident:incident:4","severity":"warning","titleKey":"world-graph-mvp.incident-litter.name","type":"incident_active"},{"clearedAtTick":null,"dismissedAtTick":null,"entityId":null,"id":"alert:8","issuedAtTick":9,"messageKey":"world-graph.alert.scenario-resolved.message","semanticKey":"scenario-resolved","severity":"info","titleKey":"world-graph.alert.scenario-resolved.title","type":"scenario_resolved"}],"buildings":[{"buildStartTick":0,"cleanliness":79,"definitionId":"kiosk","height":1,"id":"building:0","inventory":{"water":null},"pricesCents":{"water":100},"queue":{"guestIds":[],"id":"queue:1","serviceStartedAtTick":null},"rotation":0,"status":"open","wear":90,"width":1,"x":1,"y":1}],"constructionSites":[],"counters":{"buildingsCompleted":0,"guestsDeparted":1,"guestsDissatisfied":0,"guestsEntered":2,"incidentsRaised":1,"litterCleaned":1,"litterCreated":1,"servicesCompleted":1},"failures":[{"id":"bankrupt","satisfiedSinceTick":null,"state":"active","updatedAtTick":9}],"finances":{"cashCents":1847,"expensesTodayCents":53,"expensesTotalCents":253,"loan":null,"revenueTodayCents":100,"revenueTotalCents":100},"guests":[{"archetypeId":"guest","cashCents":100,"conditions":{},"drawCount":6,"id":"guest:7","intent":{"buildingId":"building:0","kind":"seek_service","productId":"water","selectedAtTick":9},"lastServedTick":null,"lifecycle":"seeking","needs":{"thirst":49},"opinions":{"price":0},"path":[{"x":0,"y":1}],"pathIndex":0,"patienceCapacityTicks":5,"patienceRemainingTicks":0,"preferences":{},"satisfaction":50,"spentTicks":1,"stayDurationTicks":10,"tickEntered":9,"x":0,"y":1}],"incidents":[{"amount":0,"buildingId":"building:0","definitionId":"litter","expiresAtTick":null,"guestId":null,"id":"incident:4","position":{"x":0,"y":1},"resolvedAtTick":9,"startedAtTick":3,"zoneId":null}],"map":{"exits":[{"x":4,"y":1}],"height":3,"paths":[{"allowed":true,"edgeCost":0,"from":{"x":0,"y":0},"to":{"x":1,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":0,"y":0},"to":{"x":0,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":0},"to":{"x":0,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":0},"to":{"x":2,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":0},"to":{"x":1,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":0},"to":{"x":1,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":0},"to":{"x":3,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":0},"to":{"x":2,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":0},"to":{"x":2,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":0},"to":{"x":4,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":0},"to":{"x":3,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":4,"y":0},"to":{"x":3,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":4,"y":0},"to":{"x":4,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":0,"y":1},"to":{"x":0,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":0,"y":1},"to":{"x":1,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":0,"y":1},"to":{"x":0,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":1},"to":{"x":1,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":1},"to":{"x":0,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":1},"to":{"x":2,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":1},"to":{"x":1,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":1},"to":{"x":2,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":1},"to":{"x":1,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":1},"to":{"x":3,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":1},"to":{"x":2,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":1},"to":{"x":3,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":1},"to":{"x":2,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":1},"to":{"x":4,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":1},"to":{"x":3,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":4,"y":1},"to":{"x":4,"y":0}},{"allowed":true,"edgeCost":0,"from":{"x":4,"y":1},"to":{"x":3,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":4,"y":1},"to":{"x":4,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":0,"y":2},"to":{"x":0,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":0,"y":2},"to":{"x":1,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":2},"to":{"x":1,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":2},"to":{"x":0,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":1,"y":2},"to":{"x":2,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":2},"to":{"x":2,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":2},"to":{"x":1,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":2,"y":2},"to":{"x":3,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":2},"to":{"x":3,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":2},"to":{"x":2,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":3,"y":2},"to":{"x":4,"y":2}},{"allowed":true,"edgeCost":0,"from":{"x":4,"y":2},"to":{"x":4,"y":1}},{"allowed":true,"edgeCost":0,"from":{"x":4,"y":2},"to":{"x":3,"y":2}}],"revision":0,"scenery":[],"spawnPoints":[{"x":0,"y":1}],"terrain":[{"terrainId":"sand","x":0,"y":0},{"terrainId":"sand","x":1,"y":0},{"terrainId":"sand","x":2,"y":0},{"terrainId":"sand","x":3,"y":0},{"terrainId":"sand","x":4,"y":0},{"terrainId":"sand","x":0,"y":1},{"terrainId":"sand","x":1,"y":1},{"terrainId":"sand","x":2,"y":1},{"terrainId":"sand","x":3,"y":1},{"terrainId":"sand","x":4,"y":1},{"terrainId":"sand","x":0,"y":2},{"terrainId":"sand","x":1,"y":2},{"terrainId":"sand","x":2,"y":2},{"terrainId":"sand","x":3,"y":2},{"terrainId":"sand","x":4,"y":2}],"width":5,"zones":[]},"nextEntityOrdinal":9,"objectives":[{"id":"clean-litter","satisfiedSinceTick":9,"state":"met","target":1,"updatedAtTick":9,"value":1}],"resolution":{"failureId":null,"objectiveIds":["clean-litter"],"resolution":"objectives_met","resolvedAtTick":9},"staff":[{"assignedBuildingId":null,"assignedZoneId":null,"drawCount":0,"id":"staff:2","moveProgressTicks":0,"path":[{"x":4,"y":1},{"x":4,"y":0},{"x":3,"y":0},{"x":2,"y":0},{"x":1,"y":0},{"x":0,"y":0},{"x":0,"y":1}],"pathIndex":6,"roleId":"cleaner","status":"idle","task":null,"tasksCompleted":1,"x":0,"y":1}],"tick":10,"unlockedAchievementIds":[],"unlockedContent":[{"id":"hut","kind":"building"},{"id":"kiosk","kind":"building"},{"id":"stall","kind":"building"},{"id":"builder","kind":"staff_role"},{"id":"cleaner","kind":"staff_role"},{"id":"restocker","kind":"staff_role"}]},"seed":"world-graph-mvp-win-seed","status":"ended"}} diff --git a/src/engine/scripts/capture-compat-baseline.test.ts b/src/engine/scripts/capture-compat-baseline.test.ts new file mode 100644 index 0000000..9f0b1c1 --- /dev/null +++ b/src/engine/scripts/capture-compat-baseline.test.ts @@ -0,0 +1,27 @@ +/** + * W104.2 — every 0.10 built-in campaign registry still produces the same canonical runtime + * content (initial `Scene` — projection, available actions, status — and Tier-1/2 findings) + * as its recorded `v0.10.0` baseline (`fixtures/compat/v0.10.0/*.json`, captured by this same + * script — `capture-compat-baseline.ts` header — run against that tag). + * + * None of these campaigns opt into any field W93-W103 added (projects, businesses, event + * chains, ...); they predate the additive programme, so this is the "fields omitted" half of + * the criterion by construction, not by a separate toggle — there is no field to omit that + * these fixtures ever populated. A future built-in campaign that *does* opt in belongs in its + * own comparison, not this one. + */ + +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { describe, it, expect } from "vitest"; +import { ENTRIES, captureEntry } from "./capture-compat-baseline.js"; + +const BASELINE_DIR = fileURLToPath(new URL("../fixtures/compat/v0.10.0/", import.meta.url)); + +describe("W104.2 compatibility baseline — v0.10.0 vs HEAD", () => { + it.each(ENTRIES)("$label: matches the committed v0.10.0 baseline", async (entry) => { + const baseline = JSON.parse(readFileSync(`${BASELINE_DIR}${entry.label}.json`, "utf8")); + const captured = await captureEntry(entry); + expect(captured).toEqual(baseline); + }); +}); diff --git a/src/engine/scripts/capture-compat-baseline.ts b/src/engine/scripts/capture-compat-baseline.ts new file mode 100644 index 0000000..30d5f79 --- /dev/null +++ b/src/engine/scripts/capture-compat-baseline.ts @@ -0,0 +1,143 @@ +/** + * W104.2 — captures, per built-in campaign registry, the canonical runtime content an 0.11 + * candidate must still reproduce: the initial `Scene` (bundling projection, available + * actions, and status) a fixed seed/`IdSource` produces, plus the registry's own Tier-1/2 + * validation findings. + * + * Run once per side of a compatibility comparison — at a baseline tag to freeze + * `fixtures/compat/