feat(simulation): Layer-4 walkable quarter — geometry + tags + streets (#957) #153

Closed
jpmschweitzer wants to merge 0 commits from layer4-quarter-skeleton into main
Owner

Summary

Layer 4 — the walkable quarter: a quarter now generates complete building +
street geometry. Implements D-220/D-229/D-233/D-234 (and the D-213 water bearing),
absorbing #976 (footprint + street geometry) into #957 as one coherent
deliverable. All integer-deterministic (D-010).

What a generated quarter now contains

  • Buildingssubdivide_block_footprints: density-scaled BSP of each
    128×128-tile block into variable axis-aligned TileRect plots (D-229 fast-path),
    with the D-233 BulkClass roofed-coverage fraction deciding building-vs-open.
    Each footprint → a BuildingPropertyTag (zone-type, entry-class, era, floors,
    condition, flavor) in QuarterWorldState.block_tags, built in the plan phase.
  • Building tag derivation (D-229) — the authored (ZoningType × economic_role × setting) → ZoneTypeId selection table (D-229 amendment; planetary variants,
    setting tweaker, station ids excluded → Q-109), entry-class (D-217 U-curve +
    Grid/Organic fork), era (founding_age + prosperity + seed), FloorExtent (D-220),
    initial condition (D-217).
  • Streets (D-234) — typed AccessPoint/CorridorSpine/ChunkLayout (were
    String stubs): access nodes from road-entry octants + reservation gates;
    morphology-gated arterials (Ribbon / HubSpoke / Prim-MST mesh), ±45°-snapped;
    per-block local lattice modulated by D-096 Grid/Organic.
  • Terrain orientation — Layer-1 TerrainAnalysis::water_bearing (8-octant
    integer, no atan2) on each attractor → real coastal/river founding orientations
    (#956's 0 stub gone for water settlements).
  • Waterfront rule (D-234b) — the water-facing quarter edge drops its setback
    to 0 so buildings present flush to the quay.
  • Removed the write-only BlockSkeleton.era stub (era lives on the tag).

Decisions

D-229 amended (zone-selection table), D-234 amended (implementation note),
Q-109 filed (cascade generation-source dispatch — the planetary/station/mod/
save-only seam surfaced here).

Pending cross-layer dependency (flagged, not a stub)

The waterfront rule + #956's bearings are correct and tested, but their real
per-settlement input only reaches quarter generation once the Layer-3 placement
→ Layer-4 GenerateSkeleton dispatch
is wired — today city_context_reader
stubs founding_orientation: Cardinal and nothing dispatches quarter generation
from placements. That dispatch (with its trigger-timing design call, D-206) is the
next integration; the geometry here is complete and ready to consume it.

Test plan

  • cargo fmt --check clean; cargo clippy --all-targets -- -D warnings clean.
  • 1323 lib tests + all integration tests pass. ~35 new tests across the
    selection table, tag derivations, footprint packing, street network, water
    bearing, and the waterfront rule (incl. the no-station-ids invariant).
  • Golden (cascade_layer1.json) + atlas_response_ready.msgpack rebaked —
    additive (water_bearing field) / representational only; selection unchanged.

Closes #957. Subsumes #976 (footprint + street geometry); only #976's open-water
edge-adjacency refinement rides with the pending dispatch.

## Summary Layer 4 — the **walkable quarter**: a quarter now generates complete building + street geometry. Implements D-220/D-229/D-233/D-234 (and the D-213 water bearing), **absorbing #976** (footprint + street geometry) into #957 as one coherent deliverable. All integer-deterministic (D-010). ## What a generated quarter now contains - **Buildings** — `subdivide_block_footprints`: density-scaled BSP of each 128×128-tile block into variable axis-aligned `TileRect` plots (D-229 fast-path), with the D-233 BulkClass roofed-coverage fraction deciding building-vs-open. Each footprint → a `BuildingPropertyTag` (zone-type, entry-class, era, floors, condition, flavor) in `QuarterWorldState.block_tags`, built in the plan phase. - **Building tag derivation** (D-229) — the authored `(ZoningType × economic_role × setting) → ZoneTypeId` selection table (D-229 amendment; planetary variants, setting tweaker, station ids excluded → Q-109), entry-class (D-217 U-curve + Grid/Organic fork), era (founding_age + prosperity + seed), FloorExtent (D-220), initial condition (D-217). - **Streets** (D-234) — typed `AccessPoint`/`CorridorSpine`/`ChunkLayout` (were `String` stubs): access nodes from road-entry octants + reservation gates; morphology-gated arterials (Ribbon / HubSpoke / Prim-MST mesh), ±45°-snapped; per-block local lattice modulated by D-096 Grid/Organic. - **Terrain orientation** — Layer-1 `TerrainAnalysis::water_bearing` (8-octant integer, no `atan2`) on each attractor → real coastal/river founding orientations (#956's `0` stub gone for water settlements). - **Waterfront rule** (D-234b) — the water-facing quarter edge drops its setback to 0 so buildings present flush to the quay. - Removed the write-only `BlockSkeleton.era` stub (era lives on the tag). ## Decisions D-229 amended (zone-selection table), D-234 amended (implementation note), **Q-109 filed** (cascade generation-source dispatch — the planetary/station/mod/ save-only seam surfaced here). ## Pending cross-layer dependency (flagged, not a stub) The waterfront rule + #956's bearings are correct and tested, but their *real* per-settlement input only reaches quarter generation once the **Layer-3 placement → Layer-4 `GenerateSkeleton` dispatch** is wired — today `city_context_reader` stubs `founding_orientation: Cardinal` and nothing dispatches quarter generation from placements. That dispatch (with its trigger-timing design call, D-206) is the next integration; the geometry here is complete and ready to consume it. ## Test plan - `cargo fmt --check` clean; `cargo clippy --all-targets -- -D warnings` clean. - **1323 lib tests + all integration tests pass.** ~35 new tests across the selection table, tag derivations, footprint packing, street network, water bearing, and the waterfront rule (incl. the no-station-ids invariant). - Golden (`cascade_layer1.json`) + `atlas_response_ready.msgpack` rebaked — additive (`water_bearing` field) / representational only; selection unchanged. Closes #957. Subsumes #976 (footprint + street geometry); only #976's open-water edge-adjacency refinement rides with the pending dispatch.
jpmschweitzer added 7 commits 2026-06-06 10:42:22 +02:00
Surfaced during #957 zone-selection authoring: a body's setting/mode should
dispatch the cascade entry to planetary / station / mod-DLC-forked /
save-only(player_base) sources, distinct from setting's in-path tweaker role.
Captured as an open question so #957 stays scoped to the planetary path; this is
why #957's zone table excludes station-only zone ids.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The pure per-footprint tag derivations for Layer-4 quarter generation (D-229),
ahead of the footprint-subdivision + assembly step:
- zone_type_for: the authored (ZoningType × economic_role × setting) → ZoneTypeId
  selection (D-229 amendment) — planetary variants only, setting as a tweaker,
  station-only ids never selected (Q-109); deterministic seed-pick, no empty slice.
- building_entry_class: zone × layout_mode × prosperity, with the D-217 Broken-band
  U-curve degrade (Commercial→BreachOnly) and the Grid/Organic credential fork.
- construction_era: founding_age + prosperity + seed (D-229); old+Broken → Derelict.
- floor_extent: D-220 density-class floor ranges from density_pct + seed-jitter.
- initial_condition: D-217 frozen-amber condition from prosperity + era cause.

All integer-deterministic (D-010). 9 unit tests incl. the no-station-ids invariant.
Footprint subdivision + assign_building_tags assembly + block_tags wiring follow
(needs the footprint-packing model decided).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Record the (ZoningType × economic_role × setting) → ZoneTypeId selection table
for the planetary cascade: base candidate slices per ZoningType refined by
economic_role, a setting tweaker (Maritime/Agricultural/Wilderness — planetary
variants only), and the gap-fill rules (entry-class U-curve at the D-217 Broken
band <2300bps; era per founding_age+prosperity+seed; FloorExtent from D-220
density midpoints; doors deferred to #979). Station-only zone ids are excluded —
they belong to a separate cascade (Q-109).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Build the per-quarter building geometry + tags (absorbs #976's footprint
subdivision into #957 — one coherent walkable-quarter deliverable):

- subdivide_block_footprints: deterministic BSP of each 128x128-tile block into
  variable axis-aligned building plots (D-229 fast-path). Lot size + setback
  scale with D-220 density (Frontier -> few big lots/wide gaps; Compressed ->
  many small lots/shared walls); the D-233 BulkClass roofed-coverage fraction
  decides building-vs-interstitial. morphology is wired in for the D-234
  waterfront/street layer (needs Layer-1 terrain water-adjacency threaded up).
- assign_all_block_tags: subdivides + tags every non-reserved block's footprints
  via the #957 derivation helpers, populating QuarterWorldState.block_tags in the
  GenerateSkeleton plan-phase (D-230). Reserved blocks get no standard fill.
- Remove the write-only BlockSkeleton.era String stub - construction era now
  lives per-footprint on BuildingPropertyTag.era (typed ConstructionEra).

All integer-deterministic (D-010). 5 new tests. Doors stay Vec::new() (#979).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Complete the Layer-4 quarter geometry with the street layer (D-234), replacing
the ChunkLayout/CorridorSpine/AccessPoint String stubs with real types:
- AccessPoint{position,kind} + AccessKind (QuarterEdge/ReservationGate/
  BlockJunction): the quarter's road nodes (from road_entry_directions octants +
  reservation gates), the node set the D-097 audit reads.
- CorridorSpine{from,to,path}: arterial trunk edges, ±45°-snapped polylines
  (D-096 cap). Topology gated by morphology (D-234a): Ribbon (fjord/canyon/
  mountain-pass), HubSpoke (delta/island/enclosed water), Mesh/Prim-MST
  (plains/meander/coastal). A spanning tree over the access nodes.
- ChunkLayout{spacing,offset,rotation_steps}: per-block local ±45° lattice,
  spacing from density, offset/rotation modulated by the D-096 Grid/Organic mode.

Wired into generate_quarter_skeleton (access_points + corridors + per-block
chunk_layout). All integer-deterministic (D-010). 7 new tests.

Remaining D-234 piece: the per-edge waterfront pier/quay rule needs Layer-1
terrain water-adjacency threaded to the skeleton (cross-layer plumbing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Close the last D-234 piece — terrain water-direction extraction wired through
to founding orientation and the quarter waterfront rule:

- Layer 1: TerrainAnalysis::water_bearing — 8-octant integer bearing toward the
  nearest water from the water_dist gradient (D-010, no atan2). Stored on
  GeographicAttractor.water_bearing (360 = none).
- #956 founding orientation: coastal/river settlements now get a real
  water-facing bearing (the anchoring attractor's), replacing the 0 stub.
- #957 waterfront rule (D-234b): the water-facing quarter edge (from the
  settlement's Coastal founding orientation) drops its block setback to 0 so
  buildings present flush to the quay (dock-orthogonal). Typed Edge + coastal_edge
  + per-block gating.

Golden + atlas_response fixture rebaked (additive water_bearing field only).
8 new tests. All integer-deterministic (D-010).

Pending: the waterfront rule reads context.founding_orientation, which
city_context_reader still stubs to Cardinal — real per-settlement orientation
reaches quarter generation once the Layer-3 placement -> Layer-4 GenerateSkeleton
dispatch is wired (the remaining cross-layer integration).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Note the #957 implementation on D-234: typed AccessPoint/CorridorSpine/
ChunkLayout, morphology-gated arterials + per-block lattice, BSP footprint
subdivision with D-233 coverage, and the waterfront rule. Flag the remaining
cross-layer dependency: the waterfront rule's real input (per-settlement Coastal
orientation) needs the Layer-3 placement -> Layer-4 GenerateSkeleton dispatch,
since city_context_reader still stubs founding_orientation to Cardinal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jpmschweitzer added 1 commit 2026-06-06 10:55:11 +02:00
PR #153 review (Hoshe H1): zone_type/era/floors/flavor all shared one fp_chain
and each took splitmix64(seed) of the same value, correlating the fields (era
tracking floor height across a city). Derive a distinct sub-chain per field so
they draw independent entropy. Block_tags aren't serialized, so no fixture
change; behavior stays deterministic.

Also (review): n=1 corridor test (empty, no panic), Frontier-density waterfront
test, and a comment on the reserved `_morphology` param (Tyre).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
Owner

Review: layer4-quarter-skeleton → main (type: code)

Reviewers: Hoshe (QA) + Tyre (architecture). Gate: clippy --all-targets -D warnings clean; 1325 lib + integration tests pass.

Hoshe (QA / correctness): APPROVE (after fix)

Determinism, BSP bounds (len - min_lot can't underflow — guarded by min_lot*2),
street net (prim_mst/snap45_path/coastal_edge boundaries), water-bearing
gradient + 360 sentinel, and the BlockSkeleton.era removal all verified clean.
One behavioral issue + two test gaps, now fixed:

# File Issue Status
H1 skeleton_gen.rs assign_block_tags zone/era/floors/flavor shared one fp_chain → each splitmix64(seed) of the same value → correlated fields (era tracking floor height citywide) Fixed f880635b2 — distinct per-field sub-chains
H2 skeleton_gen.rs tests no n=1 corridor test (empty/no-panic path) Fixed — added
H3 skeleton_gen.rs tests waterfront test only at density 70 Fixed — added Frontier-density test

Tyre (architecture): APPROVE

Scope collapse (#976#957) is the right call — street geometry + footprint
subdivision aren't separable at the Quarter level; the module is large but
coherent (one transform: context → walkable quarter). D-234 fidelity confirmed
(morphology→topology gating, arterial-MST/local-lattice split, Voronoi rejected,
waterfront rule). The flagged dispatch dependency is documented honestly across
D-234 note + PR + Q-109; implementing the waterfront rule now (correct, dormant,
safe Cardinal fallback) is the right call. Typed structs well-shaped; String→struct
change is generation-internal (not on the IPC wire). D-229/D-234 inline amendments

  • Q-109 are correct decision hygiene. One non-blocking polish: comment the
    reserved _morphology param — done in the fix commit.

Verdict: APPROVED

(Hoshe's H1 fix + both test gaps + Tyre's polish landed in f880635b2.)

## Review: layer4-quarter-skeleton → main (type: code) Reviewers: Hoshe (QA) + Tyre (architecture). Gate: clippy `--all-targets -D warnings` clean; 1325 lib + integration tests pass. ### Hoshe (QA / correctness): APPROVE (after fix) Determinism, BSP bounds (`len - min_lot` can't underflow — guarded by `min_lot*2`), street net (`prim_mst`/`snap45_path`/`coastal_edge` boundaries), water-bearing gradient + 360 sentinel, and the `BlockSkeleton.era` removal all verified clean. One behavioral issue + two test gaps, now fixed: | # | File | Issue | Status | |---|------|-------|--------| | H1 | `skeleton_gen.rs` `assign_block_tags` | zone/era/floors/flavor shared one `fp_chain` → each `splitmix64(seed)` of the same value → correlated fields (era tracking floor height citywide) | **Fixed** `f880635b2` — distinct per-field sub-chains | | H2 | `skeleton_gen.rs` tests | no n=1 corridor test (empty/no-panic path) | **Fixed** — added | | H3 | `skeleton_gen.rs` tests | waterfront test only at density 70 | **Fixed** — added Frontier-density test | ### Tyre (architecture): APPROVE Scope collapse (#976 → #957) is the right call — street geometry + footprint subdivision aren't separable at the Quarter level; the module is large but *coherent* (one transform: context → walkable quarter). D-234 fidelity confirmed (morphology→topology gating, arterial-MST/local-lattice split, Voronoi rejected, waterfront rule). The flagged dispatch dependency is documented honestly across D-234 note + PR + Q-109; implementing the waterfront rule now (correct, dormant, safe Cardinal fallback) is the right call. Typed structs well-shaped; String→struct change is generation-internal (not on the IPC wire). D-229/D-234 inline amendments + Q-109 are correct decision hygiene. One non-blocking polish: comment the reserved `_morphology` param — **done** in the fix commit. ### Verdict: APPROVED (Hoshe's H1 fix + both test gaps + Tyre's polish landed in `f880635b2`.)
jpmschweitzer closed this pull request 2026-06-06 11:01:10 +02:00

Pull request closed

This pull request cannot be reopened because the branch was deleted.
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: jpmschweitzer/settled-reach#153