docs(meta): D-242 placement-boundary amendment + D-213 RailHeadFacing amendment (T-1076)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1155,9 +1155,10 @@ Technical foundation decisions that constrain implementation: engine, client-ser
|
||||
- The district skeleton generator (Phase 1) applies `FoundingOrientation` as the base rotation for the outermost district ring. Interior districts inherit the orientation unless overridden by a `PoliticalArchetype` modifier.
|
||||
- **Hard constraint:** Maximum ±45° deviation from the parent orientation per district (same limit as D-096 `BlockPlacement.rotation_steps`). Beyond ±45°, tile-based pathfinding produces movement artifacts.
|
||||
- **Rationale:** Street grids reflect the terrain and founding logic of the original settlement. Roman camps faced cardinal directions. River towns align with the river. Coastal cities face the water. Encoding this as a named enum rather than a raw angle makes the orientation legible in the data model and debuggable during generation.
|
||||
- **Ticket:** T-914
|
||||
- **Amendment (T-1076 §4, 2026-07-16 — `RailHeadFacing` variant added; the D-242-gated follow-on):** the enum gains a sixth variant, `RailHeadFacing { bearing_degrees: u16 }` — the street grid faces the settlement's rail head. Unlike the five attractor-derived variants, it is **assigned post-placement by the Layer-2 road-graph pass** (`road_graph::assign_railhead_orientations`, run in the cascade immediately after `build_road_graph`): every settlement that is a **high-connectivity junction** (`RoadGraph::high_connectivity_junctions()`, incident-edge degree ≥ 3 — the criterion from this record's workshop source, paula-round3.md) has its attractor-derived orientation **overridden** to `RailHeadFacing`, with `bearing_degrees` the **octant-snapped** compass bearing (0 = N, clockwise; one of the eight 45° octants — integer-only math, D-010; the diagonal band is the `|minor|·2 > |major|` integer approximation, sound because the consumer snaps octants to quarter-edges anyway) from the settlement toward the **dominant incident edge** (longest `length_cells`, ties to the lowest edge index) — the direction the freight frontage faces. The Layer-4 skeleton consumes it via `skeleton_gen::railhead_edge` — the same octant→cardinal-edge snap as `coastal_edge`, feeding the same D-234b flush-frontage machinery: blocks on the rail-facing quarter edge present flush to the rail head the way waterfront blocks present flush to the quay (coastal wins if a settlement somehow carries both; variants are exclusive by construction). Deterministic: a pure function of the (already deterministic) road graph.
|
||||
- **Ticket:** T-914; T-1076 §4 (RailHeadFacing)
|
||||
- **Raised by:** Generation cascade workshop (T-897)
|
||||
- **Cross-reference:** D-096 (DistrictLayoutMode — inherits orientation), D-211 (attractor-matching — derives orientation), D-214 (PoliticalArchetype — may override orientation), D-215 (spatial arrangement patterns — uses orientation)
|
||||
- **Cross-reference:** D-096 (DistrictLayoutMode — inherits orientation), D-211 (attractor-matching — derives orientation), D-214 (PoliticalArchetype — may override orientation), D-215 (spatial arrangement patterns — uses orientation), [D-242](#d-242) (the road-graph hub refinement this variant shipped under; T-1076)
|
||||
|
||||
### D-214: PoliticalArchetype Enum and Settlement Spatial Character
|
||||
- **Date:** 2026-05-01
|
||||
@@ -1828,6 +1829,7 @@ Technical foundation decisions that constrain implementation: engine, client-ser
|
||||
- **The merge is free.** Standalone-HQ settlements are inserted as ordinary `atlas_city_names` rows (`populate_standalone_hq_settlements`, Phase B) — same schema, no corp-linkage marker — so `read_body_settlements` → `match_cities` (the "natural seam") needed **zero Rust changes**; a Standalone HQ competes for attractors purely on population/`settlement_class`, exactly like any pooled city (verified live: `cascade_for_body("GJ251c")` places "The Gate Corporation" alongside "Tributarium"/"Ruhr" through the unmodified pipeline). Two new Rust unit tests lock this invariant in (`city_context_reader.rs`, `attractor_matching.rs`).
|
||||
- **Verified against real, repeated `make regen-db` runs — each run a full recompute, not a no-op (wording corrected per PR #177 H3; the pre-review text described the one-shot backfill being stably inert):** 0 duplicate `(body_id, name)` groups (was the whole bug); 155/155 wiki corps get `corp_specialization`/`hq_placement`; **154/155 `headquarters_body` re-derived on every run** (the 1 gap is a pre-existing broken `headquarters system` reference on `sova-station-works.md`, unrelated to this record), with two consecutive runs producing **zero differences** across all 165 corps' placement columns and tenancy targets; against the prior committed snapshot the recompute changes **exactly 2 placements** — `prometheus-labs`/`kovalev-freight`, `GJ702B-belt` → `GJ702Bb` (the intended H2 outcome) — and **zero tenancy targets** (`calluna-wellness` → (GJ845-belt, Orkney Ceramics) and `namsan-collective` → (GJ268-belt, Jeju Lattice) preserved via the city-presence tier). 95 `Standalone` corps → 94 settlements emitted (same 1 `sova` gap) + 60 `CityTenant` corps → 58 links (2 unmatched: the GJ 702B pair — now correctly on planet `GJ702Bb`, which still has zero authored city names, like every body in that system: a wiki content gap [T-1115], logged, not an import error). A poisoned-copy test confirmed no DB state survives the reset: hand-written garbage in all three columns is wiped and re-derived (wiki corp) or reverted to NULL (legacy corp with no authored source — the removed-key case). The 10 legacy DB-only corps seeded by the now-deleted `populate-corporations.sh` (`mvg`, `adams-ford`, `dsmc`, and the 7 Cygni-B "combines") have no wiki page to author `corp_specialization` from and are reset to NULL across the board each run — a logged, non-fatal gap (they are load-bearing for 2,468 `brand_products` rows via `generate_brands`, which reads `corporations` directly and doesn't care about wiki-page existence, so their rows are kept, not deleted).
|
||||
- **Amendment (T-1075, 2026-07-16 — population/class derivation clarified):** the phrase above, "the per-settlement spread is **carried down into** the city/settlement source data," is superseded by **derivation-at-import, not hand-authoring**: `import_economics` computes the per-city population spread from the authored `bodies.population` on **every** `regen-db` run, via a documented integer rank-size (Zipf, exponent 1) curve (`atlas.py::_zipf_population_spread`, constants named + commented — tuning the curve is a deliberate code change, not a data edit). There is no per-city population *source file* — only `bodies.population` (already-baked) is authored; the per-settlement split is 100% derived. The spread runs over the **corrected, merged pool** (post-T-1074: Standalone-HQ settlements are ordinary rows by the time this runs, so they get a real share of their body's population like any city, not a bolted-on afterthought). `settlement_class` defaults every pooled row to `PopulationBudget` (D-196); a small authored override TOML (`wiki/economics/settlement_name_locked.toml`, `[[hero]]` stanzas keyed on `(body_id, name)` — not the volatile numeric id) pins 8 hero settlements to `NameLocked`. **EconomicTriggered and OrganicGrowth stay simulation-time** (D-196) — out of bake scope; so is the D-196 *generation-time* consumer that reads the baked population against the >=50k-active/<5k-ghost thresholds to decide skeleton depth (confirmed live: no such consumer exists yet anywhere in `server/src/atlas` — this record's bake only writes the values a future ticket will read).
|
||||
- **Amendment (T-1076 item 0, 2026-07-16 — the Standalone/CityTenant boundary made explicit; Jeroen's ruling):** the original examples ("extraction/mining, heavy industry, shipyards …" → Standalone) were operationalised at T-1074 as *`bulk_class_projected ≠ NonPhysical` → Standalone* — an over-generalisation that made every physical-goods specialization a standalone settlement (95/60 split) and produced a **winery and a leatherworks as standalone asteroid-belt settlements**. The boundary is now explicit, so it cannot be re-coarsened: **Standalone applies ONLY where the HQ is itself an industrial complex / company town** — (1) extraction/mining/quarrying, (2) heavy vehicle + shipyard manufacturing, (3) heavy energy (refinery/extraction complexes), (4) gate/orbital infrastructure fabrication. **Everything else is CityTenant**, explicitly including craft/consumer manufacturing (distilleries, breweries, wineries, ceramics, textiles, furniture), agriculture of every scale (the estate/agribusiness *office* sits in a market town; the fields are not the HQ), and high-tech fabs. Applied per-value in `corp_hq_placement.toml` (one-line justification on every flipped stanza): 9 values flipped Standalone→CityTenant (`estate_farming`, `breadbasket`, `terroir_agriculture`, `terroir_spirits`, `terroir_organics`, `general_industrial`, `consumer_goods_bazaar`, `precision_tech`, `marine_farming`); 11 stay Standalone (`ore_extraction`, `company_mining`, `marble_monopoly`, `rare_mineral_extraction`, `lattice_material_source`, `fuel_production`, `geothermal_hub`, `shipbuilding`, `vehicle_production`, `gate_fabrication`, `military_industrial`). Corp split moves 95/60 → **11 Standalone / 144 CityTenant**; `atlas_city_names` 423 → **340** rows (329 pool + 11 HQ settlements); tenant links 58 → **129** (15 unmatched: 14 on bodies with no city to tenant + `sova-station-works`, whose broken HQ-system ref predates this record). The vanished belt settlements (Orkney Ceramics, Jeju Lattice, Groot Karoo Cellars, Kalahari Leatherworks) took their two belt tenancies with them — expected, correct data change under the boundary; the H2 placement hysteresis converged in one transitional run (fixpoint verified: two subsequent regens byte-identical across all 165 corps, 0 duplicate `(body_id, name)` groups, 0 import errors). Cygni B's `NameLocked` hero pin moved from the retired "Cygni Combines" HQ settlement to its pure-pool city "Metropolis". **Jeroen's rendering note, recorded here so it is not re-litigated as placement:** CityTenant HQs often sit on a settlement's **edge** or in **industrial clusters** — that is CityTenant *rendering/district-placement color* for later phases (building placement inside the settlement), **not a third placement class**.
|
||||
- **Cross-reference:** [D-207](#d-207-fully-generative-placement--markersjson-stripped-to-topographic-features) (corp-HQ cross-ref — the insert path is removed here), [D-223](#d-223) (names-only pool — the corp-HQ-stays + pop/class-deferred notes are superseded here), [D-211](#d-211) (placement — consumes the merged list; only position is seed-derived), [D-213](#d-213) (`FoundingOrientation` — a `RailHeadFacing` variant is the gated follow-on), [D-237](#d-237) (corp `specialization` vocabulary — the HQ-placement key, extended here from 27 to 31 values), [D-195](#d-195)/[D-197](#d-197) (the 10-value `economic_role` vocabulary `standalone_economic_role` maps onto), [D-196](#d-196) (`SettlementClass` — the bake target), [D-010](#d-010) (determinism — only position is seed-derived).
|
||||
- **Dissent:** None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user