feat(simulation): add Layer-2 inter-settlement road/rail graph (T-1038)

New deterministic cascade layer (CascadeLayer::RoadGraph) building the
transport topology between Layer-3 settlement placements, stored on
BodyWorldState.road_graph (session-cached, never serialized).

- MST force-connect (strict Kruskal over squared distance) + secondary
  links across a 30-60% body-scale band where the MST detours >1.5x.
- A* over the Layer-1 terrain-cost field on a coarse ~64-wide routing grid:
  ocean/lake impassable, river corridors 1/2 cost, slope >=35deg (D-210
  roughness ~0.7) 10x, else the D-210 terrain_modification_cost. Columns
  wrap (equirectangular); integer costs + index tie-break keep A*
  deterministic.
- MaintenanceAuthority per edge (new generator.rs enum) from endpoint
  political archetypes + haul length; waypoint nodes at long-edge midpoints;
  named-route identity join (designed-for, pool empty post-D-223); junction
  detection (high_connectivity_junctions, degree>=3).

Wired into the cascade (gen_queue requests CascadeLayer::RoadGraph). Adds
Copy to PoliticalArchetype. 10 road_graph unit tests + a cascade e2e test;
full suite + clippy --all-targets -D warnings green.

Deferred to T-1076 (gated on the D-242 settlement model): the RailHeadFacing
junction pass (needs a D-213 FoundingOrientation amendment + Layer-4 handling)
and the hub-selection refinement (needs baked population/specialization).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-14 20:13:26 +02:00
co-authored by Claude Opus 4.8
parent 301be77b49
commit c0d4ac89d1
7 changed files with 1249 additions and 28 deletions
+7 -9
View File
@@ -374,15 +374,13 @@ fn run_work_item(item: &GenWorkItem) -> GenCompletion {
} else {
hm
};
// Run through RegionProfile (T-1023, D-239 §1): includes Settlement
// and all prior layers. RegionProfile > Settlement in CascadeLayer ord
// so Settlement also runs when body_params is Some. When body_params
// is None the cascade falls back to Settlement as the terminal layer.
let up_to = if body_params.is_some() {
CascadeLayer::RegionProfile
} else {
CascadeLayer::Settlement
};
// Run the full cascade through RoadGraph (Layer 2, T-1038), the
// terminal layer. It subsumes Settlement, RegionProfile (T-1023),
// and all prior layers. RegionProfile derivation still gates on
// body_params internally (skipped when absent — e.g. a body with no
// params row), but the road graph needs no body params, so it runs
// for every analyzed body.
let up_to = CascadeLayer::RoadGraph;
let snapshot = run_cascade_from_heightmap(
*body_seed,
working,