Files
gamedev-the-steward/docs/REGIONAL_SIMULATION.md
T
2026-08-23 17:20:41 +02:00

199 lines
10 KiB
Markdown

# Regional simulation contract
> **Implementation status (2026-08-23):** The record, scheduler, caravan,
> chunk, file-generation, relevance, and navigation-budget contracts are
> implemented and tested. `SimulationManager` now owns a production regional
> facade for one inbound Jajce route, and `SaveSlotStore` persists local and
> regional authority in one checked manifest. The scale economy and chunked
> long-horizon history remain separate integrations.
The regional layer extends the Jajce simulation without multiplying scene-tree
managers. One authority owns every loaded and unloaded location, settlement,
route, mobile group, named person, population cohort, polity, scheduled job,
and objective world fact. A settlement or caravan scene is presentation, never
a second simulation.
This contract implements the direction accepted in
[ADR 0002](decisions/0002-emergent-world-content-and-narrative.md).
## Authoritative records
`RegionalWorldState` is a canonical, primitive-only aggregate of:
- `LocationStateRecord`: globally unique location identity and spatial context;
- `SettlementStateRecord`: location, polity, tier, founding group, stockpile,
and exact history references;
- `RouteStateRecord`: stable endpoints and simulation-time travel duration;
- `MobileGroupStateRecord`: location or route state, detailed members, cohort
members, cargo, capacity, and exact cargo/history facts;
- `PersonStateRecord`: a persistent named person at a location or in a group;
- `PopulationCohortRecord`: a conserved anonymous population count at a
location or in a group;
- `PolityStateRecord` and `DiplomaticRelationRecord`: sparse political
membership and pairwise state.
Every ID is unique across record families. Every address includes the world and
location, so equal local coordinates in different locations cannot create a
false witness, target, or relationship. Membership and route references are
validated transactionally before a replacement state is published. Canonical
serialization sorts record families and IDs, making checksums independent of
construction order.
The current local `SimulationStateRecord` remains the unchanged v16 Jajce
record. `SimulationSaveManifest` v1 combines that record with the canonical
`RegionalSimulationFacade` envelope and checksums both authorities. Regional
fields are not copied into local records or scene nodes.
## Deterministic work
`RegionalJobScheduler` orders jobs by:
```text
(due_tick, phase, entity_id, stable_sequence)
```
The queue and execution cursor are serialized together. Budgets may defer due
jobs but cannot reorder, drop, or change them. Tests require budgets of one,
eight, and unlimited jobs to produce the same ordered results and checksum,
including work scheduled by another job during execution.
`KeyedRandom` derives a decision from world seed, system ID, entity ID, and
occurrence. It does not consume a shared stream, so loading a context or
changing the job budget cannot alter another entity's result.
`AnalyticalRegionalUpdates` advances elapsed-tick linear, exponential, and
periodic processes without replaying every omitted tick. New distant systems
should schedule threshold/completion work or compute from `last_updated_tick`;
they should not add another global per-tick scan.
## First caravan proof
`RegionalCaravanService` is the first complete regional transaction. It owns a
single envelope containing regional state, the due-job queue, the indexed world
event store, and the next event ID.
Departure performs one atomic sequence:
1. validate group, route, endpoints, cargo availability, and capacity;
2. withdraw exact cargo from the origin settlement;
3. place that cargo in the mobile group;
4. put the group on the route with authoritative departure/arrival ticks;
5. record cargo-loaded and departed facts;
6. schedule one deduplicated arrival job.
Arrival atomically moves the group to the destination, transfers exact cargo
to its stockpile, records arrived and cargo-deposited facts, and cites the prior
fact in the causal chain. Any invalid reference, duplicate job, capacity
failure, event collision, or handler failure restores all affected authorities.
Presentation mode is intentionally absent from those decisions. Always-loaded,
never-loaded, and load/unload executions must preserve cargo, headcount, facts,
arrival tick, queue state, and checksum. Nearby visuals may interpolate route
progress; they do not report elapsed travel completion.
## Production facade and combined save
`RegionalSimulationFacade` is now owned by the production `SimulationManager`.
It bootstraps an authored Travnik-to-Jajce route and advances the same
`RegionalCaravanService` before each committed local tick. Its bounded public
command can depart one exact delivery; arrival remains scheduled simulation
work and does not depend on a loaded caravan visual.
The route transports `regional_trade_goods`, a catalogued item with no local
storage tag. Jajce starts with no regional stock and Travnik starts with four
units, so the facade cannot duplicate the playable pantry's food or the
woodpile's wood. Local and regional event-ID spaces remain deliberately
separate until an explicit cross-domain history contract exists.
`SimulationSaveManifest` stores exactly the local v16 record, its checksum, the
regional facade envelope, its checksum, and the manifest schema/version. It
rejects checksum tampering and mismatched world, seed, active location, or
tick. Combined restore is transactional across both authorities. Existing raw
local v16 quicksaves remain loadable and bootstrap an idle regional route at
the restored seed and tick. Explicit non-Jajce adapter scopes continue to use
their local v16 boundary until a matching regional facade exists. That fallback
is allowed only while the regional facade equals its deterministic idle
bootstrap; Jajce delivery commands are rejected outside the combined scope.
## Event retention and daily rollups
`WorldEventRetentionPlanner` is a pure, deterministic query over a complete
`WorldEventStore`. It returns a canonical `WorldEventRetentionPlan`; it never
deletes, replaces, or mutates source facts. The plan keeps the current day plus
the configured number of prior days raw. Only older event types explicitly
listed by the caller are eligible for aggregation.
An eligible transaction must have a stable world and location plus either one
positive `item_id`/`amount` pair or a positive multi-item `cargo_ledger`.
`WorldEventDailyRollupRecord` groups those contributions by day, world,
location, event type, and item. It retains the event count, total amount,
tick/event-ID bounds, and a checksum of the exact contributing event IDs.
`WorldEventDailyRollupStore` provides canonical serialization and indexed
day/location/type/item queries. Incomplete or ambiguous transaction payloads
remain raw rather than being guessed into a rollup.
Pinned fact IDs and caller-supplied causal fact IDs must exist in the source
store and always remain raw. Payload `cause_event_id` and `cause_event_ids`
references must resolve to prior facts; every referenced cause remains raw even
when its dependent transaction can be rolled up. Missing, dangling, or
non-prior causes fail the whole plan. Per-event reason traces, reciprocal causal
edges, source-ID fingerprints, and deterministic count telemetry survive the
plan's primitive-only round trip.
This is a planning foundation, not an active retention authority. A future
integration must collect external references from knowledge, relationships,
situations, commitments, journals, and regional history; verify the plan's
source checksum immediately before applying it; and publish the retained raw
store, rollup store, and chunk manifest in one atomic transaction. A rollup is
aggregate history and must never satisfy a consumer that requires an exact
ordinary world fact.
## Presentation relevance
`PresentationRelevancePolicy` is a pure query over an active context and
candidate descriptors. It selects at most 40 detailed visuals in stable order,
after rejecting foreign worlds, contexts, and locations. Explicit pins,
player-affecting threats, situation or commitment participants, leaders, and
unique owners are mandatory. If mandatory candidates exceed the limit, the
policy fails closed instead of silently erasing an important identity.
`PresentationNavRequestBudget` admits at most two new navigation requests per
frame. Both policies expose selected/rejected IDs and reason traces and do not
enter authoritative saves.
## Scale workload
The regional structural workload is fixed at five settlements, 20 caravans of
12 detailed members, 2,000 persistent named people, 50,000 aggregate
residents, and metadata for no more than 40 detailed active visuals. It proves
record validity, global-ID uniqueness, headcount/cargo conservation,
deterministic scheduler budgets, an empty final backlog, repeatable checksums,
and measured construction/execution/serialization time.
This is a headless data-and-scheduler baseline. It does not claim rendered
frame time, a complete market/economy, production-integrated chunked history
retention, or weak-PC GPU performance. Those claims require separate
active-world and hardware captures.
## Next integrations
Build in this order, keeping each step behind conservation and continuation
tests:
1. integrate the retention/rollup and chunk codecs as one atomic,
lazily loaded history transaction;
2. schedule 20 ordinary caravan trades across five settlement economies;
3. promote/demote presentation and population fidelity while pinning named and
causally referenced people;
4. add data-defined settlement projects, tier promotion, offices, diplomacy,
armies, campaigns, and aggregate conflict.
Context-owned world-target registries and the first shared player/NPC activity
command path are complete. Patrol and study now consume authored costs, mutate
authoritative metrics, and record scoped ordinary facts through one handler in
loaded and abstract execution.
Do not add city or kingdom special cases to `SimulationManager`. A new domain
mechanic adds one reusable record/handler and ordinary world facts; new content
combines existing definitions in `.tres` packs.