feat(simulation): thread political_archetype/morphology_zone/road_entry_directions through L3->L4 dispatch (T-1039, T-1043)

Completes the GenerateSkeleton dispatch T-1022 wired (which only carried
founding_orientation). build_skeleton_work_item now also threads:

T-1039:
- political_archetype — real value from CityPlacement, replacing the Commission stub.
- morphology_zone — looked up from the covering DistrictProfile via new
  scale::heightmap_pixel_to_district (exported HEIGHTMAP_CELLS_PER_DISTRICT;
  cascade.rs drops its local CELLS_PER_REGION literal for the shared const).
- arrangement_pattern — RE-DERIVED at L4 (option b) via the single pure fn
  attractor_matching::arrangement_pattern(&archetype, &economic_role); both
  inputs already at L4. No CityGenerationContext field, no stored derived state
  (DB-as-cache). Parity test asserts L4 re-derivation == L3-stored value.

T-1043:
- road_entry_directions — derived from T-1038's BodyWorldState.road_graph:
  octant = bearing of each incident road edge, deduped per octant, ordered by
  MaintenanceAuthority rank (the AdminFacing prestige-edge consumer). Feeds
  derive_access_points -> AccessKind::QuarterEdge; BlockJunction fallback now
  fires only for genuinely isolated settlements.

Acceptance covered: Corporate+Fjord -> Ribbon topology (not mesh+Commission);
settlement with a road -> QuarterEdge on the correct octant. 11 new tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-16 11:34:22 +02:00
co-authored by Claude Opus 4.8
parent f98297d136
commit 9c8ad83071
3 changed files with 904 additions and 23 deletions
+5 -4
View File
@@ -29,6 +29,7 @@ use crate::atlas::features::TerrainAnalysis;
use crate::atlas::heightmap::{self, BodyHeightmap, HeightmapLoadError};
use crate::atlas::layer1::{self, Layer1Output};
use crate::atlas::road_graph::{self, RoadGraph};
use crate::atlas::scale;
use crate::seed::SeedChain;
use crate::simulation::generator::{CompatibilityMatrix, GeographicAttractor, TerritorialStatus};
@@ -255,16 +256,16 @@ pub fn run_cascade_from_heightmap(
// DistrictProfile layer — pure derivation from body params + terrain.
if let Some(params) = body_params {
// ~8 cells per district on a 128×64 working grid → ~80×32 = ~2 560 districts;
// at full working resolution the budget is ~6 000/body (D-203).
const CELLS_PER_REGION: usize = 8;
// Canonical cells-per-district for the working grid (T-1039):
// shared via scale::HEIGHTMAP_CELLS_PER_DISTRICT so plugin.rs converts
// CityPlacement pixel coords with the same constant.
// body_id is required for the D-243 §4 climate edge-fuzz warp domain
// separation — derive_all_districts builds the region cache internally.
let districts = district_profile::derive_all_districts(
body_seed,
params,
&ta,
CELLS_PER_REGION,
scale::HEIGHTMAP_CELLS_PER_DISTRICT,
&snapshot.body_id,
);
snapshot.layer_district = Some(LayerDistrictOutput { districts });