"Deep audit of the exterior layers EXAL / SKYAL / WATAL + ground cover — translation boundary, terrain + LOD, ground-cover pipeline, sky bake / clouds / SH, weather + sun, water translation"
Instal·la
npx skillscat add matiaszanolli/byroredux/claude-commands-audit-exterior Instal·la des del registre de SkillsCat.
Exterior Audit — EXAL / SKYAL / WATAL / Ground Cover
Read _audit-common.md and _audit-severity.md for shared protocol.
Audits the outdoors: per-game ESM environment data -> one canonical form -> terrain, LOD, ground cover, sky, weather/sun, water. Cite the code-verified specs: docs/engine/exal.md, exal-groundcover.md, exal-trees.md, skyal.md, watal.md; VRAM in docs/engine/memory-budget.md § Sky and Ground Cover. Doctrine mirrors /audit-nifal: single-boundary (one translate_* site per category), no-fabrication (a new constant cites a source or is a documented engine choice), no-leak, no-render-time-fallback (no GameKind/version branch downstream of the boundary, Rust or GLSL). Orchestrator: each dimension is a Task agent (max 3 concurrent).
Not owned here: water shading/sync, sky consumption in triangle.frag/raytrace.glsl, GPU-struct lockstep, FSR -> /audit-renderer (Dim 8, 10, 3, 11); buoyancy -> /audit-physics Dim 5; byte-level WTHR/CLMT/WATR/LTEX/GRAS/LAND decode -> /audit-esm; .spt -> /audit-speedtree; streaming cost -> /audit-performance; NIF materials -> /audit-nifal.
Severity: _audit-severity.md applies; a wrong/divergent canonical value out of an EXAL translate_* is HIGH (all-game blast radius, no render-time fallback).
Parameters: --focus <dims> (default all 7); --game <name> restricts Dim 1, 2, 5, 6; --depth shallow|deep (deep = trace one worldspace ESM -> ECS -> GPU; default).
Extra per-finding fields: Dimension (the dimension name below); Tier Violated (single-boundary | no-fabrication | no-leak | no-render-time-fallback | n/a); Game Affected.
Phase 1: Setup
mkdir -p /tmp/audit/exterior;gh issue list --repo matiaszanolli/ByroRedux --limit 200 --json number,title,state,labels > /tmp/audit/issues.json.- Read the newest
docs/audits/AUDIT_EXTERIOR_*.md; scope to dimensions whosePaths:changed (git log --since=<report date> --format='%h %cs %s' -- <Paths>). - A
Guard:counts only while live:grep -B3 'fn <name>' <file>shows a plain#[test](not#[ignore]) still asserting its name. Aim at what guards cannot see. - Cargo tests only; no windowed engine launch (feedback_no_parallel_engine_launch).
Phase 2: Dimensions
Dimension 1: EXAL boundary discipline
Paths: byroredux/src/env_translate/ (mod.rs PNAM walk, lod.rs, climate.rs, water.rs, sky_weather.rs, fallback.rs; split from one file by #5584), byroredux/src/groundcover_translate.rs, byroredux/src/fog.rs, byroredux/src/scene/world_setup.rs (orchestration only), byroredux/src/cell_loader/exterior.rs, byroredux/src/app_step.rs (per-tick XCCM override call)
First step: grep -rn 'translate_sky\|translate_weather\|translate_exterior_cell_lighting\|procedural_fallback_' byroredux/src | grep -v -E 'env_translate/|test' — callers are scene/world_setup.rs (incl. install_interior_outdoor_defaults: a direct---cell interior boot installs the canonical outdoor sky once, weather_system advances it; render/sky.rs rebuilds nothing since 387bb9d9c, #4902), systems/weather.rs, cornell.rs; a SkyParamsRes { / WeatherDataRes { literal outside env_translate/, components.rs and #[cfg(test)] is a second site (the let &WeatherDataRes { in systems/weather.rs is #4733's destructure).
- Sole producers:
translate_sky,translate_weather(carries the JNAM table with canonical [0, 1] alphas so the seed frame equals the steady-state sampler, #5344,ad280e17c; resolves IMGS itself from anImageSpaceSourcesbundle, #4416; caller-side patch removed in2eab265fe, #4914, guardtranslate_weather_resolves_the_exterior_image_space_itself),translate_exterior_cell_lighting,procedural_fallback_*,resolve_exterior_climate(->resolve_worldspace_climate) /resolve_cell_climate/resolve_default_weather,translate_lod_water,translate_terrain_lod_textures; bulk--grid, streaming and XCCM overrides all install throughapply_environment. No-climate is an explicit canonical default, not a render-loop branch. The interior-boot default is markedProvisionalOutdoorEnvironmentso the first real weather snaps instead of cross-fading (only_a_seen_weather_cross_fades). Regression guard: the cross-fade promotesimage_space(#4901,d093f194b,image_space_is_promoted_when_the_transition_completes) — the #4481 unpromoted-field class has recurred five times, so check every newWeatherDataResfield againstpromote_weather_transition_target. GameKindonly as table-shapedmatchreturning data (terrain_lod_layout,default_water_for_worldspace,object_lod_scheme,DefaultLandTexture::for_game,combined_lod_supported,resolve_exterior_climate's rung table; parse-boundary arms such as thewrld.rsstamp below are allowed). Flaggame ==in logic and any game token insky*/clouds/groundcover_*GLSL.- Climate resolution is one boundary call from
build_exterior_world_context:resolve_exterior_climate(env_translate/climate.rs, #5423,b3e679dba). Rungs: (1) WRLDCNAMup the PNAM-gatedWNAMchain (resolve_worldspace_climate); (2) the centre cell's XCLR -> REGNCNAM(region_climate_for_center, orthogonal-neighbour probe,b4497ec1d; its test still lives incell_loader/exterior.rs), mod-only: REGNCNAMis xEdit-undefined and vanilla authors none in any of the four census games (#5421,bd052048a; the "Oblivion-era regions author one" claim was retired from exal.md and theresolve_exterior_climatedoc by #5519,65cadde2f); (3) Oblivion only,oblivion_climate_rungs(#5388,217bcbc9f): the chain's authoredCNAM, then the CS<key>Climatename along the wholeWNAMchain (named_climate), then a logged richest-climate last resort. Oblivion children inherit by a parse-time stamp of the LAND/LOD/WATER/CLIMATE PNAM bits (crates/plugin/src/esm/cell/wrld.rs, #5374,67afa4b95); water, climate and (via PNAM0x02"Use LOD Data") the distant LOD rings consume it (no parent LAND outside the child's footprint, open #5505; the rings key onlod_source_worldspace/ExteriorWorldContext::lod_worldspace_key()since #5496,2ca57c4bc, guardinstalled_fo3_statesman_roof_inherits_wasteland_lod+ the synthetic chain tests), and the Oblivion inherit-all is that parse-time PNAM stamp walked by the oneinherit_up_chain(the flag-less inherit_all_up_chain twin was deleted by #5516,65cadde2f; the surviving pin isoblivion_child_gets_the_inherit_all_stamp_and_eras_stay_real). TES5/FO4XCCMis a REGN reference consumed as CLMT; it falls back to the worldspace climate (open #5463). - Default weather is one rule for the worldspace resolve and the per-cell XCCM re-resolve (
resolve_default_weather, #5424,302394f94): highest non-negative-chance resolvable WTHR, else for climates with only WTHS-referencingWSLTrows (ClimateRecord.seasonal_weathers) the authoredDefaultWeatherWTHR stand-in (chancei32::MAX;424dfe0e1, #5363). WTHS's reflection schema decodes sincea5484faf6(#5364 closed), but its instance values are uninterpreted, so the stand-in stays. Guardwths_only_climate_resolves_the_defaultweather_stand_in; its "both callers share the rule" half is asource_scanpin since #5517 (65cadde2f):cell_loader/exterior.rsandscene/world_setup.rsmust callresolve_default_weatherand neither may namedefault_weather_by_edid. A cell's XCCM override is resolved once per grid cell, success or failure (climate_override_grid, #5529,2fd7ee270). - Sentinels are not leaks (
skyrim_dalc_per_tod: None, no default water,sunlight_dimmer1.0 without HNAM); grep a parsed field's consumers before calling it "parked". Constants cite a source or are documented engine choices:SUN_SOUTH_TILT(no authored latitude, exal.md §9 Q1), cloud coverage 0.86/0.80/0.70/0.40/0.55 (skyal.md §2.3). fog.rs(exterior half): the near/far ramp becomes aFogMediumonce viafit_legacy_fog_extinction; runtime never rebuilds a ramp. Particle fire/smoke volumes there ->/audit-rendererDim 8.- Guard:
material_translate.rs::every_exterior_spawner_inserts_a_boundary_materialwalksSPAWNER_ROOTSrecursively and, since #4856, counts boundary calls (translate_texture_only_material*,translate_material,insert_object_lod_submesh_material) against, MeshHandle(inserts per enclosing function. Blind to other insert idioms and to a translate hoisted into a caller. Its stripper removes only inlinemod x {test blocks (#4917,5fe978b1d,stripper_keeps_production_after_out_of_line_test_module_declarations);rebuild_lod_water_mesh's mesh swap is an explicit exemption (d1ec1c57f).env.health(commands/env_health.rs) gates non-finite/negative/non-unit inputs, incl.WeatherSkyStateand the image-space slots with a field-list completeness scan (#4916,a55a2b906); the interior/exterior check fires only for exterior lighting under a non-exterior sky (#4915,exterior_lighting_under_a_non_exterior_sky_is_caught).
Dimension 2: Terrain, splatting, LTEX/GNAM
Paths: byroredux/src/cell_loader/terrain.rs, terrain_seam.rs, crates/renderer/shaders/include/terrain_sample.glsl
First step: cargo test -p byroredux terrain
spawn_terrain_mesh+build_cell_splat_layersare the one LAND translate site (33x33 grid; FO76/Starfield ship no LAND). Splat: <=TERRAIN_SPLAT_LAYERS(8, one named constant host + GLSL, #5113/#5173) lanes, coverage-aware truncation, BTXT feather; optional normal/specular roles use handle 0 = "no contribution" (never the checker) and upload linear. Per-lanecover_affinityis keyed from the LTEX editor ID (cover_affinity_key, #4899), never the TXST diffuse path. The BTXT base is a cover input too:base_cover_affinity(tile) / the cell record carry it andbyroGcAffinitycomposes base-then-lanes in the diffuse loop's order (#4903,177774890;groundcover_affinity_composes_the_base_in_diffuse_loop_order). The BTXT feather covers internal quadrant edges only; cell edges stay hard at vanilla parity by decision (#4905,btxt_transition_cell_edges_stay_hard_vanilla_parity): not a finding.LTEX.GNAM(an array since #4642, carried throughmerge_fromsince #4904) ->authored_grass_for_splat_layers(GPU lane order) ->TerrainTile.authored_grass, read by nothing yet (the scatter by design; the model tier uses climate, #4906).- Guard:
gpu_instance_layout_tests.rs::gpu_terrain_tile_is_176_bytes,shader_contract_tests.rs::named_splat_offset_reads_keep_their_unorm_recovery,terrain_splat_tests.rs. Unseen: >8 real layers, a DLC overriding one LAND tile.
Dimension 3: Ground-cover pipeline
Paths: crates/renderer/src/vulkan/groundcover.rs (+ groundcover/construct.rs, groundcover/frame.rs, split by #5089), groundcover_models.rs (Phase C model tier), groundcover_stats.rs, groundcover_bench.rs (opt-in §11.1 measurement harness), crates/renderer/shaders/groundcover_*, crates/renderer/shaders/include/groundcover_*.glsl, crates/renderer/src/shader_constants_data.rs (GROUNDCOVER_*), crates/core/src/ecs/components/groundcover.rs, byroredux/src/render/groundcover.rs
First step: cargo test -p byroredux-renderer groundcover && cargo test -p byroredux groundcover
- Determinism: seeds are frame-invariant integer hashes (only the tier cross-fade uses the frame serial); compaction is in candidate order, never an atomic race. Guard:
groundcover_seed_hashes_are_integer_and_frame_invariant. - Sync (#4293): counter clear = zero fill, then ordered seed fills (
counter_seed_fills_are_sequenced_after_the_zero_fill); source-shape only, so hazards need aBYRO_VALIDATION=1run. - Budget:
GROUNDCOVER_MAX_CHUNKS256 xGROUNDCOVER_MAX_BLADES_PER_CHUNK16,384 x 16 B = 64 MiB arena (64.6 MiB total, allocated on every RT device). The model tier adds 8.3 MiB (groundcover_model_resident_bytes). Guard:memory_budget_ledgers_the_sky_and_ground_cover_owners(both rows),chunk_cap_covers_every_chunk_in_reach. - LOD tiers: blade / ribbon (projected-height crossover, not distance) / clump card / always-on terrain detail layer. Guard:
tiered_indirect_streams_preserve_fixed_blade_slabs. - Lifecycle (#4307):
recreate_draw_pipelinesruns in resize'sformat_changedarm. Guard:context/resize.rs::a_surface_format_change_rebuilds_every_main_pass_pipeline. - FSR (#4297): opaque ribbons write reactive 0 / T&C 0 with previous-pose motion vectors; only the stochastic hand-off writes bounded (<=0.9) reactive (§12.14). Guard:
blade_motion_and_fsr_mask_contract_stay_material_driven(shader-text only). - Boundary: per-game data enters only via
groundcover_translate.rs(layer_affinity:nograss/asphalt suppression beats any positive keyword; climate palette;resolve_wind*);GroundCoverDimmer(HNAM grass dimmer) ridesweather_system. Blade ground colour is the terrain's ownbyroTerrainSplatAlbedochain, BTXT base included (#4907,blade_ground_colour_uses_the_shared_terrain_splat_chain). Not findings: GRAS is not translated into blades (§12.12 census); REGN RDGS/RDOT is out of scope (§10, #3301). A new bare literal ingroundcover_*GLSL is a finding; the blade-shaping set is named inshader_constants_data.rsand registered uncited in §12.12 (#4378,fd0cd577c/4fc8ab8b2). - Authored-model tier (§12.12 Phase C, #4413): GRAS ->
resolve_authored_cover(one caller,scene/world_setup.rs) ->AuthoredCover->groundcover_models.comp, drawn as indexed-indirect shapes. Guard:model_timer_encloses_all_phases_and_stats_copy(#4866). Candidates sit on one world lattice across chunk borders (#4919,model_candidates_tile_one_world_lattice_across_chunk_borders); over the instance cap each record keeps whole plants, nearest chunks first (#4920/#5176,over_budget_grant_keeps_the_nearest_chunks_plants), surfaced asstatsGCModelsdemanded/emitted and zeroed on nothing-to-place frames (#5220); rigid by documented decision (#4921, §12.12); blend-only GRAS shapes excluded at template spawn (blend_only_cover_shape, #4924). Record selection weighs climate x authored density over placeable records with one union accept rate1 - PI(1 - d_i)(render/groundcover.rs,1505b4404;3e6d94ff0was the earlier saturating bake). Known-open: not localised per lane LTEX->GNAM (#4906's remaining half; wrong species per region, DLC bleed). - Known-open: Phase 4 RT proxy gated on need (§5 Stage 2).
Dimension 4: Sky, weather, sun
Paths: crates/renderer/src/vulkan/{sky_cube,sky_dome,cloud_noise}.rs (+ sky_cube/), crates/renderer/shaders/{sky_cube,sky_prefilter,sky_irradiance}.comp, crates/renderer/shaders/include/{sky,clouds,sky_sh,sky_cube_direction,medium_transport}.glsl, byroredux/src/systems/weather.rs, byroredux/src/render/sky.rs
First step: cargo test -p byroredux-renderer sky_ && cargo test -p byroredux weather; GPU (prefilter mips + SH): cargo test -p byroredux-renderer gpu_filter_preserves_constant_radiance --lib -- --ignored
- Bake: every frame before the geometry pass, from the same
CompositeParamsas the background; ready flagexterior_sky_tint.w= pipeline presence. Guard:every_sky_cube_consumer_gates_on_the_ready_flag. - Prefilter/SH: 8 mips, 256 GGX samples per texel; 9-coefficient SH (
SKY_SH_COEFFICIENTS, set 1 binding 21; workgroup widths and the SH count come from the generated header since #5620,fff5ed9a9) replaces the ambient fallback and is deliberately unoccluded, each consumer owns its occlusion (sky_visibility_pin). - Clouds: one
cloud_marchfor background and bake (composite_does_not_carry_its_own_copy_of_the_sky, forbidden set derived from the livesky.glsl+clouds.glsldeclarations and scanningsky_cube.comptoo since #5180); height from radial altitude (the_cloud_density_profile_uses_radial_altitude); adaptive bounded march (the_cloud_march_steps_adaptively); each WTHR layer gets the mip its own tile scale needs (#4230:every_cloud_layer_samples_with_its_own_lod); sun term =compute_directional_uploadilluminance. - Weather -> sun:
translate_weathercarries HNAMsunlight_dimmer,weather_systemmultiplies the sampled sun colour (steady state + cross-fade, #4481). Guards:sunlight_dimmer_translates_from_the_hnam_block,consumer_multiplies_sunlight_and_grass_dimmers_without_a_transition,promotion_carries_the_dimmers_and_the_steady_state_uses_them. The froxel medium's sun issun_illuminanceon both arms ofvolumetric_sun(#4909,overcast_dims_the_portal_sun); the cross-fade blends the two climates' TNAM breakpoints for the sun arc (#4926,sun_arc_eases_between_the_two_climates_arcs) and publishes them asSkyParamsRes::tod_hoursfor water/time (#5179,the_published_breakpoints_ease_with_the_crossfade). Direct-sunexp(-2.5*coverage):render/directional_upload_tests.rs::exterior_cloud_coverage_attenuates_the_direct_key. TOD / sun arc / cross-fade / DALC easing:systems/weather.rstests. - Clock + interior:
GameTimeResis re-installed only byensure_game_time(insert-if-absent). InteriorSkyParamskeep the cell's own fields; the outdoor sky rides separate lanes only:exterior_zenith_color(->GpuCamera::exterior_sky_tint, window-portal escape, #3323),portal_outdoor_sky(bake + background through openings),portal_sun_*(volumetric apertures, #4839),interior_show_sky. Neverzenith_color(#2226):render/sky.rs::stale_exterior_sky_params_res_does_not_leak_into_interior. Night detail gates on the drawn palette (sky_lower.w), not the room's weather flag (#4908,night_sky_details_follow_the_outdoor_palette_not_the_weather_flag);pack_sky_domeis the one SkyDome packer for composite and interior cube (#4925,interior_sky_cube_packs_the_outdoor_palette_like_an_exterior_composite); the lane is specified in skyal.md § interior outdoor-sky lane (#4927). - Sun disc / sunset:
sky_radiancefloors the disc luminance at 1.8x the local sky behind it (hue-preserving, never attenuates;c60405083);build_tod_keyscarries a(sunset_end, TOD_SUNSET)hold key so the palette stays SUNSET through the sun's whole descent (56786698b,tod_keys_hold_sunset_through_the_suns_descent). Both shape golden-hour captures and are recorded in skyal.md (#5425,4e32d33ec); the disc floor is a literal insky.glslthatcomposite/sky_cube.compshare. - Documented, do not re-file (skyal.md §2.3-§3, 2026-09-14/18): cloud-type taxonomy, noise frequencies, atmosphere LUT, positional cloud shadows are open; coverage constants and multi-scatter a=b=c=0.5 are engine choices.
Dimension 5: Water translation (WATAL)
Paths: byroredux/src/env_translate/water.rs (water arm), byroredux/src/cell_loader/water.rs, byroredux/src/streaming/mod.rs (spawn_lod_water / refresh_lod_water), byroredux/src/material_translate.rs (attach_mesh_water, water_volume_from_phantom), crates/core/src/ecs/components/water.rs, byroredux/src/systems/water.rs, byroredux/src/commands/water.rs
First step: cargo test -p byroredux water
- Two composition sites: ESM WATR/XCWT ->
resolve_water_material; NIF water shader ->attach_mesh_water->water_material_from_mesh. Any other productionWaterMaterial { .. }outside#[cfg(test)]is a third. - Frames: WATR noise-layer angles go through
watr_angle_to_engine_xz(#4727), scoped per game since0d113b35a(#4910): Skyrim/FO4 convert (FO76 tentatively), Oblivion and FO3/FNV/Starfield read un-rotated (frames OPEN in watal.md); guardwind_angle_conversion_is_scoped_per_game. NAM0-filled layers skip it (#4900). Scroll:compose_flow_scrollsis the one composer for the WATR arm and the REFR XWCU merge (#4911,xwcu_current_recomposes_the_pattern_scroll; calm WATR promoted under an authored current, #5183); flowing kinds damp weather transport toFLOWING_WATER_WEATHER_TRANSPORTand the Rapids third layer reads the baked scroll, not BU/s (#4929,37cc637e9,flowing_kinds_damp_the_weather_transport_but_calm_keeps_it). - SENTINEL vs AUTHORED per game is watal.md §4's table. Guard:
resolve_water_material_sentinels_are_game_invariant(sweeps everyGameKindsince #5184). Byte offsets (Oblivion DATA shift, Skyrim DNAM tail) are decode ->/audit-esm; check promotion: each decoded field reachesWaterMaterial/WaterFlowor is open in watal.md §2.WaterNormalEncodingis set in the parser (FO3/FNV =OffsetNoise) and honoured on every normal layer inwater.frag(source guard invulkan/water.rs). - Heights: Oblivion Z=0 when NAM2 resolves (a child takes its parent's, #5374), other games WRLD DNAM; XCLW tri-state (
an_absent_xclw_stays_absent); LOD water NAM3/NAM4 is render-only (lod_water_is_render_only_and_cannot_create_false_submersion); distant water is one quad per cell at that cell's effective XCLW height (dry skipped, LAND-occluded dropped, #5243), its holes only where cells are actually resident, not aradius_unloaddisc (#5244/#5334,e38ff49d2,distant_water_holes_only_actual_residency; the plane rebuilds on residency change, coalesced to 250 ms, from a spawn-time cell projection, #5289; a failed or emptied rebuild records its attempt, #5339). The mesh's height and form are one boundary rule,resolve_distant_water(env_translate/lod.rs): inherit height = the full-detail default (default_water_for_worldspace, not NAM4, #5337), form = NAM3 else the full-detail NAM2, which is how Oblivion gets distant water at all (#5335;oblivion_gets_distant_water_from_the_full_detail_default). - Mesh water depth: authored
bhkSimpleShapePhantombounds beat the named4 x radiusheuristic (authored_phantom_volume_replaces_the_radius_heuristic). Currents: WATR NAM0 velocity, REFRXWCUentry 0 overrides (reference_current_overrides_the_watr_current), CELLXWCUis not a velocity; flow bounded (flow_speed_ladder_is_ordered_and_bounded). The wave tuning (WATER_WAVE_*) and the scroll prep (WaterMaterial::wave_scrolls) live once incrates/core/src/ecs/components/water.rs(#5582,7cf2bd086):water.vert(via the generated header), the render upload and the CPU crest sampler all read them, so a retyped literal in any of the three is a finding; the flowing-kind weather damping is the oneWaterKind::weather_scroll_forfor renderer, camera submersion, buoyancy and the player (#5354,cff1f9c42). - Hazard-water damage has one owner,
water_damage_system(#5548,c28571563); splash/ripple events land on the contact's published waterline (#5562). Physics stays game-invariant (grep -rn GameKind crates/physics/srcis empty). Open (watal.md): water-walking, freezing, Skyrim DNAM tail, FO76/Starfield scope.
Dimension 6: Distant LOD and trees
Paths: byroredux/src/cell_loader/{terrain_lod,terrain_lod_btr,object_lod,placement_lod,lod_bands,lod_support,lod_coverage,legacy_lod_index}.rs, byroredux/src/streaming_helpers.rs, crates/bsa/examples/probe_lod_corpus.rs
First step: cargo test -p byroredux lod; with game data, cargo run -p byroredux-bsa --example probe_lod_corpus (counts all three LOD families).
- One partitioning ring:
select_lod_quadsdescends a 4/8/16/32 quadtree so a quad emits itself or recurses, never both. Objects usecoarsen_to_available(#3502); terrain keeps descending (synthesis always covers). Guard:partition_never_covers_a_cell_twice,missing_baked_asset_subdivides_instead_of_holing,coarsened_selection_is_still_a_partition. - Schemes are data tables:
terrain_lod_layout(Oblivion FormID-keyed, FO3/FNV editor-ID quadtree, Skyrim+FO4.btr, FO76/Starfield none);object_lod_scheme(BakedBtoSkyrim+FO4+FO76 (#4488),FalloutLegacyBlocksFO3/FNV, Starfield none; OblivionDistantLOD\*.lod->_far.nifviaplacement_lod_supported). FO3/FNV ship zerodistantlod\*.lod, but "FO3/FNV have no distant-object LOD" is a false premise (#3321). Regression guard #5222 (b7987d813): FO3/FNV quads sit on per-worldspace lattices (26 of 29 FO3 worldspace/level sets are off (0,0), no single origin explains them), so the legacy object + terrain rings no longer derive an origin:LegacyLodQuadIndex(cell_loader/legacy_lod_index.rs, scanned once per streaming state from the archive name tables) feedsselect_authored_lod_quads(lod_bands.rs; ring touch by footprint, too-near yields to finer authored levels, seam suppression, terrain completes uncovered ring ground with level-4 synth tiles).worldspace_lod_grid_originstill returns (0,0) for non-combined games and only serves the synth/placement grids. Pins:authored_selection_reaches_off_lattice_quads,authored_level_8_only_selection_fills_its_near_band,authored_selection_partitions_between_levelsplus--ignoredreal-data tests inlegacy_lod_index_tests.rs; live FO3/FNV visual verification is still owed. Before selection,prune_same_level_overlapskeeps each level's majority lattice-residue class, dropping leftover-generation quads that overlap on one level (six FO3 worldspaces; FNV and both terrain tables pass through; #5387,fbf1bed74). Guards:same_level_leftover_generation_quads_are_pruned, and the--ignoredwashmontop_level8_spans_multiple_residues_and_all_selectno-overlap pin. Count ties go to the class covering more of the LOD source worldspace's authored exterior cells (lod_source_cell_exists), only a coverage tie falling back to residue order (#5520,a9577fd69,residue_count_tie_breaks_on_authored_cell_coverage); which lattice the shipped game loads stays open (exal.md). Both legacy rings and the index scan gate on the scheme tables (TerrainLodLayout::FalloutLegacy/ObjectLodScheme::FalloutLegacyBlocks), notGameKind(#5423,b3e679dba); the scan fires when EITHER table selects the legacy family (should_scan_legacy_lod_quadsinstreaming_helpers.rs, #5518,5a549e664,legacy_lod_scan_gate_keys_on_either_family_table) and the object ring warns when the index is missing. A PNAM0x02child owns no quads: every distant-ring key (index, archive paths,.btr, generated textures, band bounds) resolves onlod_worldspace_key()while full-detail residency stays on the child (#5496). .btris quad-local (btr_local_to_world),.btoworld-absolute; a block is.btror synthesized, never both (z-fight).lod_coverage.rs: overlap > 0 = double draw. Legacy (Oblivion/FO3/FNV) quad textures carry their image orientation from the boundary (TranslatedTerrainLodTexture, #4898);.btrand legacy quads sample with the authored CLAMP (#4912,235a90ba2,btr_terrain_lod_resolve_uses_the_authored_clamp); only the tiled base-LTEX fallback stays WRAP by design.- VWD: flag parsed (#1731) ->
stamp_visible_when_distant->resident_vwd_refr_cells(locks hoisted, #3142). Open (2026-09-29): full-model culling #3307. LOD entities carryIsLodTerrain, no BLAS, materials via the Dim 1 boundary. - Trees: near-field geometry is design-only (
exal-trees.mdre-scoped 2026-09-07, #3808:.sptcarries no geometry); vanilla Skyrim.btobakes no trees: the tree tier is the.btt+.lst+ treelod-atlas family, registered (tree_lod_supported/tree_lod_archive_path; layout census-verified62a31d6de) but unconsumed, so distant Skyrim forests render empty (known-open #4913)..bttrecords carry REFR FormIDs (matters for #3307).
Dimension 7: Acceptance gates and harness
Paths: scripts/renderer-eval-groundcover.sh, docs/smoke-tests/{m-exteriors,w1-water-traversal,m34-day-night}.sh
First step: read docs/smoke-tests/README.md (--bench-hold -> byro-dbg attach).
- Unit guards cannot see GPU output; a sky/cloud/ground-cover/water claim needs a captured frame (skyal.md §4: fixed camera,
--render-debug-mode composite_termseparates sky assembly from bloom/tonemap, capture twice for the noise floor). m-exteriors.sh <game> static|boundary|soak|cycle|water= cross-game readiness matrix (incl. WATR provenance, waterline delta);w1-water-traversal.sh= WATAL traversal;m34-day-night.sh= Skyrim sun response. All need Vulkan + game data.renderer-eval-groundcover.shenforces the washout precheck (#4482, groundcover_reference_capture_washout): luminance sd under 0.039 (env override BYROREDUX_GC_WASHOUT_SD) fails agc-backlit-*frame (the only ground-framing poses), WARNs frontlit ones; missing telemetry fails (#4509). The committed backlit references (sd ~0.01, 2026-09-20) are the veil: confirm a re-mint before trusting an A/B.3bcf6c8e8(#5482, renderer-owned) moved exposure ahead of the contrast grade and added a shadow toe for every game, so frames from before 2026-10-09 (those references, and the 0.039 line's calibration) are not A/B-comparable with new ones.--render-debug-mode water_term/water_reflpaint non-water pixels a 0.08 grey, so their frame mean is not water radiance (#5482 first misread a grade crush as zero-radiance Skyrim LOD water this way);composite_termshows the pre-grade scene.- "Chrome" surfaces:
tex.missingfirst (feedback_chrome_means_missing_textures). The harnesses runscripts/check-shader-artifacts.shbefore capture (#4738); a rebuilt.spvmay still not relink (feedback_spv_rebuild_staleness).
Phase 3: Merge and Report
- Write
docs/audits/AUDIT_EXTERIOR_<TODAY>.md: Executive Summary (findings by severity; verdict per tier invariant); Per-Category Matrix (terrain / sky / weather-sun / water / ground cover / LOD x the four invariants, boundary fn cited); Findings; Known-Open Register (dated items above, with what this pass changed). - Dedup per
_audit-common.md§ Deduplication (/tmp/audit/issues.json, priordocs/audits/AUDIT_*). rm -rf /tmp/audit/exterior; tell the user the report is ready and suggest/audit-publish docs/audits/AUDIT_EXTERIOR_<TODAY>.md(labels:terrain-exterior, pluswater,shaders,doc-rot;game:*if title-specific).