# Feature slice: saves, schema, migrations, and continuation The local playable world and the regional proof use different persistence boundaries. Both are primitive-only, versioned, deterministic, and validated before mutation. ## Local save boundary `SimulationStateRecord` is currently schema **v16**. It contains the local simulation seed/clock scope, village/NPC/animal/resource/storage/player/ combatant/faction records, ordered economic events, relationships, knowledge, legacy opportunities, situations, journal entries, commitments, and bounded semantic conversation acts. `SimulationManager.create_state_record()` creates it; `serialize_state()` emits canonical JSON; `restore_state()` preflights and then atomically restores dependent systems. Top-level v16 adds non-empty `world_id` and `location_id`. Activity success and blocked facts carry the same scope. A save recorded under another active adapter is rejected before clock, economy, NPC, or event state is mutated. This closes the context-reinterpretation hole where equal local coordinates could be treated as the same world. The record stores IDs, primitive values, event IDs, and semantic topic IDs. It never stores scripts, nodes, `NodePath`s, callables, imported assets, dialogue prose, or transient HUD/balloon state. ## Migration policy Nested records migrate at their own version; the top-level schema accepts an explicit historical range and normalizes old fields before cross-record validation. Current supported migrations include: - legacy village/storage and event streams; - directed relationships and causal knowledge provenance; - opportunity history and its v2 lifecycle; - animal routine/definition IDs; - player citizen needs/health; - combatants, factions, and enemy definition IDs; - situations, journal entries, commitments, and conversation acts; - world/location scope v16; - explicitly marked legacy unstructured `task_blocked` facts. Old definition values are not snapshotted. The current build supplies balance and handler definitions while mutable saved state is preserved. Unknown future schema versions fail closed until a migration exists. ## Validation invariants Restore checks include: - unique IDs and next-ID cursors above restored history; - valid definition/action/item/storage references; - actor/target/cost/effect contracts for activity facts; - animal reservations and feed event agreement; - relationship causes pointing at valid known events; - knowledge provenance and one-hop communication rules; - situation/journal/commitment references and statuses; - scope consistency for the event log and activity facts; - no future event tick or forged actor/source/destination fields. The test suite deliberately removes derived knowledge/opportunity state and tamper-tests facts, so validation does not rely on a secondary projection to catch corruption. ## Local file safety `SimulationSaveManifest` v1 combines the unchanged local v16 record and the canonical `RegionalSimulationFacade` envelope. It stores an independent checksum for each authority and cross-validates world, seed, active location, and processed tick. A rejected manifest cannot partially replace local or regional state. `SaveSlotStore` validates the complete manifest before replacement, restricts slot names and size, preserves a previous file, and can recover a backup after an interrupted replacement. It also accepts existing raw local v16 files and bootstraps an idle regional facade at the restored seed and tick. Explicit non-Jajce adapter scopes continue writing raw local v16 records until a matching regional facade is implemented. This fallback is permitted only while the regional facade equals its deterministic idle bootstrap; a local-only save fails closed rather than discarding live regional state. `F5`/`F9` use the current quicksave path in `main.tscn`. The player transform and presentation-only scene state are intentionally not saved. ## Regional persistence boundary `RegionalChunkedPersistence` and `RegionalChunkedFileStore` are the regional counterpart. They serialize a `RegionalCaravanService` envelope into global, location, mobile-group, scheduler, and event-segment chunks, with manifest and per-file checksums. The file store uses immutable generations, a current pointer, recovery scanning, and conservative pruning. Partial active-location loads are metadata-only and cannot silently resurrect a removed location. This regional chunk codec is not yet the backend for `SaveSlotStore`. The production facade and combined quicksave manifest are complete; retained raw events, rollups, chunk generations, and lazy context loading still need one atomic long-horizon publication contract. ## How to change state safely 1. Add the field to the narrow nested record first. 2. Define `SCHEMA_VERSION`, validation, canonical `to_dictionary()`, and an explicit migration from the prior version. 3. Update top-level construction/restore/checksum only after the nested record round-trips. 4. Add malformed/tamper, old-save migration, save/load, and deterministic continuation tests. 5. Update [SIMULATION_STATE_SCHEMA.md](SIMULATION_STATE_SCHEMA.md) and this guide with the actual version and compatibility range. The full field-by-field history remains in [Simulation State Schema](SIMULATION_STATE_SCHEMA.md).