feat(simulation): district-window zoom-ladder renderer — aliveness_probe --render/--window/--anchor (T-1123)

Renders true-2km district maps (derive_district per cell, NOT the coarse eager grid) as PNGs: morphology (17-zone palette transliterated from atlas_marker_overlay.gd, sync-commented), elev/temp/moisture/vegetation panels, anchor crosshair, km-honest filenames + manifest. believability.rs gains cascade_snapshot_for_body (cascade_for_body now a thin wrapper); probe re-derives the D-203-freed TerrainAnalysis via run_layer1 only under --render. Determinism proven: 5/5 panels byte-identical across passes AND cross-process (md5s reproduced in a fresh run). ~8us/district debug. First-ever look at the district layer produced the T-1125/T-1126/T-1127 findings (D-227 seam ends at ~500km; ocean-blind vegetation; unrendered glaciation).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 22:26:57 +02:00
co-authored by Claude Fable 5
parent 96737756dd
commit cf6ef2bc31
2 changed files with 440 additions and 10 deletions
+22 -5
View File
@@ -45,9 +45,9 @@ use serde::{Deserialize, Serialize};
use crate::atlas::attractor_matching::CityRecord;
use crate::atlas::body_params_reader::BodyParamsReader;
use crate::atlas::body_world_state::BodyWorldState;
use crate::atlas::cascade::{run_cascade_from_heightmap, CascadeLayer};
use crate::atlas::cascade::{run_cascade_from_heightmap, CascadeLayer, CascadeSnapshot};
use crate::atlas::chunk_context::derive_chunk_context;
use crate::atlas::district_profile::DistrictProfile;
use crate::atlas::district_profile::{BodyParams, DistrictProfile};
use crate::atlas::heightmap::{load_heightmap_png, GRID_H, GRID_W};
use crate::atlas::scale::{ChunkPos, DistrictPos, CHUNKS_PER_DISTRICT, CHUNK_M};
use crate::atlas::voxel::{derive_voxel_column, Vegetation, Water};
@@ -508,9 +508,26 @@ pub fn seed_to_u64(seed: &str) -> u64 {
///
/// `Err` if `systems.db` or the body's `heightmap.png` cannot be found, or the body
/// has no params — callers (the regression harness) may *skip* on that rather than
/// fail, so this is the one believability entry point that does I/O. Tries both the
/// repo-root and `server/`-relative paths so it works from either CWD.
/// fail, so the `*_for_body` loaders are the believability entry points that do I/O.
/// Tries both the repo-root and `server/`-relative paths so it works from either CWD.
pub fn cascade_for_body(world_seed: u64, body_id: &str) -> Result<BodyWorldState, String> {
cascade_snapshot_for_body(world_seed, body_id)
.map(|(snapshot, _params)| snapshot.into_body_world_state())
}
/// Like [`cascade_for_body`], but returns the raw [`CascadeSnapshot`] — which still
/// carries the transient `TerrainAnalysis` — together with the body's [`BodyParams`].
///
/// This is the entry point for callers that need the *on-demand* district-derivation
/// inputs (`district_profile::derive_district` wants `&TerrainAnalysis` +
/// `&BodyParams`), e.g. the aliveness probe's `--render` district-window mode
/// (T-1123). [`cascade_for_body`] is this plus the `BodyWorldState` conversion,
/// which drops the TerrainAnalysis per the D-203/T-1048 size budget — take it off
/// the snapshot *before* converting.
pub fn cascade_snapshot_for_body(
world_seed: u64,
body_id: &str,
) -> Result<(CascadeSnapshot, BodyParams), String> {
let db = first_existing(&["server/data/systems.db", "data/systems.db"])
.ok_or_else(|| "systems.db not found".to_string())?;
let hm_path =
@@ -538,7 +555,7 @@ pub fn cascade_for_body(world_seed: u64, body_id: &str) -> Result<BodyWorldState
Some(&params),
CascadeLayer::Region,
);
Ok(snapshot.into_body_world_state())
Ok((snapshot, params))
}
/// Read a body's settlements from `atlas_city_names`, mirroring