feat(simulation): Layer-3 settlement spatial-character enrichment (#956)

Derive and store each placed settlement's spatial character on top of the
#955 placement (D-212/213/214/215). Derivation is pure + integer-deterministic
(D-010); the cascade stays DB-free (D-225) — the enqueuer pre-resolves the
body's system faction onto the work item, like #955's settlements.

- TerritorialStatus (D-212): mapped from the authored dominant_faction
  (territorial_status_from_faction). Adds an AutonomistHeld variant for the
  Compact of Westphalia (self-governing bloc that rejects Assembly authority —
  neither Commission, Corp, Contested, nor truly Frontier). Grounded in
  wiki/factions/. Stored per-province on DrainageBasin.territorial_status
  (uniform per body for now; forward-compatible for per-province faction data).
- PoliticalArchetype (D-214): political_archetype(status, role), status takes
  precedence over economic_role.
- ArrangementPattern (D-215): new 5-variant enum + arrangement_pattern(); the
  block-adjacency *enforcement* stays deferred to the Quarter-skeleton gen (#957)
  — this only derives + stores which pattern applies.
- FoundingOrientation (D-213): existing fn extended with a seed-derived Free
  bearing so pioneer/open-terrain grids vary per seed.

Wiring: dominant_faction threaded through CityContextReader
(read_body_dominant_faction) → atlas proxy (cache-miss read) → AnalyzeBody work
item → run_cascade → run_layer3/match_cities. Enrichment stored on CityPlacement
(political_archetype, arrangement_pattern, founding_orientation).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-05 14:57:09 +02:00
co-authored by Claude Opus 4.8
parent 8b1005221d
commit af60d85d94
9 changed files with 514 additions and 45 deletions
+10 -2
View File
@@ -72,6 +72,10 @@ pub enum GenWorkItem {
/// dispatch time so the cascade stays DB-free. Fed to Layer-3 placement
/// (#955); empty if the body has no settlements (cascade stops at Layer 1).
cities: Vec<CityRecord>,
/// The body's system `dominant_faction` (D-237), pre-resolved at dispatch
/// time. Drives Layer-3 TerritorialStatus + spatial character (#956,
/// D-212/214/215). `None` → `FrontierUnclaimed`.
dominant_faction: Option<String>,
},
/// Generate a Phase 1 QuarterSkeleton for this city.
///
@@ -353,6 +357,7 @@ fn run_work_item(item: &GenWorkItem) -> GenCompletion {
sea_level,
body_seed,
cities,
dominant_faction,
} => match load_heightmap_png(heightmap_path, body_id, *sea_level) {
Ok(hm) => {
// Layer 1 runs at the GRID_W×GRID_H working resolution (D-202):
@@ -363,12 +368,14 @@ fn run_work_item(item: &GenWorkItem) -> GenCompletion {
hm
};
// Run through Layer 3 (settlement placement, #955): the enqueuer
// pre-resolved this body's settlements onto `cities`. A body with
// no settlements yields empty placements at negligible cost.
// pre-resolved this body's settlements onto `cities` and its
// system faction onto `dominant_faction` (#956). A body with no
// settlements yields empty placements at negligible cost.
let snapshot = run_cascade_from_heightmap(
*body_seed,
working,
cities,
dominant_faction.as_deref(),
CascadeLayer::Settlement,
);
GenCompletion::BodyAnalyzed {
@@ -463,6 +470,7 @@ mod tests {
sea_level: 0.3,
body_seed: SeedChain::for_body(42, body_id),
cities: vec![],
dominant_faction: None,
}
}