---
description: "Per-game audit of Fallout 3 compatibility — NIF v20.2.0.7, BSA v104, ESM via FNV-shared parser"
argument-hint: "--focus <dimensions>"
---

# Fallout 3 Compatibility Audit

FO3 rides the **FNV path** almost end-to-end (NIF v20.2.0.7 / BSVER 34, BSA v104, one ESM parser, one cell loader, one RT pipeline). This is NOT a re-run of `/audit-fnv`: it hunts the **divergences** — where FO3 content exercises a path FNV doesn't, where shared code carries an FNV-only assumption, and where a feature is verified on FNV but only *assumed* on FO3. Its data goes through shared mechanisms owned elsewhere (`.claude/commands/_audit-owners.md`); a defect in the mechanism is filed against its owner, but **tag its FO3 reach**: a shared-code regression hits all four classic games (the 2026-09-19 HIGH did).

**Architecture**: Orchestrator. Each dimension runs as a Task agent (max 3 concurrent).

Read `.claude/commands/_audit-common.md` (layout, methodology, dedup, finding format) and `.claude/commands/_audit-severity.md` for shared protocol.

## Game Context

| Aspect | State |
|---|---|
| NIF / BSA | v20.2.0.7 BSVER 34 · BSA v104 · 7 mesh/texture archives (base + 5 DLC `- Main.bsa`) |
| Parse baselines (pull) | `crates/nif/tests/data/per_block_baselines/fallout_3.tsv` (17 172 NIFs, 6 mesh archives, 145 types, 0 unknown — the gate walks `open_all_mesh_archives`, #3041), `ROADMAP.md` FO3 row, `docs/feature-matrix.md`, `.claude/audit-baselines/runtime/fo3-MegatonPlayerHouse.tsv` |
| Reference data | `/mnt/data/SteamLibrary/steamapps/common/Fallout 3 goty/Data/` |
| FO3 vs FNV | Both are `GameKind::Fallout3NV`. The **only** discriminator is `CharacterRulesProfile` (`FALLOUT3` vs `FALLOUT_NEW_VEGAS`, #4448). A divergence branch keyed on `GameKind` cannot tell them apart; pins: `obscript_dialect_follows_the_profile_not_the_game_kind` (`byroredux/src/cell_loader/references/attach.rs`), `fn586_condition_gate_is_profile_scoped_not_game_scoped` (`crates/plugin/src/consumables.rs`) |

**Authoring census** (measured — each line is a checklist premise that has already rotted once; re-measure before asserting the opposite):
- Inline shader stack only: **0 BGSM/BGEM** in the FO3 archives; **0** `bump_texture`; no bump-tiling field exists on any `BSShader*Property`.
- **0 `XATO` / `XTNM` / `XTXR`** in `Fallout3.esm` — no REFR texture overlays (#3511); `byroredux/src/cell_loader/refr_texture_overlay_tests.rs` fixtures are FO4-shaped and prove nothing about FO3.
- `Fallout3.esm`: 718 952 records (`HEDR.numrec` 808 699 = records + GRUPs + the TES4 header), 1 257 SCPT, 54 SCOL bases / 389 REFRs, 244 TXST records (243 parsed — Bethesda's own *NullTextureSet* is skipped), 51 LTEX all carrying `TNAM` (the 51 `landscape_texture_sets` entries).
- NIF: 1 374 `BSSegmentedTriShape` (#3101's "zero" measured one archive), 112 `bhkConvexListShape` chains, **0 `bhkMultiSphereShape`**, B-splines are 83% of base-archive `.kf` — never rule them out by era.
- Object LOD: **114** `<world>.levelN.high.x<X>.y<Y>.nif` quads that no code path consumes (54 in `Fallout - Meshes.bsa` — 28 `washmontop.level8`, 26 `wasteland.level4`; 60 in `Anchorage - Main.bsa`), every one with a plain sibling — censused-not-consumed by design (#4468, closed; `probe_lod_corpus`).
- **Fixed 2026-10-19 — regression guard, do not re-file**: #4772 (a copied-save restore's first ragdoll solve threw the root to ~1e12 BU on FO3; root-caused by #5536/#5537/#5538, and `fc9622f6f` makes `docs/smoke-tests/p2-melee-core.sh` assert, under `fo3.env`'s `P2_RAGDOLL_JOINTS=17`, every restored sample has all 17 joints live with `recoveries_total=0` and the final `phys.stats` no recoveries / explosive detaches — FO3 is the only fixture declaring it, Dim 5). **Fixed 2026-10-12**: #5642 (force-greet re-reads the open conversation per open, so a second same-frame greeter holds instead of stripping the first's line, `c0c9c9d1b`; `two_same_frame_force_greeters_open_one_conversation`, Dim 2/5), #5669 (the menu DDS census now covers every Oblivion `textures\menus*` set and the FO3/FNV `textures\interface\` art — FO3 1 157 headers, `065c9d31d`; `interface_texture_header_census_decodes` in `crates/menuxml/tests/fo3_corpus.rs`, Dim 4/5). **Every FO3 finding of the 2026-10-08/09 audits is fixed — verify, don't re-file**: #5428 (kill-script provenance follows the load order's last-writer table, `335a0179c`, Dim 2), #5429 (FO3 dialogue real-data floor + declared dt1 SKIP, `c6e48cbc6`, Dim 2/5), #5433 (the FO3 profile mounts `Fallout - Sound.bsa` + `Fallout - Voices.bsa` + `Fallout - MenuVoices.bsa`, `4ef386a1e`, Dim 2), #5521 (voice names truncate only over 25, `4d6294bb1`, Dim 2), #5494 (an override's PNAM-less INFO heads fold in authored file order, `5356c8780`, Dim 2), #5499 (package selection continues past a fail-open Dialogue decline, `f7ca4a60f`, Dim 5), #5500 (Eat/Sleep Near-Linked-Reference / In-Cell anchors, `bcaf63aa8`, Dim 5), #5496 (LOD rings key on the PNAM "Use LOD Data" ancestor, `2ca57c4bc`; reopened and finished by `bb57270c3` — grid origin + object-LOD archive/atlas paths, Dim 3), #5518 (legacy quad-index scan keys on either family table, `5a549e664`, Dim 3), #5520 (prune tie-break on authored-cell coverage, `a9577fd69`, Dim 3), #5073 (empty compressed BSA placeholders extract as empty files, `fa2af0e89`, Dim 4). Fixed 2026-10-08..09: #5387 (same-level LOD leftovers pruned, `fbf1bed74`, Dim 3), #5393 + #5395 (voice owner + file name, `5b68792b7`, Dim 2), #5390 (legacy sleep = sit fallback, `9bdfdd1c6`, Dim 5), #5423 (LOD rings + climate on the layout table, `b3e679dba`, Dim 3). Fixed 2026-09-29..10-05: #4469 (INFO `DATA` decoded, `cc9353043`), #5005 + #5223 + #5248 + #5304 (corpses, Dim 2), #5222 (legacy LOD quad index, `b7987d813`, Dim 3), #5224 (DIAL `Top-level` flag, `305363122`), #5225 (LOD census, `4e9cf87b9`), #5226 (unmatched `--wrld` errors, `c6cbbe9f9`), #5238 (NPC Health curve, `c71eeed80`), #5271 (per-INFO `QSTI` quest gate, `dbc07e8f0`). Older guards: #4398 (emitter orientation), #4401 (clamp/parallax re-latch), #4468 (`.high.` quads), #4122 (SPT tail desync, `/audit-speedtree`).

## Parameters (from $ARGUMENTS)

`--focus <dimensions>`: comma-separated numbers (e.g. `1,4`). Default: all 5.

## Phase 1: Setup

1. Parse `$ARGUMENTS`; `mkdir -p /tmp/audit/fo3`.
2. Dedup baseline: `gh issue list --repo matiaszanolli/ByroRedux --limit 200 --json number,title,state,labels > /tmp/audit/issues.json`.
3. Confirm `Fallout 3 goty/Data/` exists; if not, name the dimensions that lose real-data validation.
4. **Run the real-data lane first** — it is the only guard for shared-code regressions on FO3 and no CI lane runs it: `cargo test -p byroredux-nif --release --test parse_real_nifs --test per_block_baselines --test block_coverage_baselines -- --ignored fallout_3 real_archive_torch`. A red gate is the headline finding; dimension agents then explain it.
5. Reconcile every count against the tables above and the ROADMAP/feature-matrix rows. Measure across **all** archives (base + DLC), never the base archive alone.
6. Scope by delta: `git log --since=<last AUDIT_FO3 date> --format='%h %cs %s' -- <dimension Paths>`.
7. Toolchain: the `byroredux` bin crate needs rustc >= 1.96 (wasmtime 49, 15a6b1d2d) — rustup cargo per `docs/contributing.md` § Toolchain note (#4466).

## Phase 2: Launch Dimension Agents (parallel)

### Dimension 1: NIF, Inline-Shader Material & Collision — FO3 Authoring
**Subagent**: `legacy-specialist`
Routing: block decode `/audit-nif`; single-boundary / no-fabrication / particle + collision translation `/audit-nifal`; Disney-BSDF gate (`MAT_FLAG_PBR_BSDF` must be 0, FO3 authors no BGSM) `/audit-renderer`; solver + phantoms `/audit-physics`.
**Paths**: `crates/nif/src/blocks/shader/`, `crates/nif/src/blocks/particle.rs`, `crates/nif/src/shader_flags.rs`, `crates/nif/src/import/material/`, `crates/nif/src/import/collision/`, `crates/nif/src/import/walk/emitter.rs`, `byroredux/src/material_translate.rs`
**First step**: the Phase 1 lane, plus `git log --since=<date> -- crates/nif/src/import crates/nif/src/blocks/shader`
- FO3 shader properties use the **`BSShaderFlags` F1 + `BSShaderFlags2` F2 pair** (`fo3nv_f1` / `fo3nv_f2` in `shader_flags.rs`) — never the Skyrim/FO4 vocabularies (same numeric bits, different meaning). Both properties are field-for-field with nif.xml on every BSVER gate (14/24/26/34).
- Texture precedence: a `BSShaderTextureSet` outranks `NiTexturingProperty` for base + normal (#4235; census: 3 703 NoLighting base co-binds all name the identical path). Clamp mode + parallax pair re-latch with it (#4401; `displacing_the_base_path_re_latches_the_clamp_mode`, `legacy_property_precedence_tests.rs`).
- `BSShaderNoLightingProperty` -> `MATERIAL_KIND_NO_LIGHTING` (102) fullbright with **no distance term** (`crates/renderer/shaders/triangle.frag`); decal bits honored on both properties; two-sided comes from `NiStencilProperty` (no FO3 flag bit); legacy emissive is `EmissiveSource::Material` only.
- `BSSegmentedTriShape` decodes the nif.xml 9-byte segment layout and feeds the plain `NiTriShape` geometry arm; segment (dismemberment) metadata is consumed-and-discarded by design — route to `/audit-nifal` + `/audit-character` when a consumer exists.
- **Emitter birth rate**: FO3's leg of `real_archive_torch_meshes_surface_particle_emitters` (`crates/nif/tests/parse_real_nifs.rs`, `--ignored`) must hold its per-game floor. #4261 broke it on every game from 2026-09-12 to 2026-09-19 (#4467) because the fix ran only the default lane — **any change under `walk/` or `blocks/particle.rs` needs this lane**. Spawn cones follow the emitter's composed orientation (#4398; `flat_walker_composes_the_emitter_rotation`).
- Collision: all FO3 chains classify `CollisionAuthoring::Classic`; `havok_motion_type` keeps BOX_INERTIA crates Dynamic (1 612 in FO3); every FO3-authored bhk shape kind has a `resolve_shape` arm (a new dispatch arm needs a resolve arm).
**Output**: `/tmp/audit/fo3/dim_1.md`

### Dimension 2: ESM Data Slice (Fallout3.esm)
**Subagent**: `general-purpose`
Routing: GRUP walk, `SubReader` accounting, dispatch, remap `/audit-esm`. This dimension owns FO3-only authoring and its counts.
**Paths**: `crates/plugin/src/esm/records/`, `crates/plugin/src/esm/cell/`, `crates/plugin/tests/parse_real_esm.rs`
**First step**: `cargo test -p byroredux-plugin --release --test parse_real_esm -- --ignored parse_rate_fo3_esm` (one test at a time; whole-file `--ignored` runs can spike >20 GB).
- Baseline (#3756): `index.total()` >= `FO3_TOTAL_FLOOR` (44 000) is an *index-sum* over ~95 typed maps that double-counts by design — NOT a file record count. Also floor the cell tier `index.total()` cannot see: placed refs >= 573 000 (REFR 568 107 + ACHR 2 154 + ACRE 3 349 + PGRE 350) and exterior cells >= 41 900; 0 walker errors on the master and all 5 DLCs. Never cite "44 657" or "13 684".
- FO3 is a strict subset of FNV's `NPC_`/`DIAL`/`INFO` subforms (sole FO3-only subform `INFO.SNDD`, 1 record) and shares `CELL.XCLL` (`XCLL_SIZES_FALLOUT_ERA`, FO3 `{36,40}`) and `CELL.XCCM` (a CLMT climate override on TES4/FO3/FNV, a REGN `sky_region` only on TES5/FO4, #5463, `3b28dca71`); the SCOL gate `is_scol_era` must keep FO3 dispatching (54 bases / 389 REFRs). `SCHR` u16 tail: see `/audit-fnv` Dim 2.
- The one FO3 TXST consumer is `LTEX.TNAM -> TXST -> TX00` -> `EsmCellIndex.landscape_texture_sets` (terrain splat diffuse); the other ~192 parsed TXST have no live consumer on this title.
- Dialogue decode (consumer: `byroredux/src/systems/npc_dialogue.rs`, `/audit-gameplay`): INFO `DATA` stores the type byte + raw `u16` flags on all 22 327 INFOs, the per-game reading left to the consumer (#4469, `cc9353043`; `parse_info_data_header_decodes_type_and_flags`); DIAL `DATA` byte 1 is the Rumors 0x01 / Top-level 0x02 flags — only 844 of 5 130 Topic DIALs are top-level, the rest link-only (#5224, `305363122`). INFO `QSTI` is decoded into `InfoRecord.quest` (all 23 247 measured FNV INFOs author it) and gates each INFO on its own quest's running state, ordered by quest priority via `DialogueQuestPriorities` (#5271, `dbc07e8f0`; `a_stopped_quests_info_does_not_speak_through_a_running_sibling`, `byroredux/src/systems/npc_dialogue.rs`). INFO-side `SNAM`, `NEXT` and the script family are still undecoded (none consumer-blocking).
- **#5367 runtime on FO3** (shared path; the only live gate, `docs/smoke-tests/dt1-dialogue-layers.sh`, is FNV — `dt1 fo3` is a declared SKIP, exit 77, never a pass, #5429, `c6e48cbc6`; FO3's data premises are floored instead by the `--ignored` `dialogue_greeting_and_forcegreet_fo3_floor` in `crates/plugin/tests/parse_real_esm.rs`: literal `GREETING` >= 2 500 INFOs + `HELLO`, >= 380 Dialogue-procedure PACKs (386 measured), >= 22 000 INFOs (22 327) with 0 quest-less; a measured FO3 cell / NPC / PACK live pin is the open work): the generic `GREETING` fallback (one `GREETING`/`HELLO` DIAL per FO3 plugin, resolved once per load order as `EsmIndex::generic_greeting_form`, #5426); the positional Random stack, closed by a passing plain line or Random End (`Flags1` bit 5 on TES4/FO3/FNV; #5397, `0436bea5c`; pins in `crates/scripting/src/dialogue.rs`); DLC DIAL overrides fold their INFOs into the master topic by FormID instead of replacing it (#5375, `168b81322`; `dialogue_override_folds_infos_into_the_master_topic`), an override's PNAM-less head run composing in authored file order behind a per-override contribution cursor (#5494, `5356c8780` — The Pitt's 93 heads no longer let "Go away." outrank 92 greetings); a quest's own CK Quest Conditions tab (`QustRecord.dialogue_conditions`, the `DialogueQuestConditions` resource) gates each of its INFOs before the INFO's own conditions, fail-closed on uncatalogued functions, so the highest-priority quest's unconditioned greeting (FO3's `WaterBeggars`) no longer hijacks every speaker (#5734, `6709eb65e`; `an_unreadable_quest_gate_declines_the_hijacker`, `a_catalogued_quest_gate_evaluates_running_state`, `byroredux/src/systems/npc_dialogue.rs`); force-greet opens re-read the open conversation per open (#5642, `c0c9c9d1b`). **Voice** (`byroredux/src/systems/dialogue_voice.rs`): the owning plugin resolves through `GlobalFormIdResolver` (#5393), the name cuts the quest EDID to 10 and the topic to a 25-char total only when quest + topic exceed 25, otherwise full EDIDs, one candidate (#5521, `4d6294bb1`), writes the plugin-local id and each segment's authored response number (19 FO3 INFOs skip 1..k) (#5395, `5b68792b7`; 11 955 / 11 955 joinable `Fallout3.esm` lines resolve, 2026-10-09; pins `voice_path_candidates_truncate_only_over_the_length_limit`, `voice_owner_resolves_masters_before_the_esm`); a topic change or conversation close cuts the playing line (#5432, `40b77828b`). The FO3 profile (`assets/debug_profiles.toml`, `[profiles.fo3]`) mounts `Fallout - Sound.bsa`, `Fallout - Voices.bsa` and `Fallout - MenuVoices.bsa` as `default_sounds_bsas` (#5433, `4ef386a1e`; pin `fo3_profile_declares_sound_and_voice_archives` in `crates/game-detect/src/lib.rs`); `fo3.env` still passes only `Fallout - Sound.bsa`. PKDD Dialogue Type is the `u32` at offset 16 (19 FO3 Say-To packages), so the #5376 Say-To decline gate fires (#5492, `dae08000d`).
- **Authored corpses** (mechanism `/audit-gameplay`): base `DATA` health <= 0 marks 498 FO3 placements (#5005, `fo3_corpses_key_on_base_health_not_xrgd`), and the 85 dismember-trigger `XLKR` targets + 3 self-killing bases are recognised at load because no FO3 object script runs (#5223, `717f39a82`; `fo3_fnv_script_killed_corpses_match_the_measured_census`). The set is computed once per load order over every cell (interior, grid, worldspace-persistent) as the `ScriptKilledCorpseForms` resource, because a trigger and its persistent target never share a `load_references_budgeted` call (#5248, `d4e8c31be`; the union-over-all-cells census cannot see locality, so a per-call regression passes it). Recognition needs one of the 11 exact shipped `GenericBiped*DismembermentSCRIPT` EDIDs **and** a winning SCPT that resolves to a vanilla master, so name-alikes decline (#5304, `2d47bf7f1`; `kill_on_load_recognition_requires_exact_edid_and_vanilla_master`). A true override patch keeps the master's FormID, so `script_is_vanilla` consults the `LoadOrder` `script_winners` last-writer table (SCPT FormID -> position of the last plugin that wrote it) rather than the FormID's slot byte, and declines it (#5428, `335a0179c`; `kill_on_load_recognition_declines_a_true_override_of_a_vanilla_script`, both passes); the slot owner is only the fallback for hand-built orders with no writer table. The whole-load-order scan returns the empty set up front off FO3/FNV (#5450, `eaac75911`). NPC Health seeds from the authored NPC curve (#5238; Nova = 40.0, Three Dog 25); a non-auto-calc NPC (ACBS bit 4 clear; ~712 FO3 actors) seeds its authored `DATA` SPECIAL (`NpcRecord::data_attributes`) instead of the class `ATTR` (#5338, `4957536ba`).
**Output**: `/tmp/audit/fo3/dim_2.md`

### Dimension 3: Cell Loading — Interior + Exterior
**Subagent**: `general-purpose`
Routing: terrain / WTHR / water / LOD mechanism `/audit-exterior`; unload hygiene `/audit-safety` Dim 3.
**Paths**: `byroredux/src/cell_loader/` (`load`, `unload`, `exterior`, `references/`, `spawn`), `byroredux/src/scene/`, `byroredux/src/cell_loader/legacy_lod_index.rs`, `crates/plugin/src/esm/cell/wrld.rs`, `docs/engine/exterior-readiness-plan.md`
**First step**: `git log --since=<date> --format='%h %cs %s' -- byroredux/src/cell_loader byroredux/src/scene crates/plugin/src/esm/cell`
- Interior `MegatonPlayerHouse`: **929 REFRs** parse-side (never the stale 1609); fixture cell *MegatonMoriartysSaloon* 458 REFRs. GPU entity/FPS numbers come only from a Vulkan run — record "not measured" otherwise.
- Exterior (Capital Wasteland): worldspace selection is data-driven (`select_worldspace_key`, deterministic tie-break; `--wrld` overrides #444's heuristic, and an unmatched `--wrld` now errors naming the loaded EDIDs instead of silently loading Wasteland's copy of the cell — #5226, `c6cbbe9f9`) — FO3 data has no `wastelandnv` EDID, so an FNV-first preference can never mis-pick; `wrld.rs` must hold no hardcoded worldspace name, origin or grid. Every `GameKind` use on the load path is asset-family classification, not a behavior branch. Gate: `docs/smoke-tests/m-exteriors.sh fo3` (`MegatonWorld`); last measured figures in `exterior-readiness-plan.md` (no FO3 scene is in the ROADMAP bench-of-record table). Default land texture is `DirtWasteland01.dds` (`DefaultLandTexture::for_game`).
- Object LOD is `ObjectLodScheme::FalloutLegacyBlocks` (`placement_lod_supported` is Oblivion-only). It is **not a flat ring** (#3502): 7 of FO3's 15 worldspaces bake level-8 object quads with no level-4 sibling (93 of the **366 plain quads** in `Fallout - Meshes.bsa` — its 422 `blocks\` entries also carry 54 `.high.` variants and 2 `…postapocalypse.nif`, variant files not quads; 366 base + 331 DLC = 697 plain corpus-wide: `dcworld01/03/06/12/17`, `paradisefalls`, `washmontop`); `LodBandSelection::coarsen_to_available` must stay **object-only** (terrain keeps subdividing — `object_lod.rs` true / `terrain_lod.rs` false) and those worldspaces must show distant buildings inside 16 cells — the legacy family no longer derives a grid origin (#5222, `b7987d813`): 26 of 29 worldspace/level sets sit off `(0,0)` with no single origin, so `LegacyLodQuadIndex` (`byroredux/src/cell_loader/legacy_lod_index.rs`) scans the archive name tables once per streaming state and `select_authored_lod_quads` (`lod_bands.rs`) picks quads by footprint (objects from the mesh BSAs, terrain from the diffuse DDS names, `.high.` variants riding their plain sibling; terrain tops up uncovered ring ground with level-4 synth tiles). Selection first keeps each level's majority lattice residue (`prune_same_level_overlaps`): six FO3 worldspaces ship same-level quads from different LOD generations whose footprints and geometry overlap — washmontop 65 overlapping pairs among 37 level-8 quads (#5387, `fbf1bed74`; `same_level_leftover_generation_quads_are_pruned`; a count tie in the residue vote goes to the class whose footprints cover more of the LOD source worldspace's authored exterior cells, residue order only on a coverage tie (#5520, `a9577fd69`; `lod_source_cell_exists` threads through `select_authored_lod_quads` from both ring call sites; exal.md keeps open which lattice the shipped game loads). The terrain ring gates on `terrain_lod_layout` (`FalloutLegacy`), not `GameKind::Fallout3NV` (#5423, `b3e679dba`); the quad-index scan (`should_scan_legacy_lod_quads`, `byroredux/src/streaming_helpers.rs`) fires when either the terrain-layout or the object-scheme table selects the legacy family (#5518, `5a549e664`). **LOD-inheriting children**: WRLD `PNAM` bit `0x02` ("Use LOD Data") children — all four FO3 roof / vista worldspaces (`StatesmanRoofWorld`, `GNRroofWorld`, …) author zero quads — resolve every distant-ring key (legacy quad index, object archive paths, `.btr` paths, generated distant textures + worldspace form id, band bounds) on `ExteriorWorldContext::lod_worldspace_key()`, the nearest `WNAM` ancestor that does not set the bit (`lod_source_worldspace`, `byroredux/src/env_translate/lod.rs`); full-detail residency stays on the child (#5496, `2ca57c4bc`; installed-FO3 pin walks `StatesmanRoofWorld` -> Wasteland). The reopened #5496 moved the last three child-keyed sites — the grid origin and the object-LOD archive + atlas paths (`object_lod_quad_paths`) — onto `lod_worldspace_key()` too (`bb57270c3`; pins `lod_grid_origin_of_an_inheriting_child_is_the_ancestors`, `object_lod_paths_of_an_inheriting_child_name_the_ancestor` — Skyrim-shaped fixtures). `worldspace_lod_grid_origin` (`byroredux/src/cell_loader/lod_support.rs`) still returns (0,0) for non-combined-LOD games (FO3 included) but only the synth/placement grids consume it. Real-data pins (`--ignored`, `byroredux/src/cell_loader/legacy_lod_index_tests.rs`): `dcworld03_level8_quads_select_near_the_worldspace_centre`, `washmontop_level8_spans_multiple_residues_and_all_select` (name predates #5387 — it now pins the majority-generation selection and that no two selected same-level quads overlap).
- `CachedNifImport` Arc cache: no re-parse and no leak across FO3 unload/load cycles.
**Output**: `/tmp/audit/fo3/dim_3.md`

### Dimension 4: BSA v104 & Real-Data Validation
**Subagent**: `general-purpose`
Routing: reader discipline `/audit-parsers`. FO3's BSA is byte-identical in format to FNV's — a divergence here is a v104 regression, not a format gap.
**Paths**: `crates/bsa/src/archive/`, `crates/facegen/`, `crates/spt/`, `crates/nif/examples/nif_stats.rs`
**First step**: `cargo test -p byroredux-bsa --release --test bsa_real -- --ignored fo3_fnv_and_skyrimse_bsa_corpus_census` (#5903, `ce008af1c` — opens all 16 FO3 `.bsa`, pins 159 155 entries and **extracts every one** with 0 errors; the older #4665 `fo3_bsa_corpus_opens_and_lists` in `ba2_real.rs` only opens and lists) and `--test bsa_real -- --ignored fnv_meshes_bsa_v104`.
- The corpus test proves extraction succeeds, not that payloads are valid: still sweep `Fallout - Textures.bsa` to valid DDS headers (12 261 / 12 261 at last measure) and the folder-hash census (0 collisions over 1 810 folders); confirm no game-conditional branch in the v104 path. The 1 vanilla zero-byte compressed `.txt` placeholder extracts as an empty file — `BsaArchive::extract` decides the empty-stream edge before any decoder, so a flate2 bump cannot reopen it (#5073, `fa2af0e89`).
- Pick one deathclaw-class creature (skinned via `BSDismemberSkinInstance`, `crates/nif/src/import/mesh/skin.rs`), one UI `BSShaderNoLightingProperty` element (fullbright route), one FaceGen head (`crates/facegen/`; EGM morph deltas are scaled int16, not half-floats — #4653, real-data pin `parse_vanilla_headhuman_egm` in `crates/facegen/tests/parse_real_facegen.rs`, and `vanilla_facegen_sidecar_census_is_exact` parses every FO3 archived sidecar — 250 EGM / 36 EGT / 49 TRI over 16 archives, #5903; may not render fully — note the gap), one `.spt` (`parse_rate_fo3_spt`: placeholder-billboard fallback, not a hard error).
- Runtime telemetry diff: `/audit-runtime --game fo3`.
**Output**: `/tmp/audit/fo3/dim_4.md`

### Dimension 5: Animation, NPC Spawn, Gameplay Data & the Scripting Gap
**Subagent**: `legacy-specialist`
Routing: gameplay mechanism `/audit-gameplay`; MenuXml `/audit-ui`; scripting runtime `/audit-scripting`; clip registry `/audit-ecs`. The classic-era M41 guards (B-spline `FLT_MAX` pose fallback, clip-registry dedup, NPC hand meshes) and the spawn-time skin seam blend (tuned on Moriarty's Saloon) are listed in `/audit-fnv` Dim 4 — verify them on FO3 data here.
**Paths**: `crates/nif/src/anim/`, `byroredux/src/npc_spawn.rs`, `byroredux/src/npc_spawn/seam_blend.rs`, `byroredux/src/hud.rs`, `crates/menuxml/`, `crates/plugin/src/consumables.rs`, `docs/smoke-tests/fixtures/fo3.env`
**First step**: `git log --since=<date> --format='%h %cs %s' -- crates/nif/src/anim byroredux/src/npc_spawn byroredux/src/hud.rs crates/menuxml`
- FO3 kf-era spawn works because its `skeleton.nif` resolves (unlike FO4); Megaton dwellers with no hands = the hand-mesh regression; the three `mtforward.kf` body-class walk clips (`humanoid_walk_kf_path`) exist in FO3's archive. Creature clips resolve per game from candidate lists (`creature_idle_kf_paths` / `creature_walk_kf_paths`: FO3/FNV `mtidle.kf` then `idle.kf`; `locomotion\mtforward.kf`, `mtforward.kf`, the shipped misspelling `locomotion\mtfoward.kf`) — before #6078 (`578d8432a`) every FO3 creature took Oblivion's names, stood in bind pose and slid; FO3 now resolves an idle in 25 of 27 creature dirs. Model-less templated `CREA` spawner shells (`TPLT` -> `LVLC` + `TEMPLATE_FLAG_USE_MODEL_ANIMATION`) take the leveled pick's model — `Fallout3.esm` 124 / 124 resolve (#6075, `011ad0c07`); a placement with no resolvable skeleton is despawned, not left a body-less actor.
- **HUD**: `HudGameProfile::fallout3` — menu art from `Fallout - Textures.bsa` (the shared anchor; FNV's `Textures2` rides in as its numeric sibling), 8 font slots (FNV: 9), 2 bars keyed on AVIF editor ids resolved through `PlayerVitals` (#4675). FO3 vs FNV is picked from the load order by `fallout_profile` (#4718), falling back to FO3 when neither base ESM is listed or beside the corpus. Guards: `crates/menuxml/tests/fo3_corpus.rs` (`BYROREDUX_FO3_DATA`; its `textures\interface\` header census pins 1 157 DDS, #5669, `065c9d31d`; `fo3_menu_xml_corpus_parses_with_exact_totals` pins 99 menu XMLs, 736 tiles un-spliced / 1 445 include-spliced, #5902, `3404e4f85`) and `docs/smoke-tests/m48-5-fo3-hud.sh`.
- **Consumables**: Stimpak restores health + the seven body-condition AVIFs on FO3 as on FNV; FO3 has no Hardcore — CTDA fn 586 must be rejected under the FO3 profile (`fn586_condition_gate_is_profile_scoped_not_game_scoped`). Real-master guard: `real_stimpaks_restore_scaled_health_and_limbs_but_hardcore_only_health` in `byroredux/src/inventory.rs` (`--ignored`).
- Playable gates declared in `fo3.env` (`FIXTURE_GATES`): `p0-door-interaction`, `p5-save-restart`, `p5-f5-f9-quicksave`, `p2-melee-core` (Moriarty's Saloon, target Nova; green at HEAD per `273692be5`). An undeclared gate exits 2 — that is unmeasured, not covered.
- **Eat / Sleep** (M42, `byroredux/src/systems/eat_sleep.rs`, mechanism `/audit-gameplay`): `PKDT` procedures 3 / 4 walk to the authored `PLDT` anchor (#5391, `42aab4c09`), and a marker beyond the arrival threshold becomes the actor's next KCC walk goal before seating, so In-Cell no longer snaps an actor across the resident cell (#5726, `14e3144cf`); Near-Linked-Reference resolves through the actor's own `XLKR` edge, In-Cell searches the whole resident cell (radius is not authored), and an unresolved Near-Reference target retries on a coarse clock instead of caching the actor's position (#5500, `bcaf63aa8`; #5527) — the retry window is shared with Travel / Escort / Guard (#5724, `a02522d1b`); creatures never take furniture (`CreatureActor` gate, `9bb8bc475` + `0ba1dd3af`; 15 FO3 placements; #5495 is still open on GitHub). Legacy furniture markers author `animation_type` 0, so every marker reads Sit and on FO3 the sit fallback IS the sleep path — sleepers take the nearest sit marker, chairs included (documented contract, #5390, `9bdfdd1c6`; `legacy_sleep_actor_seats_at_the_sit_fallback`). `docs/smoke-tests/m42-eat-sleep.sh` runs FNV only; FO3 posture is unmeasured.
- **ObScript VM (M47.3, shipped 2026-10-18; mechanism `/audit-scripting`)**: 1 257 SCPT parse, and the game-agnostic SCDA bytecode interpreter now runs them on one host (`ObScriptCommandHost`, owned by a quest, a placed reference or an inventory item). Quest scripts tick on the authored `QUST` delay (101 FO3 quest scripts install, `install_quest_scripts` in `crates/scripting/src/obscript_quests.rs`, called from `byroredux/src/asset_provider/script.rs`); object scripts attach at reference spawn through `attach_object_script` (`crates/scripting/src/obscript_objects.rs`, tables from `ensure_obscript_tables`, no game gate) and run `GameMode`/`OnLoad`/`OnDeath`/`OnActivate`/the trigger blocks (FO3/FNV: `0x1a` = `OnTriggerEnter`, `0x1b` = `OnTriggerLeave`; 349 FO3 `Begin OnTrigger*` lines agree with the bytes; 139 of FO3's 373 `OnActivate` blocks reach an `Activate`); command ids are shared across Oblivion/FO3/FNV (#6015 per-game evidence table in `obscript_quests.rs`). Coverage is measured, not live-gated, on FO3: `obscript_tools names` models **93 %** of FO3's 11 892 call-site lines (`docs/engine/scripting.md`), `legacy_object_scripts_walk_and_run_without_malformed_bytecode` (`--ignored`) walks every FO3 object script, but `docs/smoke-tests/m47-3-obscript.sh` runs FNV only. Separately, the narrow static load-order translator (`attach_legacy_obscript_program`, `obscript_runtime.rs`) still gets no compiled dialect on FO3 (`obscript_dialect_for` -> `None`), so FO3 source-less SCDA is not translated there — the bytecode VM is the FO3 path. The #5223 kill-on-load corpse idiom (Dim 2) is still recognised at load, independent of the VM.
**Output**: `/tmp/audit/fo3/dim_5.md`

## Phase 3: Merge

1. Read all `/tmp/audit/fo3/dim_*.md`.
2. Combine into `docs/audits/AUDIT_FO3_<TODAY>.md`:
   - **Executive Summary** — compatibility level + delta vs FNV (what is shared, what diverges); state the real-data lane result first.
   - **Dimension Findings** — grouped by severity per dimension.
   - **FNV-Shared Surface** — record/block/shader paths FO3 inherits, plus any FO3-only gap inside them.
   - **FO3-Distinctive Gaps** — inline-shader-only material universe, Capital Wasteland worldspace, the SCPT runtime gap.
   - **Validation Status** — interior (Megaton 929 REFRs), exterior (population + image-health gates per `exterior-readiness-plan.md`; no FO3 scene in the ROADMAP bench-of-record), creature/NPC.
   - **Cross-Audit Routing** — shared-mechanism findings handed to their owner.
3. Remove cross-dimension duplicates.

Suggest: `/audit-publish docs/audits/AUDIT_FO3_<TODAY>.md`
(label every finding `game:fo3` + `legacy-compat`, plus its own domain label.)
