[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6JZWZMCQQRGY2DM98CF5RDA":3,"skill-same:01M6JZWZMCQQRGY2DM98CF5RDA":68},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":45,"commitSha":46,"skillDirectory":47,"forksCount":48,"repositoryHtmlUrl":49,"repositoryHomepageUrl":6,"ownerHtmlUrl":50,"ownerBlogUrl":51,"ownerDescription":52,"updatedAt":53,"install":54,"resources":67},"01M6JZWZMCQQRGY2DM98CF5RDA","audit-save",null,"Deep audit of the M45 save\u002Fload subsystem — full-ECS-snapshot capture, type-erased registry, format-version discipline, atomic disk write + ring, save-side refusal\u002Fvalidation gates, and the M45.1 live load-apply (cell reload + FormId-keyed deltas + player-pose restore)","matiaszanolli\u002FByroRedux","matiaszanolli","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F2379093?v=4","01M6JZJDYMPY01793W4XK157YG","Matias Zanolli","01M6JZJE2KE2BXSDPAYPQC6BSH",39,"MIT","https:\u002F\u002Fgithub.com\u002Fmatiaszanolli\u002FByroRedux\u002Fblob\u002Fmain\u002F.claude\u002Fcommands\u002Faudit-save\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"save","audit save","audit","depth","pose","load","restore","full","live","ecs","registry","snapshot","write","discipline","focus","dimensions","disk","player","deltas","deep","2026-11-10 10:12:30",false,"---\ndescription: \"Deep audit of the M45 save\u002Fload subsystem — full-ECS-snapshot capture, type-erased registry, format-version discipline, atomic disk write + ring, save-side refusal\u002Fvalidation gates, and the M45.1 live load-apply (cell reload + FormId-keyed deltas + player-pose restore)\"\nargument-hint: \"--focus \u003Cdimensions> --depth shallow|deep\"\n---\n\n# Save \u002F Load Subsystem Audit (M45 + M45.1)\n\nAudit `crates\u002Fsave` (full-ECS-snapshot format) and its sole live caller `byroredux\u002Fsrc\u002Fsave_io\u002F` (`save_io.rs` is a ~108-LOC re-export hub over `registry` \u002F `player` \u002F `commands` \u002F `live_load`, split in `d7c47d741`)\n(+ `save_io\u002F*_tests.rs`) for **data loss and save corruption**. The subsystem exists to remove Bethesda's\nslow-corruption tail by making the live ECS the single source of truth; verify the CODE delivers that.\nA silently dropped column, a stale schema guard, a torn capture, or a botched FormId remap **loses player\nprogress** — frame as CRITICAL\u002FHIGH per `_audit-severity.md` (data loss is CRITICAL).\n\n**Architecture**: Orchestrator; each dimension is a Task agent (max 3 concurrent). Read\n`.claude\u002Fcommands\u002F_audit-common.md` (layout, methodology, dedup, finding format) and\n`.claude\u002Fcommands\u002F_audit-severity.md` first; do not duplicate them here.\n\n## Scope\n\n**Crate** `crates\u002Fsave\u002Fsrc\u002F` (~3.3k LOC — read all): `lib.rs` (design intent docstring, `SaveError`),\n`snapshot.rs` (`Snapshot`, container header, `FORMAT_MAJOR` doc = the bump ledger, `encode`\u002F`decode`),\n`registry.rs` (`SaveRegistry`; `register_component` \u002F `register_replacing_component` \u002F\n`register_component_preserving` \u002F `register_resource` \u002F `register_form_id_component`; `ValidateFn`; `schema_fingerprint`), `driver.rs` (`save_world`,\n`restore_world`, `validate_snapshot_types`, `restore_resources`, `restore_resources_subset`,\n`build_form_id_remap`, `apply_deltas`), `disk.rs` (`write_slot`, `slots_by_recency`, `SaveRing`),\n`validate.rs` (`validate_world` + 6 sub-checks). Tests: `crates\u002Fsave\u002Ftests\u002Fround_trip.rs`.\n\n**Engine side** (`byroredux\u002Fsrc\u002Fsave_io\u002F`): `build_save_registry` (the curated type set — a scripting subsystem's\nsaved columns live in its own module's `register_saved(&mut registry)` (`crates\u002Fscripting\u002Fsrc\u002F*`, 45 modules, ~51 call\nsites in the hub; extracted in slices from #5802 `bde3478db` through `93bcda9bc`, then #6705 `15e498420` moved 47 more rows\nout of the hub; umbrella #5802 closed 2026-11-07). The hub keeps 39 rows, each with a stated reason: core types and the FormId column (`byroredux-core` sits\nbelow `byroredux-save`; `FactionReputation` #6759 `8fc1a9ef9` and `DismemberRecord` d50fc1153 are the two newest), the physics `CharacterController`, and the binary crate's own state. Each delegate is called at its\nrows' former position, because `schema_fingerprint` hashes rows in registration order — moving a delegate call is a\n`SchemaMismatch` for every existing save, and `the_schema_fingerprint_is_pinned` now fails it; where a module's columns sit\nin separate runs of the order it exports `register_saved_\u003Ccolumn>` too),\n`MUTABLE_DELTA_COLUMNS` (**second** hardcoded list driving the live overlay), `PRE_RELOAD_RESOURCES`,\n`SaveCommand`\u002F`SaveInfoCommand`\u002F`LoadCommand`, `PendingPlayerSaveActions` (F5\u002FF9\u002Fpause-menu ingress),\n`quickload_latest`, `execute_pending_save_loads` (+ `reload_interior_session`\u002F`reload_exterior_session`),\n`capture_player_pose`\u002F`apply_player_pose`, `validate_form_ids`, `validate_cinematic_entity_refs`.\nCross-cut: `byroredux\u002Fsrc\u002Fboot\u002Fregistries.rs` (installs registry\u002Fstate), `app_events.rs` (per-frame\norder: `capture_player_pose` → `step_player_save_actions` → `step_save_loads`), `app_step.rs`,\n`byroredux\u002Fsrc\u002Fnotifications.rs` (`PlayerNotifications`: bounded, transient, unsaved; drained in\n`app_frame.rs::render_one_frame`), `byroredux\u002Fsrc\u002Fextensions\u002F` (SDK extension-state capture\u002Fpreflight\u002F\nrestore layered around the save\u002Freload — the extension *API* is `\u002Faudit-tooling`),\n`byroredux\u002Fsrc\u002Fcell_loader\u002Freference_state.rs` (`PersistentReferenceStates`, `without_parked_state`),\n`cell_loader\u002F{transition,spawn,unload}.rs` (`unload.rs`: `purge_cinematic_retention_state`, the\n`player_owned_entities` teardown refusal), `crates\u002Fcore\u002Fsrc\u002F{atomic_file.rs,string\u002Fmod.rs,ecs\u002Fworld.rs}`,\n`crates\u002Fphysics\u002Fsrc\u002Fsync.rs`. Companion doc: `docs\u002Fengine\u002Fsave-load-roundtrip.md` (numbers the extension preflight\u002Frestore\nsteps as 1b\u002F3b).\n\n**Ownership split**: this skill owns schema, atomicity, validation, and load-apply *mechanics*. What loot \u002F\ninventory \u002F consumable state *should* persist (gameplay semantics) → `\u002Faudit-gameplay`; reconcile-after-\noverlay code lives in gameplay files but its **existence per removable column** is Dim 1's.\n\n## Guard ledger (E2) — run these first; aim dimensions at what they cannot see\n\n`cargo test -p byroredux-save` and `cargo test -p byroredux --bin byroredux save_io` (plus\n`app_step` \u002F `boot::schedule` sources named below). Only the four real-master tests in\n`save_io\u002Fconsumable_tests.rs` are `#[ignore]`d. Confirm each guard is live: not `#[ignore]`d, its scan\nfinds >0 items, and it fails when the invariant is broken.\n\n| Guard | Pins |\n|---|---|\n| `registry_completeness_tests::every_component_or_resource_impl_is_saved_or_explicitly_allowlisted` | every `Component`\u002F`Resource` impl in any `crates\u002F*\u002Fsrc` (roots **discovered**, not listed) + `byroredux\u002Fsrc` is registered XOR in `NOT_SAVED_BY_DESIGN` with a reason; `PlayerNotifications`, `PickedUp` etc. are classified there |\n| `serde_default_guard_tests::serde_default_on_saved_struct_requires_format_major_bump` | any `#[serde(default)]` on a save-participating type (skip-only fields exempt) |\n| `serde_default_guard_tests::saved_type_shape_changes_require_format_major_bump` | hash of every `#[derive(..Serialize..)]` struct\u002Fenum body in `save_type_sources()` — discovered files that carry a `cfg_attr(feature = \"save\"` derive or define a registered type, plus eight explicit non-turbofish \u002F nested-payload files (`form_id.rs` ×2, `string\u002Fmod.rs`, `script_instance.rs`; since #5059 `945be7dc6`: `lighting.rs`, `crates\u002Fsdk` `identity.rs` \u002F `script_function.rs`; since #6509 `c72ecafad`: `collision.rs`, whose `MotionType` is the payload of the saved `ReferenceMotionTypeState` and had silently left the scan when #6237 unregistered `RigidBodyData`); registered names are read from `save_io\u002Fregistry.rs` and from every scanned file that defines `fn register_saved(` (#5802) — vs `BASELINE_SHAPE_FINGERPRINT`\u002F`BASELINE_MAJOR`; the attribute scan reads the whole contiguous attribute block, so a save derive above a plain `#[derive(..)]` is seen (#5403 `e19cb0491`) |\n| `serde_default_guard_tests::set_in_chargen_renames_still_decode_v23_keys` | `serde(alias)` rename path stays decodable |\n| `round_trip_tests::delta_columns_carry_only_session_stable_fields` | no `FixedString`\u002F`EntityId`\u002Fsession handle in a `MUTABLE_DELTA_COLUMNS` type |\n| `round_trip_tests::delta_columns_removed_at_runtime_have_a_load_reconciler` | every delta column with a production removal site has a declared reconciler or \"NoReconcilerNeeded\" reason |\n| `round_trip_tests::npc_spawn_stamped_components_are_saved_or_intentionally_rederived` | spawn-stamped components registered or in `REDERIVED_NOT_SAVED` |\n| `crates\u002Fsave` `snapshot.rs`\u002F`disk.rs`\u002F`registry.rs` unit tests | header gates (`rejects_bad_magic`\u002F`_truncated`\u002F`_payload_truncation`\u002F`detects_crc_corruption`\u002F`rejects_schema_mismatch`\u002F`rejects_major_version_skew`), atomic write, slot parsing, ring resume, recency tie-break, `form_id_column_resolves_the_flagged_entry`, replacing-column semantics |\n| `tests\u002Fround_trip.rs::typed_snapshot_preflight_rejects_bad_column_without_world_mutation` | typed preflight precedes `clear_entities` |\n| `live_reload_tests::{saved_resources_are_restored_before_the_cell_reload, pre_reload_restore_must_not_install_saved_item_instance_pool_early}` | pre-reload resource subset, #4135 |\n| `live_reload_tests::{an_aborted_load_hands_the_live_ledgers_back, the_drain_captures_before_the_pre_reload_restore_and_reinstates_on_abort}` | `LiveReloadLedgers` (one field per `PRE_RELOAD_RESOURCES` entry, kept in step by a source guard) captured before the pre-reload restore and reinstated on every abort (#6081 `b5a205715`) |\n| `live_reload_tests::live_load_drain_discards_the_outgoing_sessions_script_events` | the teardown's queued `OnCellDetach`\u002F`OnUnload` deliveries are discarded and `ActorWatch` re-baselined after the session replacement (#6083 `4b8e62d87`) |\n| `live_reload_tests::{live_load_drain_discards_the_cell_resets_its_reload_raised, live_load_drain_discards_the_outgoing_sessions_requests}` | the reload's own `note_player_entered_cell` must not leave `PendingCellResets` queued against the outgoing clock (#6238 `078df54e1`, `discard_pending_cell_resets`), and the value-keyed request queues (`PlayerMoveRequest`, `PendingPlayerXp`, `PendingSkillAdvances`, `PendingSleep`, `PendingMusic`, `PendingScriptMessages`, `MessageBoxQueue`) straddling the drain are cleared (#6239 `337bf25c8`, `discard_outgoing_session_requests`); the same sweep now also clears `PendingEvidenceDeposits` (#6573 `81332fc38`) and `PendingMineHits` (#6540 `bdaf75c66`), ends `VatsState` mode via `end_vats_for_world_change` (`systems\u002Fvats.rs`: `exit_all()` keeping `roll_state`; #6508 `5a423316f` — an F9 inside VATS left the loaded world frozen on despawned targets; #6625 `6186ab77e` made it the one helper shared with the player-death and cell-transition exits) and drops the crafting session, `CraftingRequest` and `PendingDroppedItems` (#6574 `375d48c6d`) |\n| `registry_completeness_tests::every_component_or_resource_impl_is_saved_or_explicitly_allowlisted` (tail) | #6574: a `NOT_SAVED_BY_DESIGN` reason that says \"a load discards\" \u002F \"The load drain ends\" its type must be named in the body of `discard_outgoing_session_requests` — a claim the sweep does not back fails (the generic guard the earlier audits asked for) |\n| `registry_completeness_tests::the_schema_fingerprint_is_pinned` | `BASELINE_SCHEMA_FINGERPRINT` vs `build_save_registry().schema_fingerprint()`: a registration-row reorder (the #5802 slices' risk) or a column add\u002Fremove\u002Frename fails CI instead of rejecting every save (#6296 `5a3f571f4`) |\n| `registry_completeness_tests::every_delta_column_is_parked_or_explicitly_not` | the **third** list: every `MUTABLE_DELTA_COLUMNS` entry is parked in a named `ReferenceState`\u002F`ScriptState` field (`cell_loader\u002Freference_state.rs`), carried by `ActorStreamSnapshot`, or on `NOT_PARKED` with a reason; every parked field maps to a column or `FIELDS_WITHOUT_A_COLUMN` (#6085 `3b9060701`); no `NOT_PARKED` reason may read `UNAUDITED` — the marker's last row, `Escorted`, is parked now (#6294 `f3ab49f55`) |\n| `command_queue_tests::{a_save_taken_mid_cell_transition_is_refused_not_written, a_save_taken_while_chargen_disables_saving_is_refused_not_written, quicksave_ring_cursor_does_not_advance_on_validation_abort, player_save_actions_wait_for_the_quiescent_fifo_drain, quickload_empty_errors_and_corrupt_newest_falls_back}` | save-side refusal gates, ring, quiescent drain |\n| `live_reload_tests::post_reload_drain_steps_keep_their_order` | source-order pin inside `execute_pending_save_loads`: `restore_resources` → `restore_resident` → `build_form_id_remap` → the `ScriptPackage` pre-seat `apply_deltas` → `reseat_ambient_packages_after_restore` → the full `apply_deltas(.. MUTABLE_DELTA_COLUMNS)` (#5060 `02fa8358c`; the remap and pre-seat moved ahead of the re-seat in #6087 `c7463c04d`; the `reconcile_worn_gear` single-call pin joined it, #5460 `bda5a1167`) |\n| `app_step.rs::the_save_drain_publishes_the_transition_flag_before_draining` | `CellTransitionInFlight` published before the drain |\n| `crates\u002Fsave\u002Fsrc\u002Fsnapshot.rs` `the_bump_ledger_is_contiguous_and_current` | the `FORMAT_MAJOR` doc ledger has every `vN -> vN+1` step with no hole and ends at the constant — a bump that edits only the constant fails (#5459 `a4ccf3b27`) |\n| `*_survives_save_load*` (`round_trip_tests.rs`, `consumable_tests.rs`) | per-type round trips incl. lock\u002Fenable ledgers, fragment\u002Fprovider queues, cinematic trio, `Perks`\u002Ftimed restorations, SM node state + `StoryEventAliasFill` reference FormIds |\n\nNot covered by any guard (the audit's real work): the **two-list drift** between registry and\n`MUTABLE_DELTA_COLUMNS`; staleness of `NOT_SAVED_BY_DESIGN` *reasons*; whether the `NOT_PARKED` reasons of the eviction-row\npartition are *true* (seeded from the delta list by #6085; #6294 `8fc521650` \u002F `f3ab49f55` retired the `UNAUDITED` marker —\nthe guard now rejects it, and `Escorted` is parked; a delta column reverting on every cell exit is the GAME-SD-01\u002F02 class); whether a baseline refresh\nwithout a bump was *justified*; manual `impl Serialize` types (invisible to both serde guards);\nthe extension-state payload's own versioning (Dim 2); semantic correctness of load-apply ordering beyond\nthe two pinned pre-reload cases. (Path-qualified `impl byroredux_core::ecs::Resource for X` impls were\ninvisible to `impl_target_type` until #5255 `6b494d002`: it now parses the trait path's final segment, the nine\nformerly unclassified types are allowlisted, and the guard also asserts every `NOT_SAVED_BY_DESIGN` row matches a\ndiscovered production impl — a renamed\u002Fdeleted type can no longer keep a stale row. Still judge the *reasons*.)\n\n## Parameters \u002F Extra Fields\n\n`--focus \u003Cdims>` (default all 5) · `--depth shallow|deep` (`shallow` = container\u002FAPI contracts; `deep` =\ntrace capture → encode → disk → decode → reload → delta-apply). Finding fields: **Dimension**: Snapshot\nCompleteness | Format & Schema Discipline | Container & Disk | Save-Side Gates | Live Load-Apply ·\n**Data-Loss Class**: silent-drop | corruption-on-load | irrecoverable-write | reference-break | none\n(required for any finding that can lose progress).\n\n## Phase 1: Setup\n\n`mkdir -p \u002Ftmp\u002Faudit\u002Fsave`; dedup: `gh issue list --repo matiaszanolli\u002FByroRedux --limit 200 --json number,title,state,labels > \u002Ftmp\u002Faudit\u002Fsave\u002Fissues.json`;\nread the newest `docs\u002Faudits\u002FAUDIT_SAVE_*.md` (fixed findings are regression checks — verify the fix is\nin place); read the `crates\u002Fsave\u002Fsrc\u002Flib.rs` docstring and `snapshot.rs` container doc — each design claim\nmust be CODE-CONFIRMED or DRIFTED; run the guard ledger. Delta-first: for each dimension\n`git log --since=\u003Clast report> --format='%h %cs %s' -- \u003CPaths>`, skim unchanged ones.\n\n## Phase 2: Dimensions\n\n### Dimension 1: Snapshot Completeness & the Two Lists (highest risk — silent-drop)\nPaths: `byroredux\u002Fsrc\u002Fsave_io\u002Fregistry.rs` (`build_save_registry`, `MUTABLE_DELTA_COLUMNS`), `byroredux\u002Fsrc\u002Fsave_io\u002Flive_load.rs` (`PRE_RELOAD_RESOURCES`), `save_io\u002Fregistry_completeness_tests.rs`, `crates\u002Fsave\u002Fsrc\u002F{registry,driver}.rs`\nFirst step: `git log -p -S'register_' --since=\u003Clast report> -- byroredux\u002Fsrc\u002Fsave_io`; then `git log --since=\u003Clast report> --format='%h %cs %s' -- 'crates\u002F*\u002Fsrc' byroredux\u002Fsrc | grep -i 'component\\|resource\\|state'` to find new persistent state.\n- **The registry IS the completeness contract**; start from the guard's `NOT_SAVED_BY_DESIGN` list, not from a\n  hand enumeration. A persistent, player-mutable component\u002Fresource that is neither registered nor\n  allowlisted is a HIGH silent-drop; a *stale allowlist reason* (the guard checks a reason exists, not that\n  it is true) is the auditable residue — spot-check a sample every run, and re-check any reason that names a\n  \"no production mutator\" claim after new gameplay commits land (P3 loot\u002Fconsumables\u002Fpickups, perks, timed\n  restorations, hardcore mode, persistent reference state were all new saved state; `PlayerNotifications`\n  and `PickedUp` are correctly transient — the durable half of a pickup is the `PersistentReferenceStates`\n  tombstone). Recent false reasons, since fixed: `StoryEventAliasFill` (\"quests restart\n  through fresh events\" — now registered, #5394 `03e51dfad`), `EatSleepState` (\"`resolve_destination` is\n  idempotent\" — restated per PLDT anchor, #5391 `42aab4c09`), `CinematicReAdoption` (\"the teardown handles\n  them\" — the purge now despawns them, #5379 `203be9ed4`); plus the Eat\u002FSleep rows' \"registered Seated restore\"\n  (`Seated` is not overlaid — `restore_world`-only; restated and pinned by the `seated_without_path` check at the tail\n  of the completeness guard, #5458 `0aa844dbb`) and `StoryLocationCursor` (\"a load legitimately re-fires\" —\n  `execute_pending_save_loads` now resets it on every in-process load, #5435 `557ead21d`, pinned in\n  `live_reload_tests.rs`).\n- **Removed-from-allowlist stays registered**: `CharacterController` (fractional breath\u002Fdrowning carry),\n  `Material`, `RumbleOnActivate`, `FragmentExecutionQueue`, the cinematic trio — verify none\n  regressed out of `build_save_registry`, and no re-added `NOT_SAVED_BY_DESIGN` entry re-excludes them.\n- **Two lists, one truth**: the live load overlays only columns in BOTH the registry and\n  `MUTABLE_DELTA_COLUMNS`. A registered mutable column absent from the overlay is captured but never\n  replayed → HIGH silent-drop, unless deliberately excluded and documented at its `register_component`\n  site: `Material` (blast radius), `AnimationPlayer`\u002F`AnimationStack` (stale `root_entity`\u002F`clip_handle`),\n  `FollowState`\u002F`EscortState`\u002F`Seated`\u002Fcinematic pair (`EntityId`, registry handles), `ActorVitals`\n  (write-once FormID key). A column with an `EntityId`\u002Fhandle must also be covered by\n  `validate_saved_entity_references` (Dim 4).\n- **Replacing columns**: `Perks`, `CharacterLevel`, `LevelUpPoints`, `FactionReputation` (#6759), `TimedRestorations`, `Dead`, `ActorControlState`, `DismemberRecord` use\n  `register_replacing_component` (`Dead`\u002F`ActorControlState` since #5027\u002F#5052 `7aa1d7741` — the process-lifetime player\n  outlives the reload, so an additive `Dead`\u002Frestraint row survived a load into an alive save; pinned by\n  `dead_and_restraint_cleared_on_live_player_by_saved_absence`) — saved absence is\n  authoritative for FormID-matched entities (empty column kept in the snapshot; a *missing* column is not a\n  tombstone). Ordinary columns are additive-only, so **every runtime removal of an overlaid component needs a\n  reconciler** (`reconcile_dead_actor_runtime_state`, `reconcile_player_equipped_weapon`); the guard enforces\n  disposition, this dimension judges whether \"NoReconcilerNeeded\" reasons are true (carrier destroyed by\n  the cell reload?) and whether a new persisted fact (disable, pickup, lock) chose the marker-plus-\n  reconciler or FormID-keyed-ledger model (`ReferenceEnableState`, `ReferenceLockState`,\n  `PersistentReferenceStates` — resources keyed by FormID survive cell unload; a component would not). A saved\n  resource that needs session `EntityId`s takes the `StoryEventAliasFill` shape (#5394): `StoryEventFill`\n  `serde(skip)`s its `EventDataSlots` and saves the R1\u002FR2 reference FormIds, which the alias refresh re-resolves.\n  The overlay emits no `EquipmentEventBatch`: presentation derived from an overlaid column is re-derived by\n  `reconcile_worn_gear` (`byroredux\u002Fsrc\u002Fnpc_spawn\u002Floot_appearance.rs`, #5034 `20717d5b7`) — run on the player after\n  `apply_deltas` and on NPCs from `reference_state::restore`; it diffs live roots against restored slots\n  (reveal \u002F hide \u002F queue `PendingGearImport`) and synthesizes no event batch. A new presentation fact derived\n  from an overlaid column needs the same treatment. Re-derived-not-saved player state: `FactionRanks` is reset\n  to the record by `reset_player_factions_to_record` after the overlay (#5058 `be3cd9468` — alias-injected\n  ranks otherwise outlived the load).\n- **P3\u002FP4 runtime state (2026-09-29)**: player body (`PlayerBodyRoot`, `PlayerBodyRootEntity`, `HiddenFirstPerson`,\n  `PlayerCameraView`), mid-life gear import (`NpcSkeletonBones`, `ActorBodyClass`, `PendingGearImport`,\n  `PendingGearRelease` — both dropped together on load by `clear_player_gear_handoff_scratch`, #5255 `6b494d002`,\n  `PendingInventoryActions`) and NPC dialogue (`NpcDialogueTopic`, `DialogueSurfaceState`) are all\n  `NOT_SAVED_BY_DESIGN`; `ObjectiveHudCache` is an `App` field, not a Resource. Verify each reason (body subtree\n  entities have no `FormIdComponent`: captured by `save_world`, never remapped). `RigidBodyData` is\n  `NOT_SAVED_BY_DESIGN` since v116 (#6237 `6765e60eb`): every carrier is a `PhysicsSourceForm` collision entity the live\n  overlay can never resolve, so #2379's column only ever round-tripped through `restore_world`; a script's\n  `SetMotionType` lives in the `ReferenceMotionTypeState` ledger (`PRE_RELOAD_RESOURCES`), applied by\n  `cell_loader::spawn::scripted_motion_type` to each collider. A re-registered `RigidBodyData` column is the\n  regression.\n- **Determinism** (the `Snapshot` doc claims reproducible CRCs at equal state): `Snapshot` maps are\n  `BTreeMap` and component rows are sorted by entity id (`registry.rs`), but saved **resources** serialize\n  as-is — at 2026-09-19 `Globals(HashMap\u003Cu32, f32>)`, `QuestStageState`, `ReferenceEnableState` (`HashSet`),\n  `ReferenceLockState` and `PersistentReferenceStates::pair_rows` all emit hash-iteration order (since joined by\n  `DialogueSpokenInfoForms` (`HashSet`) and `StoryEventAliasFill` (`HashMap`)), so two saves\n  of equal state can differ in bytes\u002FCRC. #4748 is closed as a doc fix: the `Snapshot` doc in `snapshot.rs` now\n  says the payload \"is not guaranteed deterministic for equal logical state\" (`Globals` is still a `HashMap`, by\n  design of that resolution). A new claim of reproducible CRCs, or a consumer that diffs\u002Fhashes saves, reopens\n  it (MEDIUM doc\u002Fcontract mismatch, not data loss). Do not claim determinism at the row level without checking this.\n- **`next_entity`** is saved verbatim and replayed via `set_next_entity` before inserts; `insert_batch`'s\n  `entity \u003C next_entity` is a `debug_assert` only (release inserts at an unspawned id silently — MEDIUM);\n  `StringPool::dump`\u002F`from_dump` preserves symbol order (a reordered dump = every `Name` wrong = CRITICAL).\n**Output**: `\u002Ftmp\u002Faudit\u002Fsave\u002Fdim_1.md`\n\n### Dimension 2: Format & Schema Discipline (registry fidelity + `FORMAT_MAJOR`)\nPaths: `crates\u002Fsave\u002Fsrc\u002F{snapshot,registry}.rs`, `save_io\u002Fserde_default_guard_tests.rs`, `crates\u002Fsave\u002FCargo.toml`, `crates\u002Fcore\u002FCargo.toml`\nFirst step: `cargo test -p byroredux --bin byroredux serde_default_guard` ; `git log --since=\u003Clast report> --format='%h %cs %s' -- crates\u002Fsave\u002Fsrc\u002Fsnapshot.rs`\n- **Bump rule** (read the `FORMAT_MAJOR` doc comment in `snapshot.rs`: 124 as of 2026-11-09, with one `vN -> vN+1`\n  entry per bump from v22 on, contiguous since #5459 `a4ccf3b27` restored the v32→v33 entry (#5367 `14cff35ae`, new saved\n  resource `DialogueSpokenInfoForms`) and pinned it with `the_bump_ledger_is_contiguous_and_current`. Recent anchors: v36 Wave 4 `RigidBodyData.surface_material`, v37 #5542\n  widened it `u8`→`u32`, v38 #5300 re-keyed `DialogueSpokenInfoForms` to a (speaker ref, INFO) pair, and v39–v104\n  are almost entirely M47.2 scripting state — new saved resource columns, `Effect` \u002F `FragmentGuard` \u002F `ValueExpr`\n  variants and fields reachable through queued fragment continuations (`FragmentExecutionQueue`), `CharacterLevel`\n  v46 (kill XP ended #2947's exemption), `Bleedout` v51, `LevelUpPoints` v53; v105–v124 are gameplay\u002FObScript\n  fields and columns (`Bleedout.remaining_secs` → `Option` v105, parked `ReferenceState.bleedout` v106, M47.3 ObScript\n  columns v109–v112, `Material.source_shader_type` v113 #4256, parked `ReferenceState` script package \u002F corpse pose +\n  `PersistentReferenceStates` script rows v114 #6071–#6073 `611769b16`, parked `ReferenceState` magic effects \u002F\n`weapon_drawn` \u002F `ignore_crime` + `ScriptState` timer \u002F rumble v115 #6085 `3b9060701`; `RigidBodyData` out,\n`ReferenceMotionTypeState` in v116 #6237 `6765e60eb`; v117 #6190 `ObScriptLedger` lost four unread columns; v118 Oblivion\njail — `ObScriptLedger.stolen_goods` \u002F `evidence` \u002F `jail`; v119 the `CrimeKnowledge` resource joined the registry; v120 the `KnownWords` resource joined it and `Effect` gained `ObtainWord`; v121 `SmNodeRuntime.last_node_fire_hours`; v122 `ItemInstance.quality`; v123 #6759 `8fc1a9ef9` the `FactionReputation` replacing column (the stale \"no mutation site\" exemption); v124 d50fc1153 the `DismemberRecord` replacing column plus a parked `ReferenceState.dismember` field, VATS-LIMB-008\u002F-009); do not hardcode the number elsewhere). Precedent: v112\n  (#6050 `5f8bbf454`) — `register_saved` columns landed after a baseline refresh with no bump and the shape guard sat\n  red on main; a new registered column's defining file joins the shape scan, so it is a bump here too. Intra-type shape changes need a bump because\n  `schema_fingerprint` hashes only column keys (+ replacing policy). A new required field, retyped field, or new `Option` in a saved\n  type bumps; `#[serde(default)]` is forbidden as a compatibility mechanism (guard) — even where the default\n  would be correct for every old save. A saved map keyed by a tuple\u002Fstruct cannot be a JSON object (`serde_json` refuses the key once it holds a row, failing the\n  whole snapshot — the FNV `SetAlly` quicksave failure): such maps go through `byroredux_scripting::serde_pairs` as\n  `[key, value]` lists, which also read an empty object as the empty map; `4156e68cd` moved the shape baseline with no bump\n  on that ground (a non-empty map never wrote, so no old save can hold one) — judge any similar refresh against it.\n  Read-compatible changes move the shape baseline **without** a bump:\n  adding an enum variant to an externally-tagged `serde_json` enum (keyed by variant *name*, never ordinal —\n  #4140 corrected the old \"discriminant shift\" rationale), removing a field (no `deny_unknown_fields` on any\n  saved type), renaming with `serde(alias)`. Changing an existing variant's field shape is not compatible.\n  Adding\u002Fremoving a registered column changes the fingerprint and rejects old saves via `SchemaMismatch`\n  with no bump. An audit proposing to relax the blanket rule for a \"safe\" default reopens #1714.\n- **Baseline refresh triage** (the guard cannot judge this): every `BASELINE_SHAPE_FINGERPRINT` change must\n  carry a justification comment stating whether a registered column's shape changed. Known false-positive\n  classes: (a) the guard hashes *every* serialized-derive type in a scanned file, including `NOT_SAVED_BY_DESIGN`\n  types sharing it (e.g. `AnimatedTextureFlip`; inspect-only files such as `WaterMaterial`'s left the scan in\n  573170e1c) — those move the hash with no snapshot impact; (b) a **tuple struct** sweeps the following `impl` block into its span (`VisibilityMask`) — method\n  edits move the hash; (c) the hash includes the file-relative path, so moving a save-participating type\n  between files moves it. Verify baseline and `FORMAT_MAJOR` were updated in the same commit, and that a\n  refresh-without-bump was not hiding a real field change to a registered type.\n- **Guard blind spots**: manual `impl Serialize` (no derive) types; a nested payload type of a plain-`derive`\n  registered type (the `byroredux\u002Fsrc` pattern) defined in another file is outside `save_type_sources()` unless\n  added to its explicit list (#5059 fixed in `945be7dc6` for `LightSource.emitter` and the `crates\u002Fsdk`\n  `FormRef` \u002F `ScriptValue` payloads — the next such nested payload is the residue; the extension-manifest\n  declaration types sharing `script_function.rs` are exempt from the `serde(default)` scan, with a staleness\n  test); the shape scan once skipped a save derive that was not the *last* `#[…]` attribute (#5403 fixed in\n  `e19cb0491`, coverage markers assert `StoryManagerNodeState` \u002F `SmNodeRuntime` contribute shapes); a new `Option\u003CT>` field is hashed but\n  looks like a routine baseline bump — confirm it got the bump; `serde(alias)` must never coexist with a\n  dropped value semantic change.\n- **Second payload — extension state** (`Snapshot.resources[\"ByroExtensionState\"]`, written by\n  `extensions::capture_extension_state`, type `ExtensionStateSnapshot` in `crates\u002Fsdk\u002Fsrc\u002Fcomponent.rs`):\n  it is outside the registry, the completeness guard, and both serde guards (sdk types carry a plain\n  `derive`, so `save_type_sources()` drops them), and follows its **own** policy — a `format_version` range\n  (`MIN_EXTENSION_STATE_FORMAT_VERSION..=EXTENSION_STATE_FORMAT_VERSION`) with `#[serde(default)]` fields, which\n  the blanket rule forbids for registry types. Verify: the range gate runs in `preflight_extension_state`\n  (before teardown); every shape change to its rows bumps `EXTENSION_STATE_FORMAT_VERSION` (nothing enforces\n  it — no shape baseline); rows without stable authored identity refuse the save rather than dropping;\n  `decode_saved_state` treats a missing key as empty but a present-and-malformed one as an abort.\n- **`ValidateFn` parity**: every `register_*` variant builds a `validate` closure that decodes the SAME type\n  `load` does (`Vec\u003C(u32, T)>` for columns, bare `T` for resources) — a missing\u002Fwrong one exempts a column\n  from the preflight so it fails only after teardown.\n- **Feature chain**: `crates\u002Fsave` → `byroredux-core` `features=[\"save\"]` (→ `inspect`) must still hold, or a\n  non-default build compiles away serde impls and columns serialize to `null`.\n- **FormId handle vs pair**: `register_form_id_component` saves the stable `FormIdPair` (skips unresolvable\n  handles with WARN, never panics); load re-interns to fresh handles and errors cleanly without a pool.\n  `form_id_column()` is keyed by the explicit `is_form_id` flag, never \"first `apply: None` entry\".\n- `FnvHasher` uses canonical FNV-1a 64 constants and hashes only names\u002Forder\u002Fpolicy (no TypeId\u002Faddress).\n**Output**: `\u002Ftmp\u002Faudit\u002Fsave\u002Fdim_2.md`\n\n### Dimension 3: Container & Disk Durability\nPaths: `crates\u002Fsave\u002Fsrc\u002F{snapshot,disk}.rs`, `crates\u002Fcore\u002Fsrc\u002Fatomic_file.rs`, `crates\u002Fsave\u002Ftests\u002Fround_trip.rs`\nFirst step: `cargo test -p byroredux-save disk:: snapshot::`\nGuarded (see ledger): header gate order (length → magic → major → schema fingerprint → `payload_len`\nchecked bounds → CRC over the payload only → `serde_json::from_slice`), advisory `minor`, slot-name strictness\n(`save_42.ess.tmp` rejected), `slots_by_recency` newest-first with slot-number tie-break, ring wrap\u002Fresume.\nResidual checks:\n- **`atomic_write`** (`crates\u002Fcore\u002Fsrc\u002Fatomic_file.rs`; shared with `crates\u002Fsettings-io`,\n  `crates\u002Fboot-request`, `crates\u002Fgame-detect\u002Fsrc\u002Foverrides.rs`) order is exactly: create tmp → `write_all` →\n  `flush` → `sync_all` → byte-exact read-back → `rename` (all inside `stage_and_rename`) → parent-directory\n  fsync. **Any** failure up to the rename removes the temp (#5163 `69fd54fb2`; `failed_rename_removes_the_temp`),\n  and no caller may fall back to copying\u002Frenaming the temp over the target on error — the old \"Windows rename\"\n  fallback clobbered good files on disk-full (#5143 `2e95f0bbf`). The three non-save writers pin that with\n  `assert_no_clobber_fallback` (production text only, via `core::source_scan`); `write_slot` is pinned by\n  `write_slot_has_no_clobber_fallback` and stages through the unique `atomic_temp_path` (#5247 `eb08e14fd`;\n  the old fixed `save_\u003Cslot>.ess.tmp` raced two processes sharing a save dir) after `create_dir_all`. Rename-before-fsync, a length-only read-back, or a new fallback branch is a HIGH\n  durability hole.\n- **Ring never clobbers the last good save**: `SaveState::new` must build the ring via `SaveRing::resume`\n  (not `new`), and the cursor advances only after a committed write (`quicksave_ring_cursor_does_not_advance_on_validation_abort`).\n- **Minor-version + default-fill**: `decode` accepts newer minors and serde default-fills; that is the exact\n  path `#[serde(default)]` would exploit — cross-check Dim 2's guard is live.\n- `SAVE_DIR_ENV` (`BYROREDUX_SAVE_DIR`) redirects the ring; smoke gates depend on the spelling.\n**Output**: `\u002Ftmp\u002Faudit\u002Fsave\u002Fdim_3.md`\n\n### Dimension 4: Save-Side Gates (refusal + validation)\nPaths: `byroredux\u002Fsrc\u002Fsave_io\u002Fcommands.rs` (`SaveCommand::execute`, `validate_form_ids`, `validate_cinematic_entity_refs`), `crates\u002Fsave\u002Fsrc\u002Fvalidate.rs`, `byroredux\u002Fsrc\u002Fapp_step.rs` (`step_player_save_actions`)\nFirst step: `grep -n 'fn validate_\\|validate_[a-z_]*(world' crates\u002Fsave\u002Fsrc\u002Fvalidate.rs byroredux\u002Fsrc\u002Fsave_io\u002Fcommands.rs`\n- **Order in `SaveCommand::execute`**: refusals first (`CellTransitionInFlight` — a mid-transition save would be\n  written and permanently unloadable; `CinematicPresentationState.disable_saving` — `Game.SetInChargen`\n  `abDisableSaving`; plus the context-invariant refusal #5253 `27562f8ae` — `LoadedCellIndex` present but neither\n  `CurrentCellContext` nor `CurrentExteriorContext` means an unloadable snapshot even when the synced flag still\n  reads `false`, as in the console ingresses that dispatch outside the drain), then `validate_world` + `validate_form_ids` + `validate_cinematic_entity_refs`\n  (abort, up to 20 lines, nothing written), then `save_world` → `capture_extension_state` → `encode` →\n  `write_slot`; ring advance and the `SaveComplete` session event only after the commit. Any alternate save\n  path that bypasses the gates (console, player action, SDK) is HIGH; input adapters must enqueue through\n  `queue_player_save_action` (post-scheduler drain — `save_world` takes ~30 storage + resource read locks, so\n  a live scheduler lane needs the ABBA analysis re-derived; `\u002Faudit-concurrency`). Current ingresses: the\n  winit F5\u002FF9 intercept, the pause menu, and the debug `input.press quicksave|quickload`, which resolves\n  through `ActionBindings` to the same F5\u002FF9 edges (`queue_debug_action_press`, `byroredux\u002Fsrc\u002Finteraction.rs`,\n  `bae84e54b`; `quicksave_press_joins_the_player_save_queue_through_the_f5_binding`).\n- **Coverage**: `validate_world` currently runs hierarchy, equipment, saved-entity references (session-local\n  `EntityId` fields of columns excluded from the overlay — grep the function for the live column list),\n  animation (`AnimationPlayer`, `AnimationStack` root + layer clips, `Seated.animation_restore`),\n  inventory instances vs `ItemInstancePool`, and `Material` finiteness (the progression check\n  `validate_progression_state` retired in `dbe562c1f` when `CharacterLevel` became a saved replacing column, v46);\n  the binary adds FormId resolvability and cinematic `EntityId` refs. Do\n  not restate the list elsewhere — enumerate **inter-entity reference fields in any newly registered type\n  not covered** (MEDIUM defense-in-depth gap) and any check that flags a legitimately sparse-but-spawned id\n  (dangling = `>= next_entity`, not \"no components\").\n- **Post-load validation is diagnostic-only** (`log_validation_warnings`, WARN, no abort) in both\n  `restore_world` and `execute_pending_save_loads`; the **typed preflight aborts** (before teardown).\n- **Transient state**: saves must not depend on transients — `PlayerNotifications`, pending action queues,\n  `CellTransitionInFlight`, parked `StreamStateSnapshots` are unsaved by design; a save taken while one is\n  non-empty must still round-trip (check new transient resources land on the allowlist, not the registry).\n**Output**: `\u002Ftmp\u002Faudit\u002Fsave\u002Fdim_4.md`\n\n### Dimension 5: Live Load-Apply & Frame Boundary\nPaths: `byroredux\u002Fsrc\u002Fsave_io\u002Flive_load.rs` (`execute_pending_save_loads`, `reload_*_session`), `byroredux\u002Fsrc\u002Fsave_io\u002Fplayer.rs` (`apply_player_pose`), `crates\u002Fsave\u002Fsrc\u002Fdriver.rs`, `byroredux\u002Fsrc\u002F{app_events,app_step,app_frame}.rs`, `byroredux\u002Fsrc\u002Fcell_loader\u002F{transition,reference_state,unload}.rs`\nFirst step: read `execute_pending_save_loads` top to bottom against the sequence below; `git log --since=\u003Clast report> --format='%h %cs %s' -- byroredux\u002Fsrc\u002Fsave_io`\n- **Strict apply sequence**: drain slot → `validate_snapshot_types` (abort, session kept) →\n  `preflight_extension_state` (abort) → `restore_resources_subset(PRE_RELOAD_RESOURCES)` (only resources\n  read by spawn-time code during the reload: `ReferenceEnableState`, `ReferenceLockState`, `ReferenceScriptState`,\n  `ScriptedFactionChanges` — #5919 `aa14d3e89`, replayed by `stamp_quest_reference` as NPCs respawn — and\n  `ReferenceMotionTypeState`, #6237, read by `scripted_motion_type` as each collider is built;\n  `pre_reload_restore_installs_the_scripted_faction_ledger`; **never**\n  `ItemInstancePool` — teardown's `release_victim_item_instances` needs the live pool, #4135; the live values are\n  captured first as `LiveReloadLedgers` and reinstated if this restore or the reload below fails\u002Frefuses, so a\n  \"keeping the current session\" abort does not leave the other timeline's ledgers live — #6081 `b5a205715`) →\n  `without_parked_state` wraps the reload (outgoing session's `PersistentReferenceStates` and\n  `StreamStateSnapshots` are set aside and restored on failure) → teardown + reload (`validate_cell_loadable`\n  preflight first, so a missing ESM\u002Fcell keeps the live session; then `purge_cinematic_retention_state`\n  drops every `ActorCinematicState`\u002F`HorseTetherState` row so the convoy is not retained across the teardown\n  as a ghost twin sharing a `FormIdPair` — #5056 `a197e8563`, both reload arms + both debug-load paths, never\n  ordinary cell transitions (`purge_wiring_tests`) — and despawns + clears the unsaved `CinematicReAdoption`\n  pending list, whose un-rooted entities the `CellRootIndex` teardown cannot enumerate (#5379 `203be9ed4`).\n  Since #5384 that list holds parentless roots only, so the purge (now taking the `VulkanContext`) collects each\n  root's `Children` closure and hands it to `release_entities` — subtrees no longer orphan (which made\n  `validate_world` refuse every later save) and GPU\u002FRapier handles are released, #5490 `99cd9fd6c`. The teardown never\n  despawns the player rig: `unload_cell_inner` refuses `player_owned_entities`, #5483 `5a68e983e`)\n  → `reset_story_location_cursor` + `discard_pending_story_events` (the unsaved CLOC cursor and any straddling\n  `StoryEvent` markers describe the outgoing session; every load behaves like a fresh boot, #5435 `557ead21d`)\n  → `discard_package_reports` (outgoing actors' package transitions must not run PF_ Begin\u002FChange fragments in the\n  loaded session, #5864 `001de8741`) → `discard_pending_unload_deliveries` (the teardown's own `OnCellDetach`\u002F\n  `OnUnload` queue; Bethesda raises no unload event for a load, #6083 `4b8e62d87`) →\n  `discard_pending_cell_resets` (the reload's cell-root `note_player_entered_cell` calls ran against the outgoing\n  `CellVisits`\u002Fclock; the restored `CellVisits` is authoritative, #6238 `078df54e1`) → `discard_outgoing_session_requests`\n  (value-keyed `PlayerMoveRequest` \u002F XP \u002F skill \u002F sleep \u002F music \u002F message-box queues that would otherwise apply in the\n  loaded world and be kept by the next save, #6239 `337bf25c8`; plus jail-evidence deposits, mine hits, VATS mode (`end_vats_for_world_change`), the crafting session\n  and pending dropped items, #6573 \u002F #6540 \u002F #6508 \u002F #6574; the test pins all but the mine queue)\n  → `restore_extension_state` → wholesale\n  `restore_resources` **again** (idempotent; re-asserts `CurrentCellContext`\u002F`PlayerPose`; the second call is\n  not redundant) → `restore_resident` (parked rows for resident placements, #4695; followed by `combat::release_held_dead_actor_reconciliations`, which hands a corpse's held reconciliation to the Late-stage sink now that its placement is resident, #6133 `1e2c1d30e`) → `drop_entity_bound_continuations` on `PapyrusProviderContinuationQueue` (session-local\n  `EntityRef` handles must not resume, #4139) → `build_form_id_remap` → a `ScriptPackage`-only `apply_deltas` pre-seat (the ambient selection honours a running\n  script package over the schedule, so re-seating without it stamped the scheduled winner and the next minute's tick\n  cleared the freshly overlaid `TravelState`\u002F`Traveled` markers and reported a spurious package-change fragment; the column\n  is replacing and idempotent, so the full overlay re-applies it harmlessly — #6087 `c7463c04d`) → `reseat_ambient_packages_after_restore` (re-pick packages\n  against the restored clock *before* the overlay, #4815; its order against `restore_resident`, the remap, the pre-seat and the\n  overlay is pinned by `post_reload_drain_steps_keep_their_order`, #5060 `02fa8358c`) → park the snapshot rows\n  whose `FormIdPair` did not resolve (`unresolved_form_id_pairs` → `park_unresolved_snapshot_rows` into\n  `PersistentReferenceStates`, #5054 `17e720430` — the exterior reload streams only `radius_load`, so the\n  save's hysteresis-band rows were dropped and their loot duplicated; `unresolved_snapshot_rows_are_parked_for_the_next_respawn`)\n  → `apply_deltas(MUTABLE_DELTA_COLUMNS)` → dead\u002Fequipped-weapon reconcilers, `reconcile_worn_gear` (called\n  once; the duplicate was dropped in `a8b3d1902` and the count pinned by #5460 `bda5a1167`), `reset_player_factions_to_record` → `reapply_scripted_faction_changes` (restored scripted memberships on top) →\n  `rebaseline_actor_watch` (outgoing bleedout\u002Fcombat sets and player location must not deliver across timelines,\n  #6083) → diagnostic `validate_world` + `validate_form_ids` + `validate_cinematic_entity_refs` →\n  `apply_player_pose` LAST (after the\n  overlay of `CharacterController`, whose motion fields pose-restore then zeroes; a new field on either\n  side needs an explicit decision). Any failure after the reload returns immediately, never falling through\n  into pose-restore on a partial overlay. Idempotency: teardown is unconditional, so a second load of the same\n  slot does not stack deltas.\n- **Two restore paths**: live load uses `restore_resources` + `apply_deltas` (saved ids remapped to the\n  reloaded cell's fresh ids); `restore_world` (clear + repopulate at saved ids) is test\u002Floose only and must\n  never be reachable from `execute_pending_save_loads` (id collision = CRITICAL). `clear_entities` does not\n  free GPU\u002Fphysics handles — the live path's `unload_current_interior`\u002F`drain_streaming_state` must.\n- **Remap**: `build_form_id_remap` matches saved `FormIdPair` → live entity; rows without a form id are\n  skipped (respawned by the loader); unresolved pairs are logged (WARN, first 20) — a moved object vanishing\n  must stay diagnosable; the player body carries `PLAYER_FORM_ID_PAIR` from spawn so it participates.\n  Persistent-reference rows: resident rows arrive via the component overlay, nonresident via the resource\n  restore; rows are consumed on respawn (semantics → `\u002Faudit-gameplay`).\n- **Frame boundary**: capture runs post-scheduler (`step_player_save_actions` drains a FIFO after\n  `Scheduler::run`; remote console runs in the exclusive `DebugDrainSystem`); `capture_player_pose` reads\n  post-propagation `Transform` before the drain; `PlayerNotifications` drain in `render_one_frame` is\n  unconditional (bounded ring of 8, oldest dropped). The load drain lives in `step_save_loads` where the\n  App owns `&mut World` + `&mut VulkanContext` and is skipped while an interior transition or foreign\n  loading screen is active; the single `PendingSaveLoadSlot` is last-writer-wins and reports supersession.\n- **Pose**: yaw\u002Fpitch always to `InputState`; Character mode + live body → `place_character_body`\n  (`byroredux\u002Fsrc\u002Fsystems\u002Fcharacter.rs`, the one teleport helper since #5591 `946310eb9`: body `Transform` +\n  `GlobalTransform` + `CharacterController::clear_momentum` — `vertical_velocity`\u002F`is_grounded`\u002F`wants_jump` —\n  + kinematic Rapier sync, no-op without a handle); restore branches on the *live* mode (#2018), so\n  FlyCam-saved\u002FCharacter-loaded relocates the body and the reverse repositions the camera. `apply_player_pose`\n  returns `bool` and refuses the whole pose (non-finite or out-of-world position — `PhysicsWorld::translation_is_sane`,\n  the keyframe bound — or non-finite yaw\u002Fpitch), leaving the reloaded cell's spawn (#5530 `d28545728`,\n  `a_corrupt_saved_pose_is_refused_and_leaves_the_player_where_the_cell_spawned_them`).\n- **Context guards**: `LoadCommand` accepts interior OR exterior context and rejects only neither;\n  `SaveInfoCommand` mirrors the three arms (#3500); `quickload_latest` walks `slots_by_recency` and falls back\n  past a corrupt newest slot; `command_output_is_failure` scans every output line.\n**Output**: `\u002Ftmp\u002Faudit\u002Fsave\u002Fdim_5.md`\n\n## Phase 3: Merge\n\nCombine `\u002Ftmp\u002Faudit\u002Fsave\u002Fdim_*.md` into `docs\u002Faudits\u002FAUDIT_SAVE_\u003CTODAY>.md`: **Executive Summary** (each\n`lib.rs` design claim CODE-CONFIRMED\u002FDRIFTED; findings by severity and Data-Loss Class) · **Data-Loss\nClass Matrix** · **Completeness Ledger** (registry × `MUTABLE_DELTA_COLUMNS`: SAVED-only \u002F SAVED+OVERLAID \u002F\nstructural; cross-check against the guard's allowlist rather than re-deriving) · **Findings** (CRITICAL\nfirst, deduplicated; two-list drift owned by Dim 1, GPU\u002Fphysics teardown by Dim 5) · **Guards\nVerified** (which ledger guards were confirmed live, which reasons were spot-checked).\n\n## Phase 4: Cleanup\n\n`rm -rf \u002Ftmp\u002Faudit\u002Fsave`; tell the user the report is ready; suggest\n`\u002Faudit-publish docs\u002Faudits\u002FAUDIT_SAVE_\u003CTODAY>.md` (domain label `save-load`; `test-gap` for coverage\nfindings, `doc-rot` for drifted save\u002Fload docs).\n","# Save \u002F Load Subsystem Audit (M45 + M45.1)\n\nAudit `crates\u002Fsave` (full-ECS-snapshot format) and its sole live caller `byroredux\u002Fsrc\u002Fsave_io\u002F` (`save_io.rs` is a ~108-LOC re-export hub over `registry` \u002F `player` \u002F `commands` \u002F `live_load`, split in `d7c47d741`)\n(+ `save_io\u002F*_tests.rs`) for **data loss and save corruption**. The subsystem exists to remove Bethesda's\nslow-corruption tail by making the live ECS the single source of truth; verify the CODE delivers that.\nA silently dropped column, a stale schema guard, a torn capture, or a botched FormId remap **loses player\nprogress** — frame as CRITICAL\u002FHIGH per `_audit-severity.md` (data loss is CRITICAL).\n\n**Architecture**: Orchestrator; each dimension is a Task agent (max 3 concurrent). Read\n`.claude\u002Fcommands\u002F_audit-common.md` (layout, methodology, dedup, finding format) and\n`.claude\u002Fcommands\u002F_audit-severity.md` first; do not duplicate them here.\n\n## Scope\n\n**Crate** `crates\u002Fsave\u002Fsrc\u002F` (~3.3k LOC — read all): `lib.rs` (design intent docstring, `SaveError`),\n`snapshot.rs` (`Snapshot`, container header, `FORMAT_MAJOR` doc = the bump ledger, `encode`\u002F`decode`),\n`registry.rs` (`SaveRegistry`; `register_component` \u002F `register_replacing_component` \u002F\n`register_component_preserving` \u002F `register_resource` \u002F `register_form_id_component`; `ValidateFn`; `schema_fingerprint`), `driver.rs` (`save_world`,\n`restore_world`, `validate_snapshot_types`, `restore_resources`, `restore_resources_subset`,\n`build_form_id_remap`, `apply_deltas`), `disk.rs` (`write_slot`, `slots_by_recency`, `SaveRing`),\n`validate.rs` (`validate_world` + 6 sub-checks). Tests: `crates\u002Fsave\u002Ftests\u002Fround_trip.rs`.\n\n**Engine side** (`byroredux\u002Fsrc\u002Fsave_io\u002F`): `build_save_registry` (the curated type set — a scripting subsystem's\nsaved columns live in its own module's `register_saved(&mut registry)` (`crates\u002Fscripting\u002Fsrc\u002F*`, 45 modules, ~51 call\nsites in the hub; extracted in slices from #5802 `bde3478db` through `93bcda9bc`, then #6705 `15e498420` moved 47 more rows\nout of the hub; umbrella #5802 closed 2026-11-07). The hub keeps 39 rows, each with a stated reason: core types and the FormId column (`byroredux-core` sits\nbelow `byroredux-save`; `FactionReputation` #6759 `8fc1a9ef9` and `DismemberRecord` d50fc1153 are the two newest), the physics `CharacterController`, and the binary crate's own state. Each delegate is called at its\nrows' former position, because `schema_fingerprint` hashes rows in registration order — moving a delegate call is a\n`SchemaMismatch` for every existing save, and `the_schema_fingerprint_is_pinned` now fails it; where a module's columns sit\nin separate runs of the order it exports `register_saved_\u003Ccolumn>` too),\n`MUTABLE_DELTA_COLUMNS` (**second** hardcoded list driving the live overlay), `PRE_RELOAD_RESOURCES`,\n`SaveCommand`\u002F`SaveInfoCommand`\u002F`LoadCommand`, `PendingPlayerSaveActions` (F5\u002FF9\u002Fpause-menu ingress),\n`quickload_latest`, `execute_pending_save_loads` (+ `reload_interior_session`\u002F`reload_exterior_session`),\n`capture_player_pose`\u002F`apply_player_pose`, `validate_form_ids`, `validate_cinematic_entity_refs`.\nCross-cut: `byroredux\u002Fsrc\u002Fboot\u002Fregistries.rs` (installs registry\u002Fstate), `app_events.rs` (per-frame\norder: `capture_player_pose` → `step_player_save_actions` → `step_save_loads`), `app_step.rs`,\n`byroredux\u002Fsrc\u002Fnotifications.rs` (`PlayerNotifications`: bounded, transient, unsaved; drained in\n`app_frame.rs::render_one_frame`), `byroredux\u002Fsrc\u002Fextensions\u002F` (SDK extension-state capture\u002Fpreflight\u002F\nrestore layered around the save\u002Freload — the extension *API* is `\u002Faudit-tooling`),\n`byroredux\u002Fsrc\u002Fcell_loader\u002Freference_state.rs` (`PersistentReferenceStates`, `without_parked_state`),\n`cell_loader\u002F{transition,spawn,unload}.rs` (`unload.rs`: `purge_cinematic_retention_state`, the\n`player_owned_entities` teardown refusal), `crates\u002Fcore\u002Fsrc\u002F{atomic_file.rs,string\u002Fmod.rs,ecs\u002Fworld.rs}`,\n`crates\u002Fphysics\u002Fsrc\u002Fsync.rs`. Companion doc: `docs\u002Fengine\u002Fsave-load-roundtrip.md` (numbers the extension preflight\u002Frestore\nsteps as 1b\u002F3b).\n\n**Ownership split**: this skill owns schema, atomicity, validation, and load-apply *mechanics*. What loot \u002F\ninventory \u002F consumable state *should* persist (gameplay semantics) → `\u002Faudit-gameplay`; reconcile-after-\noverlay code lives in gameplay files but its **existence per removable column** is Dim 1's.\n\n## Guard ledger (E2) — run these first; aim dimensions at what they cannot see\n\n`cargo test -p byroredux-save` and `cargo test -p byroredux --bin byroredux save_io` (plus\n`app_step` \u002F `boot::schedule` sources named below). Only the four real-master tests in\n`save_io\u002Fconsumable_tests.rs` are `#[ignore]`d. Confirm each guard is live: not `#[ignore]`d, its scan\nfinds >0 items, and it fails when the invariant is broken.\n\n| Guard | Pins |\n|---|---|\n| `registry_completeness_tests::every_component_or_resource_impl_is_saved_or_explicitly_allowlisted` | every `Component`\u002F`Resource` impl in any `crates\u002F*\u002Fsrc` (roots **discovered**, not listed) + `byroredux\u002Fsrc` is registered XOR in `NOT_SAVED_BY_DESIGN` with a reason; `PlayerNotifications`, `PickedUp` etc. are classified there |\n| `serde_default_guard_tests::serde_default_on_saved_struct_requires_format_major_bump` | any `#[serde(default)]` on a save-participating type (skip-only fields exempt) |\n| `serde_default_guard_tests::saved_type_shape_changes_require_format_major_bump` | hash of every `#[derive(..Serialize..)]` struct\u002Fenum body in `save_type_sources()` — discovered files that carry a `cfg_attr(feature = \"save\"` derive or define a registered type, plus eight explicit non-turbofish \u002F nested-payload files (`form_id.rs` ×2, `string\u002Fmod.rs`, `script_instance.rs`; since #5059 `945be7dc6`: `lighting.rs`, `crates\u002Fsdk` `identity.rs` \u002F `script_function.rs`; since #6509 `c72ecafad`: `collision.rs`, whose `MotionType` is the payload of the saved `ReferenceMotionTypeState` and had silently left the scan when #6237 unregistered `RigidBodyData`); registered names are read from `save_io\u002Fregistry.rs` and from every scanned file that defines `fn register_saved(` (#5802) — vs `BASELINE_SHAPE_FINGERPRINT`\u002F`BASELINE_MAJOR`; the attribute scan reads the whole contiguous attribute block, so a save derive above a plain `#[derive(..)]` is seen (#5403 `e19cb0491`) |\n| `serde_default_guard_tests::set_in_chargen_renames_still_decode_v23_keys` | `serde(alias)` rename path stays decodable |\n| `round_trip_tests::delta_columns_carry_only_session_stable_fields` | no `FixedString`\u002F`EntityId`\u002Fsession handle in a `MUTABLE_DELTA_COLUMNS` type |\n| `round_trip_tests::delta_columns_removed_at_runtime_have_a_load_reconciler` | every delta column with a production removal site has a declared reconciler or \"NoReconcilerNeeded\" reason |\n| `round_trip_tests::npc_spawn_stamped_components_are_saved_or_intentionally_rederived` | spawn-stamped components registered or in `REDERIVED_NOT_SAVED` |\n| `crates\u002Fsave` `snapshot.rs`\u002F`disk.rs`\u002F`registry.rs` unit tests | header gates (`rejects_bad_magic`\u002F`_truncated`\u002F`_payload_truncation`\u002F`detects_crc_corruption`\u002F`rejects_schema_mismatch`\u002F`rejects_major_version_skew`), atomic write, slot parsing, ring resume, recency tie-break, `form_id_column_resolves_the_flagged_entry`, replacing-column semantics |\n| `tests\u002Fround_trip.rs::typed_snapshot_preflight_rejects_bad_column_without_world_mutation` | typed preflight precedes `clear_entities` |\n| `live_reload_tests::{saved_resources_are_restored_before_the_cell_reload, pre_reload_restore_must_not_install_saved_item_instance_pool_early}` | pre-reload resource subset, #4135 |\n| `live_reload_tests::{an_aborted_load_hands_the_live_ledgers_back, the_drain_captures_before_the_pre_reload_restore_and_reinstates_on_abort}` | `LiveReloadLedgers` (one field per `PRE_RELOAD_RESOURCES` entry, kept in step by a source guard) captured before the pre-reload restore and reinstated on every abort (#6081 `b5a205715`) |\n| `live_reload_tests::live_load_drain_discards_the_outgoing_sessions_script_events` | the teardown's queued `OnCellDetach`\u002F`OnUnload` deliveries are discarded and `ActorWatch` re-baselined after the session replacement (#6083 `4b8e62d87`) |\n| `live_reload_tests::{live_load_drain_discards_the_cell_resets_its_reload_raised, live_load_drain_discards_the_outgoing_sessions_requests}` | the reload's own `note_player_entered_cell` must not leave `PendingCellResets` queued against the outgoing clock (#6238 `078df54e1`, `discard_pending_cell_resets`), and the value-keyed request queues (`PlayerMoveRequest`, `PendingPlayerXp`, `PendingSkillAdvances`, `PendingSleep`, `PendingMusic`, `PendingScriptMessages`, `MessageBoxQueue`) straddling the drain are cleared (#6239 `337bf25c8`, `discard_outgoing_session_requests`); the same sweep now also clears `PendingEvidenceDeposits` (#6573 `81332fc38`) and `PendingMineHits` (#6540 `bdaf75c66`), ends `VatsState` mode via `end_vats_for_world_change` (`systems\u002Fvats.rs`: `exit_all()` keeping `roll_state`; #6508 `5a423316f` — an F9 inside VATS left the loaded world frozen on despawned targets; #6625 `6186ab77e` made it the one helper shared with the player-death and cell-transition exits) and drops the crafting session, `CraftingRequest` and `PendingDroppedItems` (#6574 `375d48c6d`) |\n| `registry_completeness_tests::every_component_or_resource_impl_is_saved_or_explicitly_allowlisted` (tail) | #6574: a `NOT_SAVED_BY_DESIGN` reason that says \"a load discards\" \u002F \"The load drain ends\" its type must be named in the body of `discard_outgoing_session_requests` — a claim the sweep does not back fails (the generic guard the earlier audits asked for) |\n| `registry_completeness_tests::the_schema_fingerprint_is_pinned` | `BASELINE_SCHEMA_FINGERPRINT` vs `build_save_registry().schema_fingerprint()`: a registration-row reorder (the #5802 slices' risk) or a column add\u002Fremove\u002Frename fails CI instead of rejecting every save (#6296 `5a3f571f4`) |\n| `registry_completeness_tests::every_delta_column_is_parked_or_explicitly_not` | the **third** list: every `MUTABLE_DELTA_COLUMNS` entry is parked in a named `ReferenceState`\u002F`ScriptState` field (`cell_loader\u002Freference_state.rs`), carried by `ActorStreamSnapshot`, or on `NOT_PARKED` with a reason; every parked field maps to a column or `FIELDS_WITHOUT_A_COLUMN` (#6085 `3b9060701`); no `NOT_PARKED` reason may read `UNAUDITED` — the marker's last row, `Escorted`, is parked now (#6294 `f3ab49f55`) |\n| `command_queue_tests::{a_save_taken_mid_cell_transition_is_refused_not_written, a_save_taken_while_chargen_disables_saving_is_refused_not_written, quicksave_ring_cursor_does_not_advance_on_validation_abort, player_save_actions_wait_for_the_quiescent_fifo_drain, quickload_empty_errors_and_corrupt_newest_falls_back}` | save-side refusal gates, ring, quiescent drain |\n| `live_reload_tests::post_reload_drain_steps_keep_their_order` | source-order pin inside `execute_pending_save_loads`: `restore_resources` → `restore_resident` → `build_form_id_remap` → the `ScriptPackage` pre-seat `apply_deltas` → `reseat_ambient_packages_after_restore` → the full `apply_deltas(.. MUTABLE_DELTA_COLUMNS)` (#5060 `02fa8358c`; the remap and pre-seat moved ahead of the re-seat in #6087 `c7463c04d`; the `reconcile_worn_gear` single-call pin joined it, #5460 `bda5a1167`) |\n| `app_step.rs::the_save_drain_publishes_the_transition_flag_before_draining` | `CellTransitionInFlight` published before the drain |\n| `crates\u002Fsave\u002Fsrc\u002Fsnapshot.rs` `the_bump_ledger_is_contiguous_and_current` | the `FORMAT_MAJOR` doc ledger has every `vN -> vN+1` step with no hole and ends at the constant — a bump that edits only the constant fails (#5459 `a4ccf3b27`) |\n| `*_survives_save_load*` (`round_trip_tests.rs`, `consumable_tests.rs`) | per-type round trips incl. lock\u002Fenable ledgers, fragment\u002Fprovider queues, cinematic trio, `Perks`\u002Ftimed restorations, SM node state + `StoryEventAliasFill` reference FormIds |\n\nNot covered by any guard (the audit's real work): the **two-list drift** between registry and\n`MUTABLE_DELTA_COLUMNS`; staleness of `NOT_SAVED_BY_DESIGN` *reasons*; whether the `NOT_PARKED` reasons of the eviction-row\npartition are *true* (seeded from the delta list by #6085; #6294 `8fc521650` \u002F `f3ab49f55` retired the `UNAUDITED` marker —\nthe guard now rejects it, and `Escorted` is parked; a delta column reverting on every cell exit is the GAME-SD-01\u002F02 class); whether a baseline refresh\nwithout a bump was *justified*; manual `impl Serialize` types (invisible to both serde guards);\nthe extension-state payload's own versioning (Dim 2); semantic correctness of load-apply ordering beyond\nthe two pinned pre-reload cases. (Path-qualified `impl byroredux_core::ecs::Resource for X` impls were\ninvisible to `impl_target_type` until #5255 `6b494d002`: it now parses the trait path's final segment, the nine\nformerly unclassified types are allowlisted, and the guard also asserts every `NOT_SAVED_BY_DESIGN` row matches a\ndiscovered production impl — a renamed\u002Fdeleted type can no longer keep a stale row. Still judge the *reasons*.)\n\n## Parameters \u002F Extra Fields\n\n`--focus \u003Cdims>` (default all 5) · `--depth shallow|deep` (`shallow` = container\u002FAPI contracts; `deep` =\ntrace capture → encode → disk → decode → reload → delta-apply). Finding fields: **Dimension**: Snapshot\nCompleteness | Format & Schema Discipline | Container & Disk | Save-Side Gates | Live Load-Apply ·\n**Data-Loss Class**: silent-drop | corruption-on-load | irrecoverable-write | reference-break | none\n(required for any finding that can lose progress).\n\n## Phase 1: Setup\n\n`mkdir -p \u002Ftmp\u002Faudit\u002Fsave`; dedup: `gh issue list --repo matiaszanolli\u002FByroRedux --limit 200 --json number,title,state,labels > \u002Ftmp\u002Faudit\u002Fsave\u002Fissues.json`;\nread the newest `docs\u002Faudits\u002FAUDIT_SAVE_*.md` (fixed findings are regression checks — verify the fix is\nin place); read the `crates\u002Fsave\u002Fsrc\u002Flib.rs` docstring and `snapshot.rs` container doc — each design claim\nmust be CODE-CONFIRMED or DRIFTED; run the guard ledger. Delta-first: for each dimension\n`git log --since=\u003Clast report> --format='%h %cs %s' -- \u003CPaths>`, skim unchanged ones.\n\n## Phase 2: Dimensions\n\n### Dimension 1: Snapshot Completeness & the Two Lists (highest risk — silent-drop)\nPaths: `byroredux\u002Fsrc\u002Fsave_io\u002Fregistry.rs` (`build_save_registry`, `MUTABLE_DELTA_COLUMNS`), `byroredux\u002Fsrc\u002Fsave_io\u002Flive_load.rs` (`PRE_RELOAD_RESOURCES`), `save_io\u002Fregistry_completeness_tests.rs`, `crates\u002Fsave\u002Fsrc\u002F{registry,driver}.rs`\nFirst step: `git log -p -S'register_' --since=\u003Clast report> -- byroredux\u002Fsrc\u002Fsave_io`; then `git log --since=\u003Clast report> --format='%h %cs %s' -- 'crates\u002F*\u002Fsrc' byroredux\u002Fsrc | grep -i 'component\\|resource\\|state'` to find new persistent state.\n- **The registry IS the completeness contract**; start from the guard's `NOT_SAVED_BY_DESIGN` list, not from a\n  hand enumeration. A persistent, player-mutable component\u002Fresource that is neither registered nor\n  allowlisted is a HIGH silent-drop; a *stale allowlist reason* (the guard checks a reason exists, not that\n  it is true) is the auditable residue — spot-check a sample every run, and re-check any reason that names a\n  \"no production mutator\" claim after new gameplay commits land (P3 loot\u002Fconsumables\u002Fpickups, perks, timed\n  restorations, hardcore mode, persistent reference state were all new saved state; `PlayerNotifications`\n  and `PickedUp` are correctly transient — the durable half of a pickup is the `PersistentReferenceStates`\n  tombstone). Recent false reasons, since fixed: `StoryEventAliasFill` (\"quests restart\n  through fresh events\" — now registered, #5394 `03e51dfad`), `EatSleepState` (\"`resolve_destination` is\n  idempotent\" — restated per PLDT anchor, #5391 `42aab4c09`), `CinematicReAdoption` (\"the teardown handles\n  them\" — the purge now despawns them, #5379 `203be9ed4`); plus the Eat\u002FSleep rows' \"registered Seated restore\"\n  (`Seated` is not overlaid — `restore_world`-only; restated and pinned by the `seated_without_path` check at the tail\n  of the completeness guard, #5458 `0aa844dbb`) and `StoryLocationCursor` (\"a load legitimately re-fires\" —\n  `execute_pending_save_loads` now resets it on every in-process load, #5435 `557ead21d`, pinned in\n  `live_reload_tests.rs`).\n- **Removed-from-allowlist stays registered**: `CharacterController` (fractional breath\u002Fdrowning carry),\n  `Material`, `RumbleOnActivate`, `FragmentExecutionQueue`, the cinematic trio — verify none\n  regressed out of `build_save_registry`, and no re-added `NOT_SAVED_BY_DESIGN` entry re-excludes them.\n- **Two lists, one truth**: the live load overlays only columns in BOTH the registry and\n  `MUTABLE_DELTA_COLUMNS`. A registered mutable column absent from the overlay is captured but never\n  replayed → HIGH silent-drop, unless deliberately excluded and documented at its `register_component`\n  site: `Material` (blast radius), `AnimationPlayer`\u002F`AnimationStack` (stale `root_entity`\u002F`clip_handle`),\n  `FollowState`\u002F`EscortState`\u002F`Seated`\u002Fcinematic pair (`EntityId`, registry handles), `ActorVitals`\n  (write-once FormID key). A column with an `EntityId`\u002Fhandle must also be covered by\n  `validate_saved_entity_references` (Dim 4).\n- **Replacing columns**: `Perks`, `CharacterLevel`, `LevelUpPoints`, `FactionReputation` (#6759), `TimedRestorations`, `Dead`, `ActorControlState`, `DismemberRecord` use\n  `register_replacing_component` (`Dead`\u002F`ActorControlState` since #5027\u002F#5052 `7aa1d7741` — the process-lifetime player\n  outlives the reload, so an additive `Dead`\u002Frestraint row survived a load into an alive save; pinned by\n  `dead_and_restraint_cleared_on_live_player_by_saved_absence`) — saved absence is\n  authoritative for FormID-matched entities (empty column kept in the snapshot; a *missing* column is not a\n  tombstone). Ordinary columns are additive-only, so **every runtime removal of an overlaid component needs a\n  reconciler** (`reconcile_dead_actor_runtime_state`, `reconcile_player_equipped_weapon`); the guard enforces\n  disposition, this dimension judges whether \"NoReconcilerNeeded\" reasons are true (carrier destroyed by\n  the cell reload?) and whether a new persisted fact (disable, pickup, lock) chose the marker-plus-\n  reconciler or FormID-keyed-ledger model (`ReferenceEnableState`, `ReferenceLockState`,\n  `PersistentReferenceStates` — resources keyed by FormID survive cell unload; a component would not). A saved\n  resource that needs session `EntityId`s takes the `StoryEventAliasFill` shape (#5394): `StoryEventFill`\n  `serde(skip)`s its `EventDataSlots` and saves the R1\u002FR2 reference FormIds, which the alias refresh re-resolves.\n  The overlay emits no `EquipmentEventBatch`: presentation derived from an overlaid column is re-derived by\n  `reconcile_worn_gear` (`byroredux\u002Fsrc\u002Fnpc_spawn\u002Floot_appearance.rs`, #5034 `20717d5b7`) — run on the player after\n  `apply_deltas` and on NPCs from `reference_state::restore`; it diffs live roots against restored slots\n  (reveal \u002F hide \u002F queue `PendingGearImport`) and synthesizes no event batch. A new presentation fact derived\n  from an overlaid column needs the same treatment. Re-derived-not-saved player state: `FactionRanks` is reset\n  to the record by `reset_player_factions_to_record` after the overlay (#5058 `be3cd9468` — alias-injected\n  ranks otherwise outlived the load).\n- **P3\u002FP4 runtime state (2026-09-29)**: player body (`PlayerBodyRoot`, `PlayerBodyRootEntity`, `HiddenFirstPerson`,\n  `PlayerCameraView`), mid-life gear import (`NpcSkeletonBones`, `ActorBodyClass`, `PendingGearImport`,\n  `PendingGearRelease` — both dropped together on load by `clear_player_gear_handoff_scratch`, #5255 `6b494d002`,\n  `PendingInventoryActions`) and NPC dialogue (`NpcDialogueTopic`, `DialogueSurfaceState`) are all\n  `NOT_SAVED_BY_DESIGN`; `ObjectiveHudCache` is an `App` field, not a Resource. Verify each reason (body subtree\n  entities have no `FormIdComponent`: captured by `save_world`, never remapped). `RigidBodyData` is\n  `NOT_SAVED_BY_DESIGN` since v116 (#6237 `6765e60eb`): every carrier is a `PhysicsSourceForm` collision entity the live\n  overlay can never resolve, so #2379's column only ever round-tripped through `restore_world`; a script's\n  `SetMotionType` lives in the `ReferenceMotionTypeState` ledger (`PRE_RELOAD_RESOURCES`), applied by\n  `cell_loader::spawn::scripted_motion_type` to each collider. A re-registered `RigidBodyData` column is the\n  regression.\n- **Determinism** (the `Snapshot` doc claims reproducible CRCs at equal state): `Snapshot` maps are\n  `BTreeMap` and component rows are sorted by entity id (`registry.rs`), but saved **resources** serialize\n  as-is — at 2026-09-19 `Globals(HashMap\u003Cu32, f32>)`, `QuestStageState`, `ReferenceEnableState` (`HashSet`),\n  `ReferenceLockState` and `PersistentReferenceStates::pair_rows` all emit hash-iteration order (since joined by\n  `DialogueSpokenInfoForms` (`HashSet`) and `StoryEventAliasFill` (`HashMap`)), so two saves\n  of equal state can differ in bytes\u002FCRC. #4748 is closed as a doc fix: the `Snapshot` doc in `snapshot.rs` now\n  says the payload \"is not guaranteed deterministic for equal logical state\" (`Globals` is still a `HashMap`, by\n  design of that resolution). A new claim of reproducible CRCs, or a consumer that diffs\u002Fhashes saves, reopens\n  it (MEDIUM doc\u002Fcontract mismatch, not data loss). Do not claim determinism at the row level without checking this.\n- **`next_entity`** is saved verbatim and replayed via `set_next_entity` before inserts; `insert_batch`'s\n  `entity \u003C next_entity` is a `debug_assert` only (release inserts at an unspawned id silently — MEDIUM);\n  `StringPool::dump`\u002F`from_dump` preserves symbol order (a reordered dump = every `Name` wrong = CRITICAL).\n**Output**: `\u002Ftmp\u002Faudit\u002Fsave\u002Fdim_1.md`\n\n### Dimension 2: Format & Schema Discipline (registry fidelity + `FORMAT_MAJOR`)\nPaths: `crates\u002Fsave\u002Fsrc\u002F{snapshot,registry}.rs`, `save_io\u002Fserde_default_guard_tests.rs`, `crates\u002Fsave\u002FCargo.toml`, `crates\u002Fcore\u002FCargo.toml`\nFirst step: `cargo test -p byroredux --bin byroredux serde_default_guard` ; `git log --since=\u003Clast report> --format='%h %cs %s' -- crates\u002Fsave\u002Fsrc\u002Fsnapshot.rs`\n- **Bump rule** (read the `FORMAT_MAJOR` doc comment in `snapshot.rs`: 124 as of 2026-11-09, with one `vN -> vN+1`\n  entry per bump from v22 on, contiguous since #5459 `a4ccf3b27` restored the v32→v33 entry (#5367 `14cff35ae`, new saved\n  resource `DialogueSpokenInfoForms`) and pinned it with `the_bump_ledger_is_contiguous_and_current`. Recent anchors: v36 Wave 4 `RigidBodyData.surface_material`, v37 #5542\n  widened it `u8`→`u32`, v38 #5300 re-keyed `DialogueSpokenInfoForms` to a (speaker ref, INFO) pair, and v39–v104\n  are almost entirely M47.2 scripting state — new saved resource columns, `Effect` \u002F `FragmentGuard` \u002F `ValueExpr`\n  variants and fields reachable through queued fragment continuations (`FragmentExecutionQueue`), `CharacterLevel`\n  v46 (kill XP ended #2947's exemption), `Bleedout` v51, `LevelUpPoints` v53; v105–v124 are gameplay\u002FObScript\n  fields and columns (`Bleedout.remaining_secs` → `Option` v105, parked `ReferenceState.bleedout` v106, M47.3 ObScript\n  columns v109–v112, `Material.source_shader_type` v113 #4256, parked `ReferenceState` script package \u002F corpse pose +\n  `PersistentReferenceStates` script rows v114 #6071–#6073 `611769b16`, parked `ReferenceState` magic effects \u002F\n`weapon_drawn` \u002F `ignore_crime` + `ScriptState` timer \u002F rumble v115 #6085 `3b9060701`; `RigidBodyData` out,\n`ReferenceMotionTypeState` in v116 #6237 `6765e60eb`; v117 #6190 `ObScriptLedger` lost four unread columns; v118 Oblivion\njail — `ObScriptLedger.stolen_goods` \u002F `evidence` \u002F `jail`; v119 the `CrimeKnowledge` resource joined the registry; v120 the `KnownWords` resource joined it and `Effect` gained `ObtainWord`; v121 `SmNodeRuntime.last_node_fire_hours`; v122 `ItemInstance.quality`; v123 #6759 `8fc1a9ef9` the `FactionReputation` replacing column (the stale \"no mutation site\" exemption); v124 d50fc1153 the `DismemberRecord` replacing column plus a parked `ReferenceState.dismember` field, VATS-LIMB-008\u002F-009); do not hardcode the number elsewhere). Precedent: v112\n  (#6050 `5f8bbf454`) — `register_saved` columns landed after a baseline refresh with no bump and the shape guard sat\n  red on main; a new registered column's defining file joins the shape scan, so it is a bump here too. Intra-type shape changes need a bump because\n  `schema_fingerprint` hashes only column keys (+ replacing policy). A new required field, retyped field, or new `Option` in a saved\n  type bumps; `#[serde(default)]` is forbidden as a compatibility mechanism (guard) — even where the default\n  would be correct for every old save. A saved map keyed by a tuple\u002Fstruct cannot be a JSON object (`serde_json` refuses the key once it holds a row, failing the\n  whole snapshot — the FNV `SetAlly` quicksave failure): such maps go through `byroredux_scripting::serde_pairs` as\n  `[key, value]` lists, which also read an empty object as the empty map; `4156e68cd` moved the shape baseline with no bump\n  on that ground (a non-empty map never wrote, so no old save can hold one) — judge any similar refresh against it.\n  Read-compatible changes move the shape baseline **without** a bump:\n  adding an enum variant to an externally-tagged `serde_json` enum (keyed by variant *name*, never ordinal —\n  #4140 corrected the old \"discriminant shift\" rationale), removing a field (no `deny_unknown_fields` on any\n  saved type), renaming with `serde(alias)`. Changing an existing variant's field shape is not compatible.\n  Adding\u002Fremoving a registered column changes the fingerprint and rejects old saves via `SchemaMismatch`\n  with no bump. An audit proposing to relax the blanket rule for a \"safe\" default reopens #1714.\n- **Baseline refresh triage** (the guard cannot judge this): every `BASELINE_SHAPE_FINGERPRINT` change must\n  carry a justification comment stating whether a registered column's shape changed. Known false-positive\n  classes: (a) the guard hashes *every* serialized-derive type in a scanned file, including `NOT_SAVED_BY_DESIGN`\n  types sharing it (e.g. `AnimatedTextureFlip`; inspect-only files such as `WaterMaterial`'s left the scan in\n  573170e1c) — those move the hash with no snapshot impact; (b) a **tuple struct** sweeps the following `impl` block into its span (`VisibilityMask`) — method\n  edits move the hash; (c) the hash includes the file-relative path, so moving a save-participating type\n  between files moves it. Verify baseline and `FORMAT_MAJOR` were updated in the same commit, and that a\n  refresh-without-bump was not hiding a real field change to a registered type.\n- **Guard blind spots**: manual `impl Serialize` (no derive) types; a nested payload type of a plain-`derive`\n  registered type (the `byroredux\u002Fsrc` pattern) defined in another file is outside `save_type_sources()` unless\n  added to its explicit list (#5059 fixed in `945be7dc6` for `LightSource.emitter` and the `crates\u002Fsdk`\n  `FormRef` \u002F `ScriptValue` payloads — the next such nested payload is the residue; the extension-manifest\n  declaration types sharing `script_function.rs` are exempt from the `serde(default)` scan, with a staleness\n  test); the shape scan once skipped a save derive that was not the *last* `#[…]` attribute (#5403 fixed in\n  `e19cb0491`, coverage markers assert `StoryManagerNodeState` \u002F `SmNodeRuntime` contribute shapes); a new `Option\u003CT>` field is hashed but\n  looks like a routine baseline bump — confirm it got the bump; `serde(alias)` must never coexist with a\n  dropped value semantic change.\n- **Second payload — extension state** (`Snapshot.resources[\"ByroExtensionState\"]`, written by\n  `extensions::capture_extension_state`, type `ExtensionStateSnapshot` in `crates\u002Fsdk\u002Fsrc\u002Fcomponent.rs`):\n  it is outside the registry, the completeness guard, and both serde guards (sdk types carry a plain\n  `derive`, so `save_type_sources()` drops them), and follows its **own** policy — a `format_version` range\n  (`MIN_EXTENSION_STATE_FORMAT_VERSION..=EXTENSION_STATE_FORMAT_VERSION`) with `#[serde(default)]` fields, which\n  the blanket rule forbids for registry types. Verify: the range gate runs in `preflight_extension_state`\n  (before teardown); every shape change to its rows bumps `EXTENSION_STATE_FORMAT_VERSION` (nothing enforces\n  it — no shape baseline); rows without stable authored identity refuse the save rather than dropping;\n  `decode_saved_state` treats a missing key as empty but a present-and-malformed one as an abort.\n- **`ValidateFn` parity**: every `register_*` variant builds a `validate` closure that decodes the SAME type\n  `load` does (`Vec\u003C(u32, T)>` for columns, bare `T` for resources) — a missing\u002Fwrong one exempts a column\n  from the preflight so it fails only after teardown.\n- **Feature chain**: `crates\u002Fsave` → `byroredux-core` `features=[\"save\"]` (→ `inspect`) must still hold, or a\n  non-default build compiles away serde impls and columns serialize to `null`.\n- **FormId handle vs pair**: `register_form_id_component` saves the stable `FormIdPair` (skips unresolvable\n  handles with WARN, never panics); load re-interns to fresh handles and errors cleanly without a pool.\n  `form_id_column()` is keyed by the explicit `is_form_id` flag, never \"first `apply: None` entry\".\n- `FnvHasher` uses canonical FNV-1a 64 constants and hashes only names\u002Forder\u002Fpolicy (no TypeId\u002Faddress).\n**Output**: `\u002Ftmp\u002Faudit\u002Fsave\u002Fdim_2.md`\n\n### Dimension 3: Container & Disk Durability\nPaths: `crates\u002Fsave\u002Fsrc\u002F{snapshot,disk}.rs`, `crates\u002Fcore\u002Fsrc\u002Fatomic_file.rs`, `crates\u002Fsave\u002Ftests\u002Fround_trip.rs`\nFirst step: `cargo test -p byroredux-save disk:: snapshot::`\nGuarded (see ledger): header gate order (length → magic → major → schema fingerprint → `payload_len`\nchecked bounds → CRC over the payload only → `serde_json::from_slice`), advisory `minor`, slot-name strictness\n(`save_42.ess.tmp` rejected), `slots_by_recency` newest-first with slot-number tie-break, ring wrap\u002Fresume.\nResidual checks:\n- **`atomic_write`** (`crates\u002Fcore\u002Fsrc\u002Fatomic_file.rs`; shared with `crates\u002Fsettings-io`,\n  `crates\u002Fboot-request`, `crates\u002Fgame-detect\u002Fsrc\u002Foverrides.rs`) order is exactly: create tmp → `write_all` →\n  `flush` → `sync_all` → byte-exact read-back → `rename` (all inside `stage_and_rename`) → parent-directory\n  fsync. **Any** failure up to the rename removes the temp (#5163 `69fd54fb2`; `failed_rename_removes_the_temp`),\n  and no caller may fall back to copying\u002Frenaming the temp over the target on error — the old \"Windows rename\"\n  fallback clobbered good files on disk-full (#5143 `2e95f0bbf`). The three non-save writers pin that with\n  `assert_no_clobber_fallback` (production text only, via `core::source_scan`); `write_slot` is pinned by\n  `write_slot_has_no_clobber_fallback` and stages through the unique `atomic_temp_path` (#5247 `eb08e14fd`;\n  the old fixed `save_\u003Cslot>.ess.tmp` raced two processes sharing a save dir) after `create_dir_all`. Rename-before-fsync, a length-only read-back, or a new fallback branch is a HIGH\n  durability hole.\n- **Ring never clobbers the last good save**: `SaveState::new` must build the ring via `SaveRing::resume`\n  (not `new`), and the cursor advances only after a committed write (`quicksave_ring_cursor_does_not_advance_on_validation_abort`).\n- **Minor-version + default-fill**: `decode` accepts newer minors and serde default-fills; that is the exact\n  path `#[serde(default)]` would exploit — cross-check Dim 2's guard is live.\n- `SAVE_DIR_ENV` (`BYROREDUX_SAVE_DIR`) redirects the ring; smoke gates depend on the spelling.\n**Output**: `\u002Ftmp\u002Faudit\u002Fsave\u002Fdim_3.md`\n\n### Dimension 4: Save-Side Gates (refusal + validation)\nPaths: `byroredux\u002Fsrc\u002Fsave_io\u002Fcommands.rs` (`SaveCommand::execute`, `validate_form_ids`, `validate_cinematic_entity_refs`), `crates\u002Fsave\u002Fsrc\u002Fvalidate.rs`, `byroredux\u002Fsrc\u002Fapp_step.rs` (`step_player_save_actions`)\nFirst step: `grep -n 'fn validate_\\|validate_[a-z_]*(world' crates\u002Fsave\u002Fsrc\u002Fvalidate.rs byroredux\u002Fsrc\u002Fsave_io\u002Fcommands.rs`\n- **Order in `SaveCommand::execute`**: refusals first (`CellTransitionInFlight` — a mid-transition save would be\n  written and permanently unloadable; `CinematicPresentationState.disable_saving` — `Game.SetInChargen`\n  `abDisableSaving`; plus the context-invariant refusal #5253 `27562f8ae` — `LoadedCellIndex` present but neither\n  `CurrentCellContext` nor `CurrentExteriorContext` means an unloadable snapshot even when the synced flag still\n  reads `false`, as in the console ingresses that dispatch outside the drain), then `validate_world` + `validate_form_ids` + `validate_cinematic_entity_refs`\n  (abort, up to 20 lines, nothing written), then `save_world` → `capture_extension_state` → `encode` →\n  `write_slot`; ring advance and the `SaveComplete` session event only after the commit. Any alternate save\n  path that bypasses the gates (console, player action, SDK) is HIGH; input adapters must enqueue through\n  `queue_player_save_action` (post-scheduler drain — `save_world` takes ~30 storage + resource read locks, so\n  a live scheduler lane needs the ABBA analysis re-derived; `\u002Faudit-concurrency`). Current ingresses: the\n  winit F5\u002FF9 intercept, the pause menu, and the debug `input.press quicksave|quickload`, which resolves\n  through `ActionBindings` to the same F5\u002FF9 edges (`queue_debug_action_press`, `byroredux\u002Fsrc\u002Finteraction.rs`,\n  `bae84e54b`; `quicksave_press_joins_the_player_save_queue_through_the_f5_binding`).\n- **Coverage**: `validate_world` currently runs hierarchy, equipment, saved-entity references (session-local\n  `EntityId` fields of columns excluded from the overlay — grep the function for the live column list),\n  animation (`AnimationPlayer`, `AnimationStack` root + layer clips, `Seated.animation_restore`),\n  inventory instances vs `ItemInstancePool`, and `Material` finiteness (the progression check\n  `validate_progression_state` retired in `dbe562c1f` when `CharacterLevel` became a saved replacing column, v46);\n  the binary adds FormId resolvability and cinematic `EntityId` refs. Do\n  not restate the list elsewhere — enumerate **inter-entity reference fields in any newly registered type\n  not covered** (MEDIUM defense-in-depth gap) and any check that flags a legitimately sparse-but-spawned id\n  (dangling = `>= next_entity`, not \"no components\").\n- **Post-load validation is diagnostic-only** (`log_validation_warnings`, WARN, no abort) in both\n  `restore_world` and `execute_pending_save_loads`; the **typed preflight aborts** (before teardown).\n- **Transient state**: saves must not depend on transients — `PlayerNotifications`, pending action queues,\n  `CellTransitionInFlight`, parked `StreamStateSnapshots` are unsaved by design; a save taken while one is\n  non-empty must still round-trip (check new transient resources land on the allowlist, not the registry).\n**Output**: `\u002Ftmp\u002Faudit\u002Fsave\u002Fdim_4.md`\n\n### Dimension 5: Live Load-Apply & Frame Boundary\nPaths: `byroredux\u002Fsrc\u002Fsave_io\u002Flive_load.rs` (`execute_pending_save_loads`, `reload_*_session`), `byroredux\u002Fsrc\u002Fsave_io\u002Fplayer.rs` (`apply_player_pose`), `crates\u002Fsave\u002Fsrc\u002Fdriver.rs`, `byroredux\u002Fsrc\u002F{app_events,app_step,app_frame}.rs`, `byroredux\u002Fsrc\u002Fcell_loader\u002F{transition,reference_state,unload}.rs`\nFirst step: read `execute_pending_save_loads` top to bottom against the sequence below; `git log --since=\u003Clast report> --format='%h %cs %s' -- byroredux\u002Fsrc\u002Fsave_io`\n- **Strict apply sequence**: drain slot → `validate_snapshot_types` (abort, session kept) →\n  `preflight_extension_state` (abort) → `restore_resources_subset(PRE_RELOAD_RESOURCES)` (only resources\n  read by spawn-time code during the reload: `ReferenceEnableState`, `ReferenceLockState`, `ReferenceScriptState`,\n  `ScriptedFactionChanges` — #5919 `aa14d3e89`, replayed by `stamp_quest_reference` as NPCs respawn — and\n  `ReferenceMotionTypeState`, #6237, read by `scripted_motion_type` as each collider is built;\n  `pre_reload_restore_installs_the_scripted_faction_ledger`; **never**\n  `ItemInstancePool` — teardown's `release_victim_item_instances` needs the live pool, #4135; the live values are\n  captured first as `LiveReloadLedgers` and reinstated if this restore or the reload below fails\u002Frefuses, so a\n  \"keeping the current session\" abort does not leave the other timeline's ledgers live — #6081 `b5a205715`) →\n  `without_parked_state` wraps the reload (outgoing session's `PersistentReferenceStates` and\n  `StreamStateSnapshots` are set aside and restored on failure) → teardown + reload (`validate_cell_loadable`\n  preflight first, so a missing ESM\u002Fcell keeps the live session; then `purge_cinematic_retention_state`\n  drops every `ActorCinematicState`\u002F`HorseTetherState` row so the convoy is not retained across the teardown\n  as a ghost twin sharing a `FormIdPair` — #5056 `a197e8563`, both reload arms + both debug-load paths, never\n  ordinary cell transitions (`purge_wiring_tests`) — and despawns + clears the unsaved `CinematicReAdoption`\n  pending list, whose un-rooted entities the `CellRootIndex` teardown cannot enumerate (#5379 `203be9ed4`).\n  Since #5384 that list holds parentless roots only, so the purge (now taking the `VulkanContext`) collects each\n  root's `Children` closure and hands it to `release_entities` — subtrees no longer orphan (which made\n  `validate_world` refuse every later save) and GPU\u002FRapier handles are released, #5490 `99cd9fd6c`. The teardown never\n  despawns the player rig: `unload_cell_inner` refuses `player_owned_entities`, #5483 `5a68e983e`)\n  → `reset_story_location_cursor` + `discard_pending_story_events` (the unsaved CLOC cursor and any straddling\n  `StoryEvent` markers describe the outgoing session; every load behaves like a fresh boot, #5435 `557ead21d`)\n  → `discard_package_reports` (outgoing actors' package transitions must not run PF_ Begin\u002FChange fragments in the\n  loaded session, #5864 `001de8741`) → `discard_pending_unload_deliveries` (the teardown's own `OnCellDetach`\u002F\n  `OnUnload` queue; Bethesda raises no unload event for a load, #6083 `4b8e62d87`) →\n  `discard_pending_cell_resets` (the reload's cell-root `note_player_entered_cell` calls ran against the outgoing\n  `CellVisits`\u002Fclock; the restored `CellVisits` is authoritative, #6238 `078df54e1`) → `discard_outgoing_session_requests`\n  (value-keyed `PlayerMoveRequest` \u002F XP \u002F skill \u002F sleep \u002F music \u002F message-box queues that would otherwise apply in the\n  loaded world and be kept by the next save, #6239 `337bf25c8`; plus jail-evidence deposits, mine hits, VATS mode (`end_vats_for_world_change`), the crafting session\n  and pending dropped items, #6573 \u002F #6540 \u002F #6508 \u002F #6574; the test pins all but the mine queue)\n  → `restore_extension_state` → wholesale\n  `restore_resources` **again** (idempotent; re-asserts `CurrentCellContext`\u002F`PlayerPose`; the second call is\n  not redundant) → `restore_resident` (parked rows for resident placements, #4695; followed by `combat::release_held_dead_actor_reconciliations`, which hands a corpse's held reconciliation to the Late-stage sink now that its placement is resident, #6133 `1e2c1d30e`) → `drop_entity_bound_continuations` on `PapyrusProviderContinuationQueue` (session-local\n  `EntityRef` handles must not resume, #4139) → `build_form_id_remap` → a `ScriptPackage`-only `apply_deltas` pre-seat (the ambient selection honours a running\n  script package over the schedule, so re-seating without it stamped the scheduled winner and the next minute's tick\n  cleared the freshly overlaid `TravelState`\u002F`Traveled` markers and reported a spurious package-change fragment; the column\n  is replacing and idempotent, so the full overlay re-applies it harmlessly — #6087 `c7463c04d`) → `reseat_ambient_packages_after_restore` (re-pick packages\n  against the restored clock *before* the overlay, #4815; its order against `restore_resident`, the remap, the pre-seat and the\n  overlay is pinned by `post_reload_drain_steps_keep_their_order`, #5060 `02fa8358c`) → park the snapshot rows\n  whose `FormIdPair` did not resolve (`unresolved_form_id_pairs` → `park_unresolved_snapshot_rows` into\n  `PersistentReferenceStates`, #5054 `17e720430` — the exterior reload streams only `radius_load`, so the\n  save's hysteresis-band rows were dropped and their loot duplicated; `unresolved_snapshot_rows_are_parked_for_the_next_respawn`)\n  → `apply_deltas(MUTABLE_DELTA_COLUMNS)` → dead\u002Fequipped-weapon reconcilers, `reconcile_worn_gear` (called\n  once; the duplicate was dropped in `a8b3d1902` and the count pinned by #5460 `bda5a1167`), `reset_player_factions_to_record` → `reapply_scripted_faction_changes` (restored scripted memberships on top) →\n  `rebaseline_actor_watch` (outgoing bleedout\u002Fcombat sets and player location must not deliver across timelines,\n  #6083) → diagnostic `validate_world` + `validate_form_ids` + `validate_cinematic_entity_refs` →\n  `apply_player_pose` LAST (after the\n  overlay of `CharacterController`, whose motion fields pose-restore then zeroes; a new field on either\n  side needs an explicit decision). Any failure after the reload returns immediately, never falling through\n  into pose-restore on a partial overlay. Idempotency: teardown is unconditional, so a second load of the same\n  slot does not stack deltas.\n- **Two restore paths**: live load uses `restore_resources` + `apply_deltas` (saved ids remapped to the\n  reloaded cell's fresh ids); `restore_world` (clear + repopulate at saved ids) is test\u002Floose only and must\n  never be reachable from `execute_pending_save_loads` (id collision = CRITICAL). `clear_entities` does not\n  free GPU\u002Fphysics handles — the live path's `unload_current_interior`\u002F`drain_streaming_state` must.\n- **Remap**: `build_form_id_remap` matches saved `FormIdPair` → live entity; rows without a form id are\n  skipped (respawned by the loader); unresolved pairs are logged (WARN, first 20) — a moved object vanishing\n  must stay diagnosable; the player body carries `PLAYER_FORM_ID_PAIR` from spawn so it participates.\n  Persistent-reference rows: resident rows arrive via the component overlay, nonresident via the resource\n  restore; rows are consumed on respawn (semantics → `\u002Faudit-gameplay`).\n- **Frame boundary**: capture runs post-scheduler (`step_player_save_actions` drains a FIFO after\n  `Scheduler::run`; remote console runs in the exclusive `DebugDrainSystem`); `capture_player_pose` reads\n  post-propagation `Transform` before the drain; `PlayerNotifications` drain in `render_one_frame` is\n  unconditional (bounded ring of 8, oldest dropped). The load drain lives in `step_save_loads` where the\n  App owns `&mut World` + `&mut VulkanContext` and is skipped while an interior transition or foreign\n  loading screen is active; the single `PendingSaveLoadSlot` is last-writer-wins and reports supersession.\n- **Pose**: yaw\u002Fpitch always to `InputState`; Character mode + live body → `place_character_body`\n  (`byroredux\u002Fsrc\u002Fsystems\u002Fcharacter.rs`, the one teleport helper since #5591 `946310eb9`: body `Transform` +\n  `GlobalTransform` + `CharacterController::clear_momentum` — `vertical_velocity`\u002F`is_grounded`\u002F`wants_jump` —\n  + kinematic Rapier sync, no-op without a handle); restore branches on the *live* mode (#2018), so\n  FlyCam-saved\u002FCharacter-loaded relocates the body and the reverse repositions the camera. `apply_player_pose`\n  returns `bool` and refuses the whole pose (non-finite or out-of-world position — `PhysicsWorld::translation_is_sane`,\n  the keyframe bound — or non-finite yaw\u002Fpitch), leaving the reloaded cell's spawn (#5530 `d28545728`,\n  `a_corrupt_saved_pose_is_refused_and_leaves_the_player_where_the_cell_spawned_them`).\n- **Context guards**: `LoadCommand` accepts interior OR exterior context and rejects only neither;\n  `SaveInfoCommand` mirrors the three arms (#3500); `quickload_latest` walks `slots_by_recency` and falls back\n  past a corrupt newest slot; `command_output_is_failure` scans every output line.\n**Output**: `\u002Ftmp\u002Faudit\u002Fsave\u002Fdim_5.md`\n\n## Phase 3: Merge\n\nCombine `\u002Ftmp\u002Faudit\u002Fsave\u002Fdim_*.md` into `docs\u002Faudits\u002FAUDIT_SAVE_\u003CTODAY>.md`: **Executive Summary** (each\n`lib.rs` design claim CODE-CONFIRMED\u002FDRIFTED; findings by severity and Data-Loss Class) · **Data-Loss\nClass Matrix** · **Completeness Ledger** (registry × `MUTABLE_DELTA_COLUMNS`: SAVED-only \u002F SAVED+OVERLAID \u002F\nstructural; cross-check against the guard's allowlist rather than re-deriving) · **Findings** (CRITICAL\nfirst, deduplicated; two-list drift owned by Dim 1, GPU\u002Fphysics teardown by Dim 5) · **Guards\nVerified** (which ledger guards were confirmed live, which reasons were spot-checked).\n\n## Phase 4: Cleanup\n\n`rm -rf \u002Ftmp\u002Faudit\u002Fsave`; tell the user the report is ready; suggest\n`\u002Faudit-publish docs\u002Faudits\u002FAUDIT_SAVE_\u003CTODAY>.md` (domain label `save-load`; `test-gap` for coverage\nfindings, `doc-rot` for drifted save\u002Fload docs).\n",{"description":7,"argument-hint":44},"--focus \u003Cdimensions> --depth shallow|deep","main","6afdb791976ed2c24a6c7e61fcbe0cd1f8776061",".claude\u002Fcommands\u002Faudit-save",6,"https:\u002F\u002Fgithub.com\u002Fmatiaszanolli\u002FByroRedux","https:\u002F\u002Fgithub.com\u002Fmatiaszanolli","matiaszanolli.com","15+ years turning mate into code, and just getting started! All things Python and data lover overall. Music addict, always looking to unite music and tech.","2026-11-12 21:39:17",{"source":8,"skillName":5,"commands":55,"prompt":65,"zipAvailable":66},[56,59,62],{"tool":57,"command":58},"npx","npx skills add matiaszanolli\u002FByroRedux --skill audit-save",{"tool":60,"command":61},"bunx","bunx skills add matiaszanolli\u002FByroRedux --skill audit-save",{"tool":63,"command":64},"pnpm","pnpm dlx skills add matiaszanolli\u002FByroRedux --skill audit-save","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmatiaszanolli\u002FByroRedux\u002Fblob\u002Fmain\u002F.claude\u002Fcommands\u002Faudit-save\u002FSKILL.md",true,[],[69,78,87,96,105,113],{"skillNo":70,"name":71,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":72,"categories":73,"keywords":74,"publishedAt":39},"01M6K028GPYDYZ5441BW6YDFNK","trace-flow","https:\u002F\u002Fgithub.com\u002Fmatiaszanolli\u002FByroRedux\u002Fblob\u002Fmain\u002F.zcode\u002Fcommands\u002Ftrace-flow\u002FSKILL.md",[],[75,76,77],"trace flow","trace","flow",{"skillNo":79,"name":80,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":81,"categories":82,"keywords":83,"publishedAt":39},"01M6K026GTTXBBXXCH09SDRNX6","session-close","https:\u002F\u002Fgithub.com\u002Fmatiaszanolli\u002FByroRedux\u002Fblob\u002Fmain\u002F.zcode\u002Fcommands\u002Fsession-close\u002FSKILL.md",[],[84,85,86],"close","session close","session",{"skillNo":88,"name":89,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":90,"categories":91,"keywords":92,"publishedAt":39},"01M6K024KA1JFTV7BGYFSZV2VV","legacy-lookup","https:\u002F\u002Fgithub.com\u002Fmatiaszanolli\u002FByroRedux\u002Fblob\u002Fmain\u002F.zcode\u002Fcommands\u002Flegacy-lookup\u002FSKILL.md",[],[93,94,95],"lookup","legacy lookup","Legacy",{"skillNo":97,"name":98,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":101,"publishedAt":39},"01M6K022H36BJ35KB6MT2NHVH6","fix-issue","https:\u002F\u002Fgithub.com\u002Fmatiaszanolli\u002FByroRedux\u002Fblob\u002Fmain\u002F.zcode\u002Fcommands\u002Ffix-issue\u002FSKILL.md",[],[102,103,104],"issue","fix","fix issue",{"skillNo":106,"name":107,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":108,"categories":109,"keywords":110,"publishedAt":39},"01M6K0208JHG4WDJX143YKFF0W","audit-ui","https:\u002F\u002Fgithub.com\u002Fmatiaszanolli\u002FByroRedux\u002Fblob\u002Fmain\u002F.zcode\u002Fcommands\u002Faudit-ui\u002FSKILL.md",[],[111,112,21],"audit ui","ui",{"skillNo":114,"name":115,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":116,"categories":117,"keywords":118,"publishedAt":39},"01M6K01XPHAPHF9BPS4CG5DDWX","audit-tooling","https:\u002F\u002Fgithub.com\u002Fmatiaszanolli\u002FByroRedux\u002Fblob\u002Fmain\u002F.zcode\u002Fcommands\u002Faudit-tooling\u002FSKILL.md",[],[21,119,120],"audit tooling","tooling"]