890. Lesson identity
891. Learning objective
After this lesson, you can write a load contract, implement a bounded restoration path, and validate an AI-assisted persistence change against explicit rules for source validation, migration, sequencing, commit-time failure, and final system state.
892. Why this matters
A parsed save file is not yet safe game state. Profile, Inventory, and Loadout may own different facts, while the HUD only presents their confirmed state. If one owner changes and a later owner rejects restoration, the game can contain a mixture of old and new data even though the loader reports an error.
AI can draft a loader, but it cannot decide the required ownership order or failure contract for you. Those decisions must be written before implementation and verified against the complete diff and observable final state.
893. Starter path
Use the instructor-provided runnable persistence harness when your project does not already expose a suitable load path. The harness contains:
- bounded
Profile,Inventory,Loadout, andHUDinterfaces; - current-version and version-1 text fixtures;
- malformed and cross-field-invalid fixtures;
- a restoration fault switch selected by test input or configuration rather than a specific UI;
- before-load and after-load state tables;
- contract, changed-file review, and acceptance-result templates.
You may operate the harness through any accessible editor, test runner, command interface, or instructor-provided control surface. The required evidence is the same state transition and review record, not a particular input method.
894. Core concept: two validation levels before restoration
A migration must not consume an arbitrary parsed object, but an older payload also cannot be required to satisfy the current schema before migration. Use two validation levels:
- Parse and validate the source envelope. Parse without mutating game systems. Validate the version discriminator and the minimum schema required for that declared source version. Reject malformed data, unknown versions, or source fields that the migration cannot safely read.
- Migrate without mutation. Transform a supported older payload into a new in-memory value. Do not write into
Profile,Inventory,Loadout, orHUD, and do not mutate the original parsed payload. - Validate the normalized current payload. Validate all current required fields, types, ranges, and cross-field invariants. For example, every
activeLoadoutitem must satisfy the contract's ownership rule. - Plan, preflight, and commit restoration. Build an ordered restoration plan. Ask systems whether they can accept it where their interfaces support preflight checks. Then apply one explicit commit-time failure strategy.
- Rebuild and report. Rebuild the
HUDonly from the final confirmed gameplay state and report the outcome.
895. Restoration pipeline
| Phase | Gate question | Evidence | Failure response |
|---|---|---|---|
| Parse and source validation | Is the version known, and does the payload satisfy the minimum schema for that version? | Parsed version plus source-schema result | Reject without mutation. |
| Non-mutating migration | Can the supported source payload be transformed into a current candidate? | New normalized candidate and migration result | Reject without mutation. |
| Current validation | Does the complete candidate satisfy the current schema and invariants? | Field, range, required-value, and cross-field results | Reject without mutation. |
| Plan and preflight | Is the ordered change set coherent, and can each owner accept it where preflight is available? | Restoration plan and preflight results | Reject without mutation. |
| Commit | Can all intended owners reach one consistent result? | Commit trace plus final states | Complete atomically, roll back, or enter the defined safe state. |
| Rebuild and report | Does presentation reflect the final confirmed gameplay state? | HUD projection and outcome |
Never display an unconfirmed mixture. |
A validation failure and a commit-time failure are different:
- Validation or preflight failure: no game system has changed, so rejection before mutation is sufficient.
- Commit-time failure: at least one owner may already have changed. Merely returning an error is insufficient; the loader must restore consistency according to the contract.
896. Commit-time consistency strategies
Choose one strategy supported by the bounded implementation:
1. Atomic staged commit
Each owner prepares candidate state without publishing it. The loader publishes all prepared states as one commit. If preparation fails, nothing is published.
2. Snapshot and rollback
Before committing, capture the specific pre-load state needed to restore every owner that may change. Apply in order. If Inventory or Loadout fails, restore all previously changed owners from their snapshots, then rebuild the HUD from the restored pre-load state.
3. Defined safe-state transition
If atomic commit and reliable rollback are unavailable, define one complete safe state in the contract. On commit failure, transition every relevant owner to that state and rebuild the HUD from it. A safe state must specify the final values or reset rules for Profile, Inventory, and Loadout; “show an error” is not a state definition.
A repair policy for inconsistent save data is separate from commit recovery. Any repair must occur on the normalized in-memory candidate, be documented, and pass current-schema validation before restoration begins.
897. Concrete example
Current version 2 uses this shape:
{
"version": 2,
"profile": { "credits": 120, "rank": 3 },
"ownedItems": ["scanner"],
"activeLoadout": ["scanner"]
}
A safe version-1 path is:
- Parse the text.
- Confirm that
versionis present and equals the supported value1. - Validate only the documented version-1 fields and types required by the migration.
- Produce a separate version-2 candidate in memory.
- Validate the entire version-2 candidate and its cross-field invariants.
- Build and preflight the ordered plan for
Profile,Inventory, andLoadout. - Commit using the selected consistency strategy.
- Rebuild the
HUDfrom the resulting confirmed state.
If the candidate contains "grappler" in activeLoadout but not in ownedItems, the loader must follow the written rule: reject the candidate or apply a documented repair before current validation completes. It must not silently grant ownership merely to make presentation look consistent.
898. Failure trace
Suppose the pre-load state is:
| System | Before load |
|---|---|
Profile |
credits 20, rank 1 |
Inventory |
scanner owned |
Loadout |
scanner active |
HUD |
displays credits 20 and scanner |
The candidate requests credits 120, scanner and grappler ownership, and grappler active. Profile commits, but the fault switch makes Loadout fail.
The test does not pass merely because the loader reports failure. It must assert the final state of all four systems:
- Under rollback,
Profile,Inventory, andLoadoutmatch the complete pre-load state, and theHUDis rebuilt from that restored state. - Under a defined safe-state transition, all three gameplay systems match the safe-state contract, and the
HUDreflects that state. - Under an atomic staged commit, none of the candidate state becomes visible.
A final mixture such as new Profile, old Loadout, and a refreshed HUD is contradictory and fails the contract.
899. AI-native workflow
- Complete the concise contract template: supported versions, source schemas, migration output, current invariants, owner order, consistency strategy, safe-state or rollback definition, and final-state assertions.
- Give the AI only the loader entry point, bounded system interfaces, fixtures, allowed files, and acceptance checks.
- Require a proposed changed-file list and assumptions before editing.
- Ask for a non-mutating migration and an explicit restoration plan. Do not accept field-by-field mutation during parsing or migration.
- Require the implementation to expose the provided configuration-driven fault switch for
InventoryorLoadoutrestoration. - Inspect the complete diff using the checklist: intended files only, no hidden ownership creation, no presentation data treated as durable state, no mutation before normalized validation, and no error path that leaves an unspecified partial state.
- Give every changed file a one-sentence rationale tied to the brief. Reject or revert unsupported changes.
900. Guided practice
Use the starter harness or an equivalent existing path.
- Fill in the load-contract template. Choose rollback, atomic staged commit, or a fully specified safe-state transition for commit-time failures.
- Define the minimum version-1 source schema and the complete version-2 schema. Write the non-mutating transformation between them.
- Define the ownership invariant for
activeLoadoutandownedItems. - Implement or review the sequence: parse, source validation, migration, current validation, plan, preflight where available, commit,
HUDrebuild, and outcome report. - Review the complete diff and record a rationale for every changed file.
- Run these acceptance cases using the supplied text fixtures:
- valid current payload;
- valid supported version-1 payload;
- malformed text or missing version discriminator;
- source-version payload that does not satisfy its minimum migration schema;
- normalized payload with inconsistent ownership;
- forced
InventoryorLoadoutcommit failure.
- For every case, complete an expected-versus-observed table for
Profile,Inventory,Loadout,HUD, and the reported outcome. - Document one proposed change rejected for exceeding the brief.
901. Evidence
Submit the load contract, bounded AI brief, implementation artifact, complete diff review, changed-file rationales, and acceptance table. The evidence must show:
- source-version validation before migration;
- a non-mutating migration;
- complete current-schema and invariant validation before restoration;
- an ordered restoration plan and preflight where supported;
- a concrete atomic, rollback, or safe-state mechanism for commit-time failure;
- final-state assertions for
Profile,Inventory,Loadout, andHUDafter the forced failure; HUDreconstruction from final confirmed gameplay state;- one rejected out-of-scope proposal.
902. Key takeaways
- Validate the version discriminator and source-version shape before migration.
- Migrate into a separate in-memory candidate, then validate the complete current contract.
- Rejection before mutation handles validation failures, not failures after a system has committed.
- Commit-time failures require atomic publication, rollback of every changed owner, or a fully defined safe-state transition.
- Assert final system state, not only the reported error.
- Presentation systems should observe restored, confirmed gameplay state rather than serialized presentation values; this same observer boundary leads into the next module on audio.
903. Next lesson
Continue to 2.14 — Audio.
904. Knowledge check
Answer these items for yourself before reading the answers.
A loader parses a payload declaring version 1. What must happen before its migration reads version-1 fields?
Show answer and feedback
Answer: Validate the version discriminator and the minimum version-1 schema required by the migration
Why: The loader must establish a supported version and validate the minimum source shape that the migration will consume. It then migrates into a separate candidate and validates that candidate against the complete current schema.
Why should ownership normally be established before restoring a dependent loadout?
Show answer and feedback
Answer: The loadout can verify each selection against the state confirmed by Inventory
Why: Inventory provides the confirmed ownership state against which dependent selections can be checked. Loadout must not invent ownership to make its own data valid.
A normalized candidate contains an active item that is not owned. Which response respects the load contract?
Show answer and feedback
Answer: Apply the documented reject-or-repair rule before restoration and validate the resulting candidate
Why: The ownership rule belongs to normalized validation. A documented repair may transform the in-memory candidate, but the repaired candidate must pass validation before any owner is changed.
Trace: before loading, Profile has 20 credits, Inventory owns scanner, Loadout uses scanner, and the HUD shows both. During loading, Profile commits 120 credits and then Loadout fails. The contract requires rollback. Which evidence satisfies the contract?
Show answer and feedback
Answer: Profile returns to 20 credits, Inventory and Loadout match their complete pre-load states, the HUD is rebuilt from those states, and failure is reported
Why: A rollback contract is satisfied by the final state of every affected owner and presentation system, not merely by an error result. All changed owners must return to the defined pre-load state, and the HUD must be rebuilt from that confirmed state.