docs(governance): D-243 spatial scale ladder — resolve Q-110, Region→Province
Resolves Q-110 (region/chunk physical-scale anchoring), which had three
contradictory metres-per-scale assumptions in the cascade and no record
fixing metres-per-heightmap-pixel.
D-243 establishes a nested absolute-metre containment ladder with a single
elastic seam at planetary scale:
voxel 1m -> chunk 64m -> block 128m -> quarter 512m -> district 2km
-> region ~205km (100x100 districts) -> (elastic) -> planet
A body holds round(2*pi*R/204.8km) regions (the only per-body-floating
quantity); everything below is fixed integer math, so ChunkPos->RegionPos
is body-independent. The region is the largest hard block: the climate/
weather/season lockdown scale (the Q-105 cheap-dynamism source) and the
sane planetary grid. Sub-heightmap detail is invented deterministically
(interpolation + domain warp + detail-scatter, never stored, D-227); climate
is edge-fuzzed so the grid never shows ('climate does not change on a line').
Vocabulary locked and recorded in CLAUDE.md: 'region' = the 205km metric
cell only; the old 1km RegionProfile is dropped onto the 2km district; D-201
tier-4 'Region' renamed Province (the watershed/political overlay), amended
here. Refines D-239 §2 (district temperature now modulates a region baseline).
Implementation tickets: T-1077 (re-scale the code to the ladder + elastic-seam
resampling; blocks T-1046) and T-1078 (region climate stack + edge fuzz).
T-1046 is un-gated on Q-110 and now blocked only by T-1077.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -490,3 +490,4 @@ INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updat
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYMFY5VFPXGANNRSXW', '06FB0TNSRW8ADSD8ZK32K7SWSC', '2026-06-07 16:35:38', '2026-06-07 16:35:38', NULL, 'adecdfdf040b687fd266c8bbf5a98616', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FCDG73NW7SNCA5Y8JN905QMR', '06FCDG73V9R6N024QSFDEJ4R68', '2026-06-14 15:29:50', '2026-06-14 15:29:50', NULL, '389b010a55f227808e2b2e451acf8d36', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FCDG73S4ET8KKTK62Z5KFEQM', '06FCDG73V9R6N024QSFDEJ4R68', '2026-06-14 15:29:50', '2026-06-14 15:29:50', NULL, '7e362812933c59ffe9f76ab069a83dc1', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FCF56EAJ55PHQ977SV866CFM', '06FBPTXP19FFZEVED140S1708W', '2026-06-14 19:21:18', '2026-06-14 19:21:18', NULL, 'b83c6b1a0bc70e67a08b417328944eb8', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
|
||||
@@ -1915,3 +1915,9 @@ DEFERRED (-> T-1076, gated on the D-242 settlement model T-1074/T-1075):
|
||||
- step 6 RailHeadFacing second pass — the adopted FoundingOrientation enum (D-213) has no RailHeadFacing; needs a D-213 amendment + Layer-4 skeleton_gen handling. Junction data is already exposed for it.
|
||||
- hub-selection refinement (scaled-cap hubs, co-location collapse, hybrid minor-settlement attach) — needs baked population/specialization (D-242).
|
||||
Decision D-242 filed (corp-HQ settlement model) — resolves why corp HQs were polluting the city pool as duplicate settlements.', NULL, '2026-06-14 15:30:58', '2026-06-14 15:30:58', '2026-06-14 15:30:58', NULL, 'adca3fc7b58c20d9805752a9082817a4', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPTXP19FFZEVED140S1708W', 'description', '(description follows in first append)', '(description follows in first append)
|
||||
|
||||
---
|
||||
Q-110 resolved (2026-06-14) -> D-243. Scope item (1) ChunkPos->RegionPos is now defined: the D-243 absolute ladder is voxel(1m)->chunk(64m)->block(128m)->quarter(512m)->district(2km)->region(~205km), with a single elastic seam region<->planet (round(2*pi*R/204.8km) regions per body from body_radius_km). ChunkPos->district->region is fixed integer math; only region->heightmap-sample is body-specific.
|
||||
|
||||
Prerequisite: T-1077 re-scales the code to this ladder (the current ~1km RegionProfile moves onto the 2km district; the region becomes the new 205km top hard block) — T-1046 now blocked by T-1077. Climate/weather lockdown + edge fuzz on the region is T-1078. Once T-1077 lands, T-1046 is the production wiring (proxy layers + Atlas surfacing) over a ladder whose scale is finally coherent.', NULL, '2026-06-14 19:22:14', '2026-06-14 19:22:14', '2026-06-14 19:22:14', NULL, '386fdc3725a511127d2254a36dd90c62', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
|
||||
@@ -84,3 +84,5 @@ INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FCDG73NW7SNCA5Y8JN905QMR', 'T-1074', '2026-06-14 15:29:50', '2026-06-14 15:29:50', NULL, '2bf2aa1e4d416127b28f923b273bfd67', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FCDG73S4ET8KKTK62Z5KFEQM', 'T-1075', '2026-06-14 15:29:50', '2026-06-14 15:29:50', NULL, '5227303769a48815b53724fb47d42b39', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FCDG73V9R6N024QSFDEJ4R68', 'T-1076', '2026-06-14 15:29:50', '2026-06-14 15:29:50', NULL, '8df24639ed7dc3c6a29bc2019da02f74', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FCF56EAJ55PHQ977SV866CFM', 'T-1077', '2026-06-14 19:21:18', '2026-06-14 19:21:18', NULL, '957e1846040da6c5fc25d19db17c20c3', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FCF56ED5ZJZRXFMF3PNSNZF0', 'T-1078', '2026-06-14 19:21:18', '2026-06-14 19:21:18', NULL, 'f1eda12cb9d42f52b353b30dcca81db6', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
|
||||
@@ -2219,3 +2219,25 @@ DEFERRED (-> T-1076, gated on the D-242 settlement model T-1074/T-1075):
|
||||
- step 6 RailHeadFacing second pass — the adopted FoundingOrientation enum (D-213) has no RailHeadFacing; needs a D-213 amendment + Layer-4 skeleton_gen handling. Junction data is already exposed for it.
|
||||
- hub-selection refinement (scaled-cap hubs, co-location collapse, hybrid minor-settlement attach) — needs baked population/specialization (D-242).
|
||||
Decision D-242 filed (corp-HQ settlement model) — resolves why corp HQs were polluting the city pool as duplicate settlements.', 'in_progress', 'high', NULL, 'server', 'D-211', '2026-06-12 10:23:49', '2026-06-14 15:30:58', NULL, '55c0e158089122497cf34e9574595d50', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FCF56EAJ55PHQ977SV866CFM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Re-scale the cascade to the D-243 ladder + elastic-seam resampling (region 205km / district 2km)', 'Per D-243 (resolves Q-110). Make the code match the canonical ladder.
|
||||
|
||||
1. Re-scale the terrain/climate carrier: the current ~1km RegionProfile (region_profile.rs, derive_all_regions 8-cells/region) moves to the DISTRICT (2km), aligning with D-239 §2''s climate district. Rename RegionProfile -> DistrictProfile (or equivalent); fix the contradictory scale assumptions Q-110 flagged (region_profile.rs:3-4 ~1km doc, cascade.rs:235-237 ''8 cells/region on 128x64'', chunk_context.rs:137-141 ''region=16 chunks'').
|
||||
2. Add the REGION tier = 100x100 districts = 204.8km (~205km) = the top hard block.
|
||||
3. Elastic seam region<->planet: round(2*pi*R/204.8km) regions per body from body_radius_km (D-204). ChunkPos -> district -> region is fixed integer math; region->heightmap-sample is the only body-specific function.
|
||||
4. Detail-scatter synthesis (D-243 §2): interpolate the coarse heightmap (~40-78km/px) + compose a simple multi-octave seed+position-keyed detail field (shaped by morphology/slope, continuous) atop the D-239 §4 domain warp — invented, not stored (D-227), plausible-at-a-glance.
|
||||
|
||||
Blocks T-1046 (the tier can''t wire to production until its scale matches the ladder).', 'backlog', 'high', NULL, 'server', 'D-243', '2026-06-14 19:21:18', '2026-06-14 19:21:18', NULL, '07e0b2847d5908017dbb53e5e34b16d8', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FCF56ED5ZJZRXFMF3PNSNZF0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Region climate stack — weather/season/temperature baseline + district modulation + edge fuzz', 'Per D-243 §3/§4 (refines D-239 §2; contributes the region-clock toward Q-105).
|
||||
|
||||
Three-level climate stack:
|
||||
- REGION (~205km): the climate context — latitude-driven temperature baseline, weather state, seasonal clock. Resolved ONCE per region (the Q-105 cheap-dynamism source); inherited by every district/tile inside. ''Region temperature'' = context, not a uniform slab.
|
||||
- DISTRICT (2km): local temperature = region baseline + elevation lapse + slope aspect (D-239 §2''s district temperature, now a MODULATION of the region rather than derived from scratch).
|
||||
- CHUNK/VOXEL: D-239 §3 freeze/snow scatter on the local temperature.
|
||||
|
||||
EDGE FUZZ (D-243 §4): climate does not change on a line. A tile''s climate is a continuous warp-perturbed blend of surrounding regions'' baselines (bilinear across region centres + noise displacement so the boundary is ragged); same at district edges. The region/district grid must be invisible in the output. This is the climate counterpart to D-239 §4''s domain warp — climate is feathered/blended (continuous field), unlike morphology which stays sharp-but-organic (D-239 §7).', 'backlog', 'medium', NULL, 'server', 'D-243', '2026-06-14 19:21:18', '2026-06-14 19:21:18', NULL, '5ea7ddbb4540f2e443e2b9e08d0ecc73', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPTXP19FFZEVED140S1708W', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire the Region→Chunk→Voxel tier into production — ChunkPos→RegionPos mapping, proxy layers, Atlas surfacing', '(description follows in first append)
|
||||
|
||||
---
|
||||
Q-110 resolved (2026-06-14) -> D-243. Scope item (1) ChunkPos->RegionPos is now defined: the D-243 absolute ladder is voxel(1m)->chunk(64m)->block(128m)->quarter(512m)->district(2km)->region(~205km), with a single elastic seam region<->planet (round(2*pi*R/204.8km) regions per body from body_radius_km). ChunkPos->district->region is fixed integer math; only region->heightmap-sample is body-specific.
|
||||
|
||||
Prerequisite: T-1077 re-scales the code to this ladder (the current ~1km RegionProfile moves onto the 2km district; the region becomes the new 205km top hard block) — T-1046 now blocked by T-1077. Climate/weather lockdown + edge fuzz on the region is T-1078. Once T-1077 lands, T-1046 is the production wiring (proxy layers + Atlas surfacing) over a ladder whose scale is finally coherent.', 'backlog', 'high', NULL, 'server', 'D-239', '2026-06-12 10:40:58', '2026-06-14 19:22:14', NULL, 'c3379b82cb706bdf11174688e5d203e6', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||
|
||||
@@ -49,6 +49,25 @@ Development follows a strict cascade. Each phase has a concrete deliverable. **D
|
||||
|
||||
**Generation before player (D-166, amended 2026-05-22 and 2026-06-12).** The world is built **outside-in**: geo layer → economic layer drawn onto the world (cities, buildings, streets, blocks, inter-settlement roads/rail) → building templates → **door boundary contracts** to building insides → only then the background NPC layer + interiors (Phase 6) → only then gameplay/scenarios ("making it a game again", parked in epic T-1036). Player control and visual rendering (Phase 5) ramp **in parallel once outside generation produces walkable exteriors** (gate held by T-962) — not before. There is **no hand-made test map** — test layers are produced by the generator itself. Progress is viewed through the implant **Atlas** (per-layer maps), which already exists (Phase 3). The current in-world rendering code is **left as-is until Phase 5** — do not build on it or rip it out before then. Guiding statement: *before this is a game, it is a Reach a character can travel through.* Phase-independent maintenance/test debt lives under initiative T-1037, not a phase epic.
|
||||
|
||||
## Spatial scale ladder (D-243)
|
||||
|
||||
The generation cascade is a **nested, absolute-metre containment ladder** with **exactly one elastic seam** — the jump to planetary scale. Every level below the seam is a fixed integer multiple of the one below, identical on every body; only the planetary count floats per body. Full detail + rationale: **[D-243](governance/decisions/architecture.md#d-243)**.
|
||||
|
||||
| Level | Side | = | Role |
|
||||
|-------|------|---|------|
|
||||
| voxel | 1 m | — | the tile |
|
||||
| chunk | 64 m | 64 voxels | stream / derive unit |
|
||||
| block | 128 m | 2 chunks | generator planning unit |
|
||||
| quarter | 512 m | 4 blocks | settlement footprint cell |
|
||||
| district | 2,048 m | 4 quarters | urban division **and** local-climate cell |
|
||||
| **region** | **~205 km** | 100 districts | **top hard block** — climate/weather/season lockdown + planetary grid |
|
||||
| *— elastic seam —* | | | |
|
||||
| planet | per body | `round(2πR / 204.8 km)` regions | the **only** per-body-floating quantity |
|
||||
|
||||
- **Region** is the largest *hard* block: the unit where **weather, season, and the temperature baseline are resolved** (the Q-105 cheap-dynamism source — computed once, inherited by every district/tile inside). It is also the sane planetary grid (~195×98 regions on an Earth-sized body).
|
||||
- **Climate is edge-fuzzed** — the region/district is where climate is *computed*, never where it *steps*. Sampled values are a warp-perturbed blend across neighbours, so the grid is invisible (*climate does not change on a line*). Terrain detail finer than the heightmap (~40–78 km/px) is **invented deterministically** (interpolation + domain warp + detail-scatter, D-227), never stored.
|
||||
- **Vocabulary (locked):** "region" = the ~205 km metric cell **only**. The old 1 km "RegionProfile" is dropped — its terrain/climate carrier role lives on the **district**. **Province** (D-201 tier-4, 50–500 km watershed/political) is an irregular lore-bearing **overlay** painted across regions — *not* a containment rung; keep it distinct from "region". `sector` is reserved (`perception::VisibilitySector`); do not reuse it for scale.
|
||||
|
||||
## Work Modes
|
||||
|
||||
### Kanban mode (default)
|
||||
|
||||
@@ -308,6 +308,7 @@ line in place — keep the Q-record for the audit trail rather than deleting it.
|
||||
- [D-240: Registry orbit/star data is non-canonical — climate derives from a `planet_class` temperature envelope](decisions/architecture.md#d-240-registry-orbitstar-data-is-non-canonical--climate-derives-from-a-planet-class-temperature-envelope) — _architecture_
|
||||
- [D-241: Spike binary artifacts are local-only — gitignored, never tracked](decisions/process.md#d-241-spike-binary-artifacts-are-local-only--gitignored-never-tracked) — _process_
|
||||
- [D-242: Corp HQ settlement model — HQs are not cities; separate from the name pool, specialization-keyed, install-baked](decisions/architecture.md#d-242-corp-hq-settlement-model--hqs-are-not-cities-separate-from-the-name-pool-specialization-keyed-install-baked) — _architecture_
|
||||
- [D-243: Spatial scale ladder — nested absolute containment with a single elastic planetary seam (resolves Q-110)](decisions/architecture.md#d-243-spatial-scale-ladder--nested-absolute-containment-with-a-single-elastic-planetary-seam-resolves-q-110) — _architecture_
|
||||
|
||||
## Open questions
|
||||
|
||||
@@ -382,7 +383,6 @@ line in place — keep the Q-record for the audit trail rather than deleting it.
|
||||
- [Q-107: Wiki → Atlas content-set consolidation](questions/architecture.md#q-107-wiki--atlas-content-set-consolidation) — _architecture_
|
||||
- [Q-108: Subterranean / domed / sealed-habitat settlement morphology — does the built-world fill model need a surface-vs-enclosed branch](questions/architecture.md#q-108-subterranean--domed--sealed-habitat-settlement-morphology--does-the-built-world-fill-model-need-a-surface-vs-enclosed-branch) — _architecture_
|
||||
- [Q-109: Cascade generation-source dispatch — planetary / station / mod-DLC-forked / save-only](questions/architecture.md#q-109-cascade-generation-source-dispatch--planetary--station--mod-dlc-forked--save-only) — _architecture_
|
||||
- [Q-110: Region/chunk physical-scale anchoring — what is a region in metres, and what anchors a heightmap pixel?](questions/architecture.md#q-110-regionchunk-physical-scale-anchoring--what-is-a-region-in-metres-and-what-anchors-a-heightmap-pixel) — _architecture_
|
||||
- [Q-111: Live-econ coupling of generation inputs — which inputs refresh from the rolling sim vs stay static-authored?](questions/architecture.md#q-111-live-econ-coupling-of-generation-inputs--which-inputs-refresh-from-the-rolling-sim-vs-stay-static-authored) — _architecture_
|
||||
- [Q-112: Storyteller engagement scoring — move to bps integer arithmetic or sanction f32?](questions/architecture.md#q-112-storyteller-engagement-scoring--move-to-bps-integer-arithmetic-or-sanction-f32) — _architecture_
|
||||
- [Q-113: Is cross-platform f32 bit-identity required for saves? (golden vectors are x86_64-only)](questions/architecture.md#q-113-is-cross-platform-f32-bit-identity-required-for-saves-golden-vectors-are-x86-64-only) — _architecture_
|
||||
@@ -435,6 +435,7 @@ line in place — keep the Q-record for the audit trail rather than deleting it.
|
||||
- [Q-101: Refinement / derivation contract — coarse hint → fine geometry + morphology algorithm family](questions/architecture.md#q-101-refinement--derivation-contract--coarse-hint--fine-geometry--morphology-algorithm-family) — _architecture_
|
||||
- [Q-104: Floor-index ↔ absolute voxel-z coordinate mapping](questions/architecture.md#q-104-floor-index--absolute-voxel-z-coordinate-mapping) — _architecture_
|
||||
- [Q-106: Era-band stacking depth for layered architecture-flavor](questions/architecture.md#q-106-era-band-stacking-depth-for-layered-architecture-flavor) — _architecture_
|
||||
- [Q-110: Region/chunk physical-scale anchoring — what is a region in metres, and what anchors a heightmap pixel?](questions/architecture.md#q-110-regionchunk-physical-scale-anchoring--what-is-a-region-in-metres-and-what-anchors-a-heightmap-pixel) — _architecture_
|
||||
|
||||
## Rejected
|
||||
|
||||
|
||||
@@ -894,7 +894,7 @@ Technical foundation decisions that constrain implementation: engine, client-ser
|
||||
| 1 | Galaxy | 300 systems | Galaxy graph, gate topology, cultural corridors |
|
||||
| 2 | System | — | Orbital mechanics, body catalog |
|
||||
| 3 | Body | ~512×256 pixels (equirectangular heightmap) | Planetary topography, climate zones |
|
||||
| 4 | Region | ~50–500km | Province boundaries (watershed-derived, D-205), biome zones |
|
||||
| 4 | Province | ~50–500km | Province boundaries (watershed-derived, D-205), biome zones |
|
||||
| 5 | Settlement | ~1–30km radius | City footprint, district layout |
|
||||
| 6 | District | 2048×2048 tiles (2048m, 4.19 km²) | 4×4 quarters — large urban division (D-222) |
|
||||
| 7 | Quarter | 512×512 tiles (512m, 0.262 km²) | 4×4 blocks — settlement footprint cell, Phase 1 skeleton grid (D-222) |
|
||||
@@ -904,6 +904,7 @@ Technical foundation decisions that constrain implementation: engine, client-ser
|
||||
- Tiers 6–9 (District → Chunk) are the sub-settlement spatial hierarchy, canonical in [D-222](#d-222) — renamed/resized from the original D-094 ladder (the old 512m "District" is now the Quarter; District is now 2048m), at the Tile = 1m / Subtile = 0.5m scale of D-220. This decision formalizes Tiers 1–5 with equivalent lock status.
|
||||
- Tier 3 heightmap resolution (512×256 equirectangular working grid; 1024×512 PNG) is the canonical format. Deviation requires amending D-191. **Amended (T-963, D-202):** the canonical *stored* heightmap is now a per-body 16-bit grayscale `heightmap.png` at **1024×512** carrying native elevation (the prior PNG was a 1024×512 RGB *relief*, now renamed `reliefmap.png`). PNG dimensions are unchanged (1024×512); Layer 1 downsamples to the 512×256 working grid. This amendment is the explicit deviation gate being satisfied — format/content changed, resolution preserved.
|
||||
- Tier 4 province boundaries are pre-computed at build-time and stored in `atlas_province_boundaries` (D-205). They are not re-computed at runtime.
|
||||
- **Amended 2026-06-14 ([D-243](#d-243)):** Tier 4 renamed **Region → Province** — it always *was* the watershed/political tier (D-205), and the name "Region" is now reserved for [D-243](#d-243)'s ~205 km metric containment cell (the top hard block of the absolute scale ladder + the climate/weather lockdown scale). Province is an **overlay** painted across regions, not a containment rung; the two sit at overlapping scales but are different kinds (irregular lore-bearing boundary vs fixed metric grid cell). [D-243](#d-243) owns the metric containment ladder (voxel→chunk→block→quarter→district→region); Tiers 1–5 here (Galaxy/System/Body/Province/Settlement) are organizational scopes/overlays, not metric rungs, and the ~1 km RegionProfile scale this record's tier-4 collided with is dropped (its carrier role moves onto the district, D-243 §5).
|
||||
- The `SettingType` enum on `DistrictSkeleton` is the interface between Tier 5 (settlement planning) and the skeleton cell (the 512m Quarter, Tier 7 — `DistrictSkeleton` is pending rename to match D-222).
|
||||
- **Rationale:** Locking spatial dimensions prevents the generative layers from drifting in incompatible directions. The heightmap pipeline, atlas pipeline, and district generator all assume these dimensions and would need coordinated migration if they changed. Formalization prevents silent per-system variation.
|
||||
- **Ticket:** T-912 (WorldTier enum), T-913 (SettlementClass)
|
||||
@@ -1805,4 +1806,40 @@ Technical foundation decisions that constrain implementation: engine, client-ser
|
||||
|
||||
---
|
||||
|
||||
*99 decisions (D-001 through D-242, excluding gaps). Last updated: 2026-06-14 (corp-HQ settlement model — D-242: HQs leave the city name pool, HQ placement is a specialization-keyed CityTenant/Standalone preference, population/settlement_class become install-baked with only position seed-derived; supersedes the D-223 corp-HQ-cross-ref and deferred-pop/class notes).*
|
||||
### D-243: Spatial scale ladder — nested absolute containment with a single elastic planetary seam (resolves Q-110)
|
||||
- **Date:** 2026-06-14
|
||||
- **Resolves:** [Q-110](../questions/architecture.md#q-110).
|
||||
- **Decision:** The world's spatial structure is a **fully nested, absolute-metre containment ladder** with **exactly one elastic seam** — the jump to planetary scale. Below the seam every level is a fixed integer multiple of the level below, in real metres, identical on every body; only the planetary seam floats per body. This resolves Q-110's three-way contradiction (region documented as ~1 km but implemented as 8 heightmap cells ≈ 624 km, with `chunk_context` assuming a third value) by fixing one ladder and one anchoring rule.
|
||||
|
||||
**The ladder** (side length; the 2-D child count is the square of the linear ratio):
|
||||
|
||||
| Level | Side | Linear ratio | Role |
|
||||
|---|---|---|---|
|
||||
| voxel | 1 m | — | the tile ([D-228](#d-228)/[D-220](#d-220)) |
|
||||
| chunk | 64 m | ×64 voxels | stream / derive unit ([D-222](#d-222), [D-239](#d-239)) |
|
||||
| block | 128 m | ×2 chunks | generator planning unit ([D-222](#d-222)) |
|
||||
| quarter | 512 m | ×4 blocks | settlement footprint cell ([D-222](#d-222)) |
|
||||
| district | 2,048 m | ×4 quarters | urban division **and** local-climate cell ([D-222](#d-222), [D-239](#d-239) §2) |
|
||||
| **region** | **204.8 km (~205 km)** | **×100 districts** | **top hard block** — climate/weather/season lockdown + planetary grid |
|
||||
| *(elastic seam)* | | | |
|
||||
| planet | per body | `round(2πR / 204.8 km)` regions | the **only** floating quantity |
|
||||
|
||||
**(1) The elastic seam — region ↔ planet.** A body holds `round(2πR / 204.8 km)` regions around the equator (and half that pole-to-pole), `R` = `body_radius_km` ([D-204](#d-204)) — the single per-body quantity. The fixed 1024×512 heightmap ([D-202](#d-202)) is the coarse elevation field, **finer** than the region grid (≈ 5 heightmap pixels per region on an Earth-sized body) and consumed per **district**, not per region. Everything below the region is fixed integer math; `ChunkPos → … → RegionPos` is body-independent.
|
||||
|
||||
**(2) Detail-scatter synthesis — the heightmap is the data ceiling.** The heightmap carries continental shape only (one elevation sample per ~40–78 km). All sub-heightmap detail (region → voxel) is **invented deterministically, not stored** ([D-227](#d-227)): the heightmap is interpolated and a **simple, spatially-coherent detail-scatter field** (a few octaves of seed+position-keyed value/fractal noise, shaped by local morphology + slope, continuous — never per-tile dice) is composed on top, alongside the [D-239](#d-239) §4 domain warp. It is calibrated to **read as plausible terrain at a glance** — visible, but never measured against a ground truth that does not exist (the player never traverses planetary scale continuously, which is *why* the top jump can be elastic). The containment ladder is for **addressing/streaming**, not for holding terrain features: large-wavelength landforms come from the low-frequency octaves of this field keyed to absolute coordinates, **not** from a container that size — which is why the ladder stops at the region and needs nothing coarser.
|
||||
|
||||
**(3) Region is the climate/weather lockdown scale** (refines [D-239](#d-239) §2; feeds [Q-105](../questions/architecture.md#q-105)). Weather, the seasonal clock, and the temperature **baseline** are region-scale phenomena (~hundreds of km), resolved **once per region** — the cheap shared-dynamism source Q-105 sought; every district and tile inside inherits it. Climate is a **three-level stack**: **region** = the climate *context* (latitude-driven temperature baseline, weather state, season); **district** (2 km) = local temperature = that baseline **+** elevation lapse + slope aspect (this is [D-239](#d-239) §2's 2 km climate district, now derived as a *modulation* of the region rather than from scratch); **chunk/voxel** = the [D-239](#d-239) §3 freeze/snow scatter on the local temperature. "Region temperature" is the *context*, not a uniform 205 km slab — intra-region latitude (~1.8°) and elevation land in the district/tile modulation. "Locked down" = *defined at this scale*, not frozen in time (the weather/season state still ticks; the region is the unit it ticks at).
|
||||
|
||||
**(4) Edge fuzz — climate does not change on a line.** The region (and district) is where climate is *computed*, **not** where it *steps*. A tile's climate value is a **continuous, warp-perturbed blend of the surrounding regions' baselines** (bilinear across region centres, displaced by a noise field so the blend boundary is ragged, not a straight gradient); the same applies at district edges for the local modulation. The ~205 km and 2 km grids are therefore **invisible in the output** — temperature, weather, and season grade smoothly and raggedly across boundaries the way a real frontal gradient does. This is the **climate counterpart to [D-239](#d-239) §4's domain warp** (same meta-rule: the addressing grid must never be visible in the result), with one distinction from [D-239](#d-239) §7: **morphology** seams stay *sharp-but-organic* (hard gates, warp-displaced — real geology is sharp), whereas **climate** is a continuous scalar field and is *feathered/blended*, not gated.
|
||||
|
||||
**(5) Vocabulary** — fixed, and recorded in CLAUDE.md. "Region" now means **only** the ~205 km top hard block. The old 1 km "RegionProfile" scale is **removed** — its terrain/climate carrier role moves onto the **district** (2 km), aligning it with [D-239](#d-239) §2's existing climate district. [D-201](#d-201)'s tier-4 "Region" (50–500 km watershed/political) is renamed **Province** (its actual content — drainage basins, [D-205](#d-205), territory), an **overlay** painted across regions, *not* a containment rung. Region (a fixed metric grid cell) and Province (an irregular lore-bearing boundary) sit at overlapping scales but are different kinds and must stay distinct in the docs. No new words are invented (`sector` is reserved by `perception::VisibilitySector`; `tract` and the rest were rejected).
|
||||
|
||||
- **Rationale:** The cascade glued absolute voxel/chunk scales to the body-relative heightmap with three contradictory assumptions and no record fixing metres-per-pixel, so nothing downstream could be metre-precise (Q-110). A rigid absolute ladder with a single elastic seam removes the contradiction at the source: the only body-specific function in the whole chain is region↔planet via `body_radius_km`; everything below is fixed integer math, unblocking the production wiring (T-1046). Every rung earns a job — the region in particular is not mere addressing: it is the natural lockdown scale for weather/season/climate (the Q-105 dynamism source) and the sane planetary grid (~195×98 regions on an Earth-sized body, vs an unusable ~19,500 districts). Confining invented terrain detail to a deterministic scatter layer, and climate variation to an edge-fuzzed continuous blend, honours derive-don't-store ([D-227](#d-227)) while keeping both believable — the grid never shows and the planetary seam is never walked.
|
||||
- **Raised by:** Jeroen, 2026-06-14 — driving Q-110 during the cascade-spine sequencing (next: wire the tier into production, T-1046).
|
||||
- **Implementation:** unblocks **T-1046** (`ChunkPos → … → RegionPos` is now defined); the region↔heightmap interpolation + detail-scatter synthesis + climate edge-fuzz is the elastic-seam stage (new ticket). [D-201](#d-201) amended (Region→Province). The canonical ladder is recorded in **CLAUDE.md**. Refines [D-239](#d-239) §2 (district temperature now modulates a region baseline) and contributes the region-clock answer toward [Q-105](../questions/architecture.md#q-105).
|
||||
- **Cross-reference:** [D-201](#d-201) (spatial hierarchy — tier-4 Region→Province, amended here), [D-202](#d-202) (heightmap 1024×512), [D-204](#d-204) (`body_radius_km`), [D-220](#d-220)/[D-222](#d-222) (locked sub-settlement dims — chunk/block/quarter/district), [D-205](#d-205) (province boundaries — the overlay), [D-225](#d-225)/[D-227](#d-227) (derive-on-demand / don't-store), [D-239](#d-239) §2/§3/§4/§7/§10 (climate primitive refined; warp; seams; budget reframes to per-active-derivation), [Q-105](../questions/architecture.md#q-105) (region seasonal/clock state — answered at the region).
|
||||
- **Dissent:** None
|
||||
|
||||
---
|
||||
|
||||
*100 decisions (D-001 through D-243, excluding gaps). Last updated: 2026-06-14 (spatial scale ladder — D-243 resolves Q-110: nested absolute containment voxel→chunk→block→quarter→district→region with a single elastic region↔planet seam; region is the climate/weather lockdown scale + planetary grid, edge-fuzzed; the 1 km RegionProfile is dropped onto the district; D-201 tier-4 Region→Province; ladder recorded in CLAUDE.md).*
|
||||
|
||||
@@ -431,7 +431,7 @@ Technical foundation questions: engine, protocols, data structures, performance,
|
||||
---
|
||||
|
||||
### Q-110: Region/chunk physical-scale anchoring — what is a region in metres, and what anchors a heightmap pixel?
|
||||
- **Status:** Open — surfaced 2026-06-12 during cascade-work grounding (post-D-239 implementation)
|
||||
- **Status:** Resolved → [D-243](../decisions/architecture.md#d-243) (2026-06-14). A nested absolute-metre containment ladder (voxel→chunk→block→quarter→district→**region** ~205 km) with a **single elastic seam** region↔planet (`round(2πR/204.8 km)` regions per body). Closest to option **(a)** full-planet anchoring, sharpened: the ladder tops at the region (the largest *hard* block, sized to ×100 districts ≈ one weather cell), not the ~1 km RegionProfile (dropped onto the district) — so the elastic count is a sane planetary grid, not millions. The region doubles as the climate/weather/season lockdown scale (feeding Q-105), edge-fuzzed so the grid never shows. D-201 tier-4 "Region"→"Province" disambiguates the vocabulary; the canonical ladder is recorded in CLAUDE.md.
|
||||
- **Question:** The refinement chain's nominal physical anchors (workshop brief: 78 km L1 cell → ~1 km region → 64 m chunk → 1 m voxel, `docs/workshops/tile-derivation-contract/tile-derivation-contract-workshop-brief.md:19-22`) are mutually inconsistent in the implementation, and no record fixes metres-per-heightmap-pixel. Evidence: (1) `region_profile.rs:3-4` documents ~1 km² regions, ~6,000/body (80×75); (2) `derive_all_regions` (`region_profile.rs:1014-1044`) actually tiles the 512×256 L1 working grid at 8 cells/region spanning ±90° latitude — for a planet-scale equirect map an L1 cell is body-relative (~78 km on an Earth-like body), making such a region ~624 km, not 1 km; (3) `cascade.rs:235-237` claims "8 cells per region on a 128×64 working grid → ~80×32 = ~2,560 regions" — internally inconsistent and inconsistent with `heightmap.rs` GRID 512×256; (4) `chunk_context.rs:137-141` assumes a region is 16 chunks ≈ 1.024 km; (5) D-239 §10 asserts "D8 ≈ 152 m/cell" — true only if the whole 512-cell grid spans ~78 km, i.e. one nominal L1 cell, not a planet; (6) D-201's tier-4 "Region" is the 50–500 km Province, colliding with the D-239 ~1 km RegionProfile vocabulary. Voxel/chunk scales are absolute (D-222: 1 m / 64 m) but the heightmap pixel is body-relative — the cascade currently glues them with three contradictory assumptions. **Which anchoring model is canonical?**
|
||||
- **(a) Full-planet anchoring:** fix metres/pixel per body from `body_radius_km` (D-204): `px_m = 2πR/1024`. Regions become fixed ~1 km cells whose count varies per body (millions for a planet) — requires lazy region derivation (already the D-225/D-227 model) plus an explicit L1→region resampling stage; the 8-cells-per-region shortcut and the ~6,000/body budget die.
|
||||
- **(b) Working-window anchoring:** L1–L3 stay planet-equirect; the voxel cascade (region→chunk→voxel) operates on bounded windows (~one 78 km L1 cell) anchored at points of interest (settlements), with fixed metres/pixel inside the window. Matches the brief's chain literally and keeps ~6,000 regions/window; needs a window-addressing scheme in BodyWorldState and a rule for inter-window terrain.
|
||||
|
||||
Reference in New Issue
Block a user