Audit de compatibilité Fallout New Vegas

Audit par jeu de la compatibilité avec Fallout New Vegas, couvrant NIF, BSA, ESM, ragdoll et détection de régressions.

Spar Skills Guide Bot
TestingAvancé
0026/07/2026
Claude Code
#fallout-new-vegas#compatibility-audit#regression-testing#game-modding#engine-audit

Recommandé pour


description: "Per-game audit of Fallout New Vegas compatibility — reference title, ESM + cells + RT lighting + ragdoll" argument-hint: "--focus <dimensions>"

Fallout New Vegas Compatibility Audit

Deep audit of ByroRedux readiness for Fallout: New Vegas content. FNV is the reference title — the most-validated end-to-end path in the engine and the reference realization for the canonical translation layers (NIFAL material/physics, PHYSAL ragdoll). Audits here hunt regressions and unshipped polish, not missing foundations: a foundation that broke on FNV is the single highest-severity finding this command can produce.

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

See .claude/commands/_audit-common.md for the master project-layout map, key reference docs, game-data locations, methodology, dedup rules, and finding format. See .claude/commands/_audit-severity.md for severity.

Game Context

| Aspect | State | |----------------|----------------------------------------------------------------------------------------| | NIF format | v20.2.0.7 · bsver 34 (bsver::FO3_FNV in crates/nif/src/version.rs) | | BSA format | v104 — crates/bsa/src/archive/ | | ESM parser | Long-tail dispatch closed; unknown_records catch-all removed | | Ragdoll | PHYSAL slice 1 reference (classic bhk chain) — byroredux/src/ragdoll.rs | | Reference data | /mnt/data/SteamLibrary/steamapps/common/Fallout New Vegas/Data/ |

Authoritative status — do NOT hardcode counts here (they rot). Pull live from:

  • ROADMAP.md — per-game compat matrix (FNV parse rate, the Prospector bench-of-record entity/FPS/fence/draw numbers + the commit they were taken at), Known Issues.
  • docs/feature-matrix.md — what works at runtime on FNV per subsystem.

The Prospector Saloon bench is the FNV bench-of-record; treat any drop below the ROADMAP-recorded numbers (at the recorded commit) as the regression baseline. The full pre-collider FNV baseline has not been recovered — see ROADMAP Known Issues before flagging fence/FPS as a fresh regression.

Parameters (from $ARGUMENTS)

  • --focus <dimensions>: Comma-separated dimension numbers (e.g., 1,3). Default: all 9.

Phase 1: Setup

  1. Parse $ARGUMENTS.
  2. mkdir -p /tmp/audit/fnv.
  3. Dedup baseline: gh issue list --repo matiaszanolli/ByroRedux --limit 200 --json number,title,state,labels > /tmp/audit/issues.json.
  4. Confirm Fallout New Vegas/Data/ exists (required — FNV is the baseline).
  5. Read the FNV row of ROADMAP.md's compat matrix + docs/feature-matrix.md to capture the current baseline numbers and commit. Every "regression" claim is judged against those, not against numbers written into this skill.

Phase 2: Launch Dimension Agents (parallel)

Dimensions are ordered by current FNV risk: the layers most likely to silently break FNV first (cell load + canonical translation + RT), regression guards last.

Dimension 1: Cell Loading End-to-End (highest blast radius)

Subagent: general-purpose Entry points: byroredux/src/cell_loader/ (cell_loader.rs is a thin dispatcher), byroredux/src/scene/world_setup.rs, byroredux/src/streaming.rs Companion docs: docs/engine/pipeline-overview.md (interior cell load trace) and docs/engine/exterior-grid-streaming.md (exterior grid, background pre-parse, cell-boundary + door-teleport swaps) — both verified against the tree 2026-07-15. Checklist:

  • Interior load — Prospector Saloon entity count + XCLL lighting + NiAlphaProperty decal routing.
  • Exterior 7×7 (radius 3) WastelandNV grid — LAND terrain (byroredux/src/cell_loader/terrain.rs), LTEX/TXST splat, WTHR→CLMT→WTHR resolution, cloud texture resolution through the asset provider's TextureProvider.
  • NifImportRegistry Arc cache (byroredux/src/cell_loader/nif_import_registry.rs::CachedNifImport) prevents duplicate parsing across cells.
  • Cell unload hygiene (regression guard): byroredux/src/cell_loader/unload.rs must drop BLAS per freed mesh handle and release physics bodies. #1520 (34c7a218): Rapier bodies/colliders are released on unload — verify the unload path frees them (covered by byroredux/src/cell_loader/rapier_release_tests.rs); a leak here compounds per cell-streaming cycle. Also check the inventory_release_tests.rs / unload_skin_cleanup_tests.rs siblings.
  • M38 water — byroredux/src/cell_loader/water.rs spawns WaterPlane per cell; byroredux/src/systems/water.rs::submersion_system writes camera submersion state on entry.
  • _far.nif distant-object LOD (#1726/#1745, Session 52) — verify the placement scheme + real LOD textures still resolve on FNV's WastelandNV exterior grid; entry points cell_loader/object_lod.rs, cell_loader/placement_lod.rs. Output: /tmp/audit/fnv/dim_1.md

Dimension 2: NIFAL Canonical Translation — FNV Slice

Subagent: legacy-specialist Entry points: byroredux/src/material_translate.rs, crates/core/src/ecs/components/material.rs, crates/nif/src/import/collision/mod.rs, docs/engine/nifal.md Checklist: FNV is the reference content for this boundary, so it must be exercised here first.

  • material_translate.rs::translate_material is the single ImportedMesh → Material boundary — no second per-game material path may exist.
  • FNV materials land with Material::metalness / roughness as plain resolved f32 (material.rs), not Option. Material::resolve_pbr (→ classify_pbr_keyword) runs once at translation — there must be no per-draw keyword scan in byroredux/src/render/static_meshes.rs (the old render-time Material::classify_pbr is deleted).
  • EmissiveSource guard: FNV legacy emissive uses EmissiveSource::Material (the genuine NiMaterialProperty.emissive_mult scalar). The EmissiveSource enum (material.rs) carries Material / Lighting / Effect variants; Skyrim+ Lighting and FO4+ Effect must not bleed into the FNV Material path (~1.0 scale untouched).
  • Collision-shape no-drop guard (9c6096aa): BhkMultiSphereShape + BhkConvexListShape translate to CollisionShape via collision.rs::resolve_shape (Compound of Ball children / ConvexHull) — previously silently dropped. Any FNV mesh with a multi-sphere / convex-list Havok shape must surface a CollisionShape.
  • No-fabrication invariant: translation may not invent PBR values FNV never authored; keyword-classified dielectric defaults are fine, fabricated metalness is not.
  • See /audit-nifal for the dedicated single-boundary / no-fabrication / no-render-time-fallback audit. Output: /tmp/audit/fnv/dim_2.md

Dimension 3: RT Lighting Pipeline — FNV Scenes

Subagent: renderer-specialist Entry points: crates/renderer/src/vulkan/acceleration/, crates/renderer/shaders/triangle.frag, crates/renderer/shaders/composite.frag, docs/engine/lighting-from-cells.md Checklist:

  • TLAS frustum culling — no lights dropped for in-view fragments.
  • ReSTIR-DI direct lighting in crates/renderer/shaders/include/lighting.glsl (#included by triangle.frag; the reservoir G-buffer attachment was retired #1583/#1590 — the WRS now stays register-local) — NUM_RESERVOIRS = 16 reservoirs/fragment, unbiased W = resWSum / (K · w_sel) estimator, shadow-ray budget caps, distance-based shadow/GI ray fallback.
  • BLAS compaction + LRU eviction at the dynamic VRAM-derived budget: predicates.rs::compute_blas_budget = device_local_bytes / 3 floored at MIN_BLAS_BUDGET_BYTES (~4 GB on a 12 GB-VRAM dev box — NOT any stale "1 GB" figure); the result is cached in the blas_budget_bytes field (acceleration/mod.rs).
  • SVGF temporal accumulation uses motion vectors + mesh_id disocclusion; TAA Halton jitter + YCoCg variance clamp.
  • M33 sky gradient + cloud layer blends correctly with tone-mapped geometry.
  • Disney BSDF gate guard (#1248–#1252): zero FNV materials author BGSM (FO4+), so MAT_FLAG_PBR_BSDF (crates/renderer/shaders/include/shader_constants.glsl = 32u) must be 0 across the FalloutNV.esm material universe — the Disney lobe (now in crates/renderer/shaders/include/pbr.glsl) is unreachable for FNV. If any FNV scene activates Burley retro-reflection / anisotropic GGX / per-material-IOR Fresnel, the gate regressed.
  • #1125 skyTint interior gate at the crates/renderer/shaders/include/raytrace.glsl reflection + refraction miss fallbacks (2 sites) — FNV interiors (Prospector, every Vault) must drop to cell ambient alone, not default zenith blue.
  • Sun-sprite mip-0 force (8b5d77c1) at composite.frag::compute_sky — explicit textureLod 0.0 avoids pixelating the tiny screen-space sun disc. Output: /tmp/audit/fnv/dim_3.md

Dimension 4: ESM Record Parser — Coverage & Accuracy

Subagent: general-purpose Entry points: crates/plugin/src/esm/records/, crates/plugin/src/esm/cell/ (post-split: walkers.rs / helpers.rs / support.rs / wrld.rs) Checklist:

  • Record counts on FalloutNV.esm match the ROADMAP / feature-matrix baseline (do not transcribe a fixed count into this skill — diff against the living doc).
  • Spot-check semantics: Varmint Rifle stats, NCR faction relations, VATS AVIF entries (the FNV gameplay-record path in crates/plugin/src/esm/records/index.rs + crates/plugin/src/esm/records/misc/effects.rs).
  • CELL XCLL fog_far_color optional-field handling.
  • SCOL is FNV-era, not an FO4 addition (#1538): FalloutNV.esm carries 98 SCOL bases referenced by 1084 REFRs (road segments, guardrails, debris LOD) — the is_scol_era = is_fo4_plus || Fallout3NV gate in crates/plugin/src/esm/records/mod.rs MUST keep dispatching parse_scol_group for FNV/FO3; re-narrowing it to FO4-only is the regression that silently drops those 1084 placements. The genuinely FO4+-only records are MOVS / PKIN / MSWP (byte-scan-confirmed absent from FalloutNV.esm) — those must not steal FNV dispatch. TXST/XATO/XTNM/XTXR cell-subrecord arms live in crates/plugin/src/esm/cell/walkers.rs; an unreachable_patterns warning there is a code smell.
  • LVLI leveled-list flattening — crates/plugin/src/equip.rs::expand_leveled_form_id resolves NPC default-outfit LVLI refs into base ARMO/WEAP; FNV NPCs whose outfits reference LVLI must spawn gear, not empty.
  • SCPT SCHR flags are a u16 (#1654, 590351c1): the Oblivion/FO3/FNV SCHR is exactly 20 bytes with a u16 flags tail after the script_type u16 (cursor @18, 2 bytes left). crates/plugin/src/esm/records/script.rs reads it via u16_or_default into ScriptRecord.flags; a u32 read fails on every real script and unwrap_or(0) pins flags to 0. The field is a u16 on every game — a regression back to u32 silently zeroes all script flags. Output: /tmp/audit/fnv/dim_4.md

Dimension 5: NIF Parser — FNV Regression Guard

Subagent: legacy-specialist Entry points: crates/nif/src/blocks/, crates/nif/tests/parse_real_nifs.rs, crates/nif/examples/nif_stats.rs Checklist:

  • Parse rate holds at the ROADMAP FNV figure; block histogram from nif_stats matches expected distribution (a meaningful shift = a block type being mis-dispatched).
  • NiTexturingProperty decal-slot off-by-one; BSMultiBound*; BSDecalPlacementVectorExtraData all stay fixed (reference N23.4 FO3/FNV validation).
  • #1277 collision/version guards:
    • collision.rs::examine_collision_kind classifies FNV chains as CollisionAuthoring::Classic (the bhk* path), not NewPhysicsStub/Phantom/Unrecognised — a misclassified discriminator silently drops the rigid body.
    • bhk motion_type via the canonical Havok enum (#1652, dc33ec7d): collision.rs::havok_motion_type maps the raw hkMotionType byte per the full nif.xml enum (1–5/8 → Dynamic, 6 KEYFRAMED → Keyframed, 7 FIXED → Static, 9 CHARACTER → CharacterKinematic, 0/other → Static). The pre-fix 4 => Keyframed / _ => Static collapse mis-typed BOX_INERTIA (4) clutter (crates/ammo boxes) as kinematic-frozen instead of falling — re-introducing the collapse is the regression.
    • version.rs raw-bsver-compare migration: bsver::FO3_FNV = 34, RIGID_BODY_FLAGS16 = 76, NI_BS_LTE_16 = 16 etc. must still place FNV (bsver 34, > NI_BS_LTE_16) on the post-Oblivion side of every gate — a flipped comparison shifts field layout and corrupts collision/anim reads.
  • #1269 walker guard: MAX_NIF_NODE_DEPTH = 128 in crates/nif/src/import/walk/mod.rs guards both hierarchical + flat walkers; a legit FNV scene must never trip the 128-depth bail (covered by crates/nif/src/import/walk/tests.rs). Output: /tmp/audit/fnv/dim_5.md

Dimension 6: Animation, Skinning & Particles (FNV)

Subagent: legacy-specialist Entry points: crates/nif/src/anim/, crates/core/src/animation/, byroredux/src/anim_convert.rs, byroredux/src/npc_spawn.rs, byroredux/src/systems/particle.rs Checklist:

  • .kf load from BSA; AnimationClipRegistry populated; NiTransformInterpolator + NiFloatInterpolator + NiBoolInterpolator channels sample correctly; NiTextKeyExtraData text events collected; Clamp/Loop/Reverse cycle types honored; FixedString interning at clip-load (#340) — no per-frame StringPool locks.
  • Skinning regression (NOT a foundation check — GPU skinning M29 + #178 SkinnedMesh palette are live): NiSkinData sparse weights still parse; bone palette stays correct on the GPU path.
  • B-spline pose-fallback (#772): gated on a FLT_MAX sentinel; without it NPCs vanish under FNV BSPSysSimpleColorModifier particle stacks that share time-zero with the actor's player. NiBSplineCompTransformInterpolator IS reachable on FNV/FO3 — do not rule it out by game era.
  • AnimationClipRegistry dedup (#790): dedup by lowercased path so cell streaming doesn't grow it unboundedly (else one keyframe set leaks per cell load).
  • NPC hand-mesh load (#793): lefthand.nif + righthand.nif load alongside upperbody.nif on kf-era NPCs (npc_spawn.rs) — any body assembly loading only upperbody leaves Doc Mitchell / Sunny Smiles handless.
  • Typed-emitter particle pin (5708b5b9 / 9db60714): NiPSysEmitter / NiPSysEmitterCtlr / NiPSysEmitterCtlrData / NiPSysGrowFadeModifier are typed structs in crates/nif/src/blocks/particle.rs. walk/mod.rs::extract_emitter_params + ::extract_emitter_rate feed systems/particle.rs::apply_emitter_params — FNV's heavy particle stacks must drive from the authored birth-rate / emitter size / base_scale, not preset kinematics. (Particle translation is part of the NIFAL tier — see /audit-nifal.) Output: /tmp/audit/fnv/dim_6.md

Dimension 7: PHYSAL Ragdoll — FNV Reference Slice

Subagent: legacy-specialist Entry points: byroredux/src/ragdoll.rs, crates/nif/src/import/collision/mod.rs (ragdoll + constraint decode), crates/nif/src/blocks/collision/, docs/engine/physal.md Checklist: FNV is the reference realization for PHYSAL slice 1 (the classic bhk chain — 0a0bc3ce / 2c21a470, 2026-06-14). Newly shipped, so audit for correctness, not just regression.

  • The importer hands ImportedRagdoll (bone names + ImportedJointKind); ragdoll.rs::activate_ragdoll resolves it against the skeleton's GlobalTransform, and ragdoll_writeback_system writes solver results back to bone transforms.
  • Silent-drop regression guards (#1718/#1539/#1540/#1772, the D7 audit-guard family): template_from_imported (ragdoll.rs) warns on dropped bodies/constraints by bone-name miss (#1718, ffe9a816); extract_ragdoll (collision.rs) warns on dropped non-Ragdoll/LimitedHinge constraint kinds (#1539); trimesh bone inertia no longer degenerate (#1540); keyframed bone-follower bodies are torn down on ragdoll activation, not left double-simulating (#1772, da4a849d). Confirm all four still hold on a real FNV skeleton with divergent bone naming.
  • Per PHYSAL, the only per-game seam is the constraint CInfo decode — confirm no per-game branch leaked into ragdoll.rs or the solver bridge (crates/physics/).
  • FNV's dominant constraint form is a bhkMalleableConstraint wrapping a Ragdoll (see docs/engine/physal.md §FO3/FNV) — confirm that decode path in crates/nif/src/blocks/collision/constraints.rs + ragdoll.rs survives and produces a jointed body, not a single rigid blob.
  • Writeback must not corrupt the skinned bone palette feeding the GPU skin path (cross-check Dimension 6). Output: /tmp/audit/fnv/dim_7.md

Dimension 8: Real-Data Validation & Bench-of-Record

Subagent: general-purpose Entry points: crates/nif/examples/nif_stats.rs, demo CLI invocations Checklist:

  • CWD matters (ROADMAP repro note): bare --bsa / --textures-bsa names resolve against CWD, not the --esm folder. Run with CWD = Fallout New Vegas/Data/, else archives silently fail and the scene loads near-empty (~36 entities / spurious FPS).
  • Interior bench-of-record: cargo run --release -- --esm FalloutNV.esm --cell GSProspectorSaloonInterior --bsa Meshes.bsa --textures-bsa Textures.bsa --textures-bsa Textures2.bsa --bench-frames 300 --bench-hold then attach byro-dbg (port 9876) and capture stats. Compare entity / draw / FPS / fence against the ROADMAP FNV row (not numbers in this skill).
  • Exterior: --grid <x>,<y> --radius 3 on WastelandNV.
  • Validate tex.missing / tex.loaded return sensible output (FNV ships base textures split across Fallout - Textures.bsa + DLC archives — tex.missing first when surfaces look chrome/posterized). Output: /tmp/audit/fnv/dim_8.md

Dimension 9: AI Packages & Procedure Runtimes (M41.5/M42–M42.8)

Subagent: general-purpose Entry points: byroredux/src/npc_spawn.rs (spawn_npc_entity package-selection tail, package_conditions_pass), byroredux/src/systems/{sandbox,wander,travel,follow,escort,guard,patrol,locomotion}.rs, crates/core/src/ecs/components/{sandbox,furniture,wander,travel,follow,escort,guard,patrol}.rs, crates/plugin/src/esm/records/misc/pack.rs (PACK/PKDT/PSDT/PLDT/PTDT decode + the seven active_package_is_* selectors), docs/engine/npc-spawn-ai-packages.md Checklist: Seven of ~17 FO3/FNV package procedures execute (Sandbox, Wander M42.3, Travel M42.4, Follow M42.5, Escort M42.6, Guard M42.7, Patrol M42.8) — audit for correctness of what's implemented, not for missing scope. Do not flag the absence of a Find/Eat/Sleep/Accompany/UseItemAt/Ambush/FleeNotCombat/CastMagic/Dialogue/UseWeapon runtime as a bug — each needs a subsystem (item/furniture use beyond seat-snap, combat, magic, dialogue) that doesn't exist in this engine at all yet, not just a missing dispatch arm. Do also not flag any of the seven's documented v0 approximations (no pathing/NAVM, no animation-clip swap, no per-frame package re-evaluation, PTD2 unparsed) as bugs — see docs/engine/npc-spawn-ai-packages.md for the authoritative v0-scope list per procedure.

  • CTDA fail-open is intentional, not a bug (M42.2): package_conditions_pass treats a package's whole condition list as passing if ANY referenced function is outside the ~15-function M47.1 catalog (ConditionFunction::Unknown) — this preserves M42.1 behavior (every scheduled package eligible) rather than silently dropping packages the evaluator can't reason about. Only lists whose every function is implemented are gated for real. Applies to all seven procedures' selection, not just Sandbox's. Verify a regression doesn't flip this to fail-closed.
  • Schedule gating, all seven procedures: each active_package_is_* (crates/plugin/src/esm/records/misc/pack.rs) picks the first package scheduled-active at GameTimeRes.hour — verify an NPC with a non-matching package active at the current hour (e.g. an AtBar schedule) does NOT get that procedure's Behavior marker for that hour. Since an NPC's active package is always a single winning PackRecord, the seven checks must be mutually exclusive per actor — verify npc_spawn.rs's if runs_* chain never inserts two Behavior components on one actor.
  • PLDT search radius/destination, six of seven: Sandbox/Wander/Travel/Escort/Guard/Patrol all read a PLDT radius (Follow instead reads PTDT's count_or_distance as a stand-off distance) — verify radius-0 / no-PLDT packages fall back to each system's own default rather than a degenerate radius-0 search.
  • NearReference resolution differs by procedure — do not flag the difference as inconsistency: Sandbox's search center is deliberately never resolved (investigated 2026-07-14 against real FalloutNV.esm: 1822 NearReference Sandbox packages, ~12% theoretically resolvable, not worth it). Travel/Escort/Guard DO attempt NearReference FormID resolution via resolve_entity_by_global_form_id on their own first tick (a materially different, later vantage point than Sandbox's spawn-time investigation — the whole cell has finished loading by then). Guard's fallback on a resolution miss is deliberately the actor's own position (not Travel's hash-picked point — reusing Travel's fallback was tried and reverted, since it trivially satisfies Guard's own leash check on tick one and the actor never walks anywhere). Follow has no fallback at all — an unresolved PTDT target means the actor never moves, by design (a Follow package with nothing to follow has nothing meaningful to do).
  • Seat reservation correctness (0a21d5f9): seats are keyed (furniture entity, marker index), not just furniture entity — verify a multi-marker furniture (bench, long table) seats one actor per marker independently rather than treating the whole furniture as one seat. Sandbox-specific; the other six procedures don't touch furniture.
  • Legacy marker over-match (known v0 limitation, Sandbox-specific): FNV/FO3/Oblivion BSFurnitureMarkers carry no AnimationType, so is_sit_marker treats every legacy marker as sit-eligible — sleep/lean markers on FNV furniture will be over-matched as sit targets. Confirm this is still documented as a known gap, not silently "fixed" by a heuristic that could misfire.
  • Live-tracking vs. frozen destination (Follow/Escort vs. Travel/Guard): follow_system and escort_system's collect phase re-read the target's GlobalTransform fresh every tick; travel_system, escort_system's lead phase, and guard_system resolve/pick a position exactly once and freeze it. A NearReference target that moves after Travel/Guard resolution is NOT re-tracked — that's Follow's job, and conflating the two is a finding.
  • Patrol shares Wander's algorithm on purpose, not by accident: no patrol-route/waypoint data is decoded anywhere in this codebase (Bethesda's real routes come from linked patrol-idle markers, outside PACK's own sub-records) — v0 Patrol calls wander_system's shared step_oscillating_wander core directly. Do not flag "Patrol is identical to Wander" as a bug; do flag if patrol_system silently diverges from wander_system's core without an equivalent update on both sides, or if it duplicates the state machine instead of calling the shared function.
  • All seven are opt-in, none in the default scheduler: BYRO_SANDBOX_SIT/BYRO_WANDER/BYRO_TRAVEL/BYRO_FOLLOW/BYRO_ESCORT/BYRO_GUARD/BYRO_PATROL (boot.rs) — a regression that registers any of these seven systems unconditionally silently changes FNV NPC behavior for every cell load, not just test scenarios. Output: /tmp/audit/fnv/dim_9.md

Phase 3: Merge

  1. Read all /tmp/audit/fnv/dim_*.md.
  2. Combine into docs/audits/AUDIT_FNV_<TODAY>.md:
    • Executive Summary — FNV is the baseline; any regression against the ROADMAP-recorded numbers is at least HIGH (CRITICAL if it breaks a shipped foundation).
    • Dimension Findings — grouped by severity per dimension.
    • Baseline Comparison Table — ROADMAP number vs observed for entity count, draw count, FPS, fence, parse rate, record count (cite the ROADMAP commit you compared against).
    • Regression Guard List — previously-fixed issues this audit verified still correct.
  3. Remove cross-dimension duplicates.

Suggest: /audit-publish docs/audits/AUDIT_FNV_<TODAY>.md

Skills similaires