From ee0dd6df2518dc5bbabc5e991e03363192efd1cc Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Fri, 17 Jul 2026 07:39:28 +0200 Subject: [PATCH 1/5] =?UTF-8?q?chore(meta):=20activate=20seam=20batch=20T-?= =?UTF-8?q?1125/T-1126/T-1127=20=E2=80=94=20Jeroen's=20crinkle-varies=20ru?= =?UTF-8?q?lings=20+=20Si=20gap=20resolutions;=20T-1126=20blocked=20by=20T?= =?UTF-8?q?-1125?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pql/changelog/ticket_deps/2026-07.sql | 1 + .pql/changelog/ticket_history/2026-07.sql | 61 ++++++++++++++ .pql/changelog/tickets/2026-07.sql | 99 +++++++++++++++++++++++ 3 files changed, 161 insertions(+) diff --git a/.pql/changelog/ticket_deps/2026-07.sql b/.pql/changelog/ticket_deps/2026-07.sql index a4361903c..8a2fd0f78 100644 --- a/.pql/changelog/ticket_deps/2026-07.sql +++ b/.pql/changelog/ticket_deps/2026-07.sql @@ -1,3 +1,4 @@ INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FKE0EW2617K9VE4QHSYSR9HW', '06FKCPBF41RWK9PW9M22Q7961C', '2026-07-06 10:38:33.017', '2026-07-06 10:38:33.017', NULL, '290574b958bce69afc8a81d7487f8fd5', 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 ('06FB0TNSRWYHR72SC80F5T0TBW', '06FB0TNSRWGDT3AJJE5QD0E3FR', '2026-07-07 16:57:31.296', '2026-07-07 16:57:31.296', NULL, 'e6784e95e525cb6458fe258ab4b6e4c2', 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 ('06FBPTXQGAX5GT4PTTBQYGJCV4', '06FB0TNSRZKXHAYMT7Q6WW3XDC', '2026-07-08 15:53:21.023', '2026-07-08 15:53:21.023', NULL, '64d1cc675b69a17b5e1ec1f59710e9eb', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', '06FPS6RRBF9HHAED5QKTKQCY1G', '2026-07-17 05:38:31.704', '2026-07-17 05:38:31.704', NULL, '2064c9c98f1ac17eb75a65adc6282ea9', 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); diff --git a/.pql/changelog/ticket_history/2026-07.sql b/.pql/changelog/ticket_history/2026-07.sql index ad3bdc72d..1535e62c5 100644 --- a/.pql/changelog/ticket_history/2026-07.sql +++ b/.pql/changelog/ticket_history/2026-07.sql @@ -1415,3 +1415,64 @@ INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, chang 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 ('06FPRZX1PJ55V7462N56ME2QZ0', 'assigned_to', NULL, 'dudley', NULL, '2026-07-16 19:56:02', '2026-07-16 19:56:02.144', '2026-07-16 19:56:02.144', NULL, 'e1f4abe4a51a17463c14dbe68e85f24f', 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 ('06FPRZX1PJ55V7462N56ME2QZ0', 'status', 'in_progress', 'review', NULL, '2026-07-16 20:31:20', '2026-07-16 20:31:20.627', '2026-07-16 20:31:20.627', NULL, '6d6a4755a90f4c6e87cd1f33d03ce397', 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 ('06FPRZX1PJ55V7462N56ME2QZ0', 'status', 'review', 'done', NULL, '2026-07-16 20:36:46', '2026-07-16 20:36:46.488', '2026-07-16 20:36:46.488', NULL, 'e3c840b23066917cd35f62ef3e0e1492', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'description', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64.', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64. + +Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010.', NULL, '2026-07-17 05:29:10', '2026-07-17 05:29:10.686', '2026-07-17 05:29:10.686', NULL, '2e918d8dc5ef41b56a3e35aedce12ec8', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'description', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64. + +Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010.', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64. + +Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010. + +Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data.', NULL, '2026-07-17 05:30:58', '2026-07-17 05:30:58.591', '2026-07-17 05:30:58.591', NULL, '960ed4bdf97d679bf3f1d6cd05eaba8e', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'description', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64. + +Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010. + +Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data.', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64. + +Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010. + +Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data. + +Jeroen, further (2026-07-17): ''more ocean is more wet and rainy, for instance'' — hydrosphere/ocean coverage as a tier-1 driver chain: large hydrosphere -> wetter, rainier climate -> HIGHER EROSION -> smoother, more mature coastlines (and more dendritic/eroded relief); small hydrosphere / thin atmosphere -> dry -> sharp, young, uneroded coasts. Erosion-proneness therefore need not be a new authored field — it is derivable from hydrosphere + atmosphere + precipitation, compounding with the tectonic/glaciation drivers. Note for implementation: hydrosphere already feeds moisture derivation (T-1034 fixed the vocab fall-through), so part of this chain exists — the new link is wet/rainy feeding COASTLINE/relief character, not just biome wetness.', NULL, '2026-07-17 05:32:00', '2026-07-17 05:32:00.843', '2026-07-17 05:32:00.843', NULL, 'aff24c00645457ce4122e705900a685c', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'description', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64. + +Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010. + +Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data. + +Jeroen, further (2026-07-17): ''more ocean is more wet and rainy, for instance'' — hydrosphere/ocean coverage as a tier-1 driver chain: large hydrosphere -> wetter, rainier climate -> HIGHER EROSION -> smoother, more mature coastlines (and more dendritic/eroded relief); small hydrosphere / thin atmosphere -> dry -> sharp, young, uneroded coasts. Erosion-proneness therefore need not be a new authored field — it is derivable from hydrosphere + atmosphere + precipitation, compounding with the tectonic/glaciation drivers. Note for implementation: hydrosphere already feeds moisture derivation (T-1034 fixed the vocab fall-through), so part of this chain exists — the new link is wet/rainy feeding COASTLINE/relief character, not just biome wetness.', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64. + +Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010. + +Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data. + +Jeroen, further (2026-07-17): ''more ocean is more wet and rainy, for instance'' — hydrosphere/ocean coverage as a tier-1 driver chain: large hydrosphere -> wetter, rainier climate -> HIGHER EROSION -> smoother, more mature coastlines (and more dendritic/eroded relief); small hydrosphere / thin atmosphere -> dry -> sharp, young, uneroded coasts. Erosion-proneness therefore need not be a new authored field — it is derivable from hydrosphere + atmosphere + precipitation, compounding with the tectonic/glaciation drivers. Note for implementation: hydrosphere already feeds moisture derivation (T-1034 fixed the vocab fall-through), so part of this chain exists — the new link is wet/rainy feeding COASTLINE/relief character, not just biome wetness. + +--- +Gap resolution (2026-07-17, Si review + Jeroen''s rulings): + +1. TILT/D-240: DROPPED — no axial tilt input; D-240 stays intact. latitude_deg alone carries the position-tier polar effects (glaciation_grade is already latitude/temperature-derived). Tier-1 envelope CONFIRMED as what exists: planet_class + hydrosphere + atmosphere + body_radius_km (BodyParams, body_params_reader.rs:14-20). No tectonic-activity or erosion field exists — tectonic_class derives from planet_class (derive_tectonic_class always falls to its planet_class branch in production); erosion-proneness is DERIVED from hydrosphere + atmosphere + precipitation per Jeroen''s chain. Do not hunt for richer body signals; they are not there. + +2. CIRCULARITY: ACCEPT STALE INPUT — glaciation_grade reads the UNWARPED raw-bilinear ocean_fraction_q (one-step-stale, deterministic, coarse ~156km-pixel value anyway). Document on the call site. tectonic_class is cheap to hoist to the top of derive_district (needs only BodyParams). + +3. SCOPE: BOTH PATHS — the invention must feed derive_district (on-demand: probe + future T-1124 view) AND the production derive_district_profile (district_profile.rs:1062-1134, the batch path BodyWorldState stores — currently has NO scatter at all; the paths silently diverged). Implementer may unify the shared classification core if cleaner than duplicating — his call, reviewers verify. Believability goldens (server/tests/golden/believability.json) WILL regenerate — expected and acknowledged via UPDATE_GOLDEN=1; report the metric deltas (contrast/coherence blocks) in the PR as evidence of improvement, not just churn. + +Si verification facts for the implementer: +- Direct reusable precedents: climate_edge_warp (region_profile.rs:477-513) — warp-the-sampling with a distinct hash path, exactly the pattern for ocean_fraction_q; domain_warp.rs (T-1026, stateless (seed,body,pos)->offset); detail_scatter.rs voxel_mosaic (T-1084) proves slope-INDEPENDENT position-keyed scatter is established. Longitude variation comes free via position-keyed noise on world-metre coords — no explicit longitude plumbing needed. +- Fix shape (from the ticket + rulings): warp the ocean-mask sampling position (invented bays/capes), slope-independent scatter floor whose AMPLITUDE/CHARACTER varies by the two driver tiers (body envelope: planet_class/hydrosphere/atmosphere/radius -> personality incl. erosion-smoothness; position: latitude, glaciation_grade, tectonic_class, climate bands -> fjordy vs smooth vs sharp), plus seeded heterogeneity so same-coast stretches differ. All deterministic seed+body+position (D-227/D-010); continuous across district/region boundaries (D-243 edge-fuzz discipline — character never steps on a line). +- Acceptance: (a) before/after zoom ladders (aliveness_probe --render, same bodies/windows as T-1123: GJ380c/GJ820Bc/GJ244Ad at w256/w64/w16) reviewed by Jeroen — the primary artifact; (b) believability metrics moved not regressed; new coastline-legibility criterion work belongs to open Q-123, not this ticket. (c) determinism byte-compare as in T-1123. +- Performance: no per-district budget record exists; do the D-246-style check — verify derive_district cost stays same order (~8us was the T-1123 measurement) and the believability/derivation harness budgets stay green.', NULL, '2026-07-17 05:37:57', '2026-07-17 05:37:57.401', '2026-07-17 05:37:57.401', NULL, 'a029e889e078aa3d6a2e8ec4a13471b9', 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 ('06FPS6RRBF9HHAED5QKTKQCY1G', 'description', 'T-1123 zoom-ladder finding: derive_vegetation(temp, moisture, elev, near_water) has no ocean/water-mask input — GJ380c w256 renders solid Forest across ~250km of OpenOcean districts (see .cache/screenshots/district-windows gj380c_w256_veg.png vs _morphology.png). Voxel-level water generators mask this in-world, but any district-resolution Atlas view (T-1124) will show forested oceans. Fix: vegetation classification should return a None/Marine class when the district''s morphology/ocean_fraction says water (reuse ocean_fraction_q already computed in derive_district).', 'T-1123 zoom-ladder finding: derive_vegetation(temp, moisture, elev, near_water) has no ocean/water-mask input — GJ380c w256 renders solid Forest across ~250km of OpenOcean districts (see .cache/screenshots/district-windows gj380c_w256_veg.png vs _morphology.png). Voxel-level water generators mask this in-world, but any district-resolution Atlas view (T-1124) will show forested oceans. Fix: vegetation classification should return a None/Marine class when the district''s morphology/ocean_fraction says water (reuse ocean_fraction_q already computed in derive_district). + +Gap resolution (2026-07-17, Si review + Jeroen): (1) GATE: morphology-derived — pass the already-computed morphology water verdict (OpenOcean/Lake) into the vegetation call; vegetation and morphology can NEVER disagree by construction; no fourth threshold is born. (2) VARIANT: Marine, appended at discriminant 6 (after RiparianThicket=5); update the load-bearing vegetation_class_discriminants_pinned test; document that Ord position is non-semantic (the density-ladder doc does not extend to Marine). (3) SEQUENCING: blocked by T-1125 (edge added) — classify against the warped/invented coastline, not the smooth one. Si facts: the ticket phrase near_water is a TRAP — derive_vegetation''s 4th param is near_perennial_water (riparian/river proximity), unrelated to ocean; do not reuse it. voxel.rs consumers are wildcard-safe (ocean districts route to the WaterBody generator upstream per D-239/T-1082) — no changes needed there. aliveness_probe.rs vegetation_rgb is an EXHAUSTIVE match and will fail to compile until it gets a Marine color — extend the palette deliberately (ocean blue distinct from morphology water tones). D-239 gets an implementation-addendum note recording the Marine variant (its §8 climate-vegetation law predates a water class).', NULL, '2026-07-17 05:38:10', '2026-07-17 05:38:10.917', '2026-07-17 05:38:10.917', NULL, 'b29435e54ddc6a109cd87e2f3af36ea9', 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 ('06FPS6TF9XDZPKZY8DHG1T0E2R', 'description', 'T-1123 zoom-ladder finding: GJ244Ad (frozen planet_class, temp panel pegged at the -60C clamp floor, vegetation solid Barren) still renders temperate-green morphology — the 17-zone MorphologyZone vocabulary (T-1027, FROZEN per its own contract) has no ice/glacial zone, and glaciation_grade is a separate DistrictProfile field nothing renders. Do NOT reopen the frozen vocab by default: the likely fix is rendering glaciation_grade as a modifier/overlay on the morphology color (ice-tinting affected zones) in both the T-1123 probe renderer and the future T-1124 view — or, if the team decides an ice zone belongs in the vocabulary after all, that is a deliberate amendment to the freeze decision, not a drive-by. Images: .cache/screenshots/district-windows gj244ad_w256_morphology.png vs _temp.png.', 'T-1123 zoom-ladder finding: GJ244Ad (frozen planet_class, temp panel pegged at the -60C clamp floor, vegetation solid Barren) still renders temperate-green morphology — the 17-zone MorphologyZone vocabulary (T-1027, FROZEN per its own contract) has no ice/glacial zone, and glaciation_grade is a separate DistrictProfile field nothing renders. Do NOT reopen the frozen vocab by default: the likely fix is rendering glaciation_grade as a modifier/overlay on the morphology color (ice-tinting affected zones) in both the T-1123 probe renderer and the future T-1124 view — or, if the team decides an ice zone belongs in the vocabulary after all, that is a deliberate amendment to the freeze decision, not a drive-by. Images: .cache/screenshots/district-windows gj244ad_w256_morphology.png vs _temp.png. + +Refinement (2026-07-17, Si review): READY. Upstream verified HEALTHY — glaciation derivation (derive_glaciation_grade_from_climate, district_profile.rs:339-366) is correctly wired and even gates the Fjord family; GJ244Ad moisture clears the gate floor. This is purely a rendering gap: aliveness_probe render_window_panels computes the full DistrictProfile and discards .glaciation_grade (line ~458 reads only morphology_zone). SCOPE RULING (lead): probe-only THIS ticket — per-pixel ice tint blended on the morphology panel keyed on glaciation_grade (+ optionally a 6th dedicated glaciation panel; PANEL_NAMES/bufs are array-driven, cheap). The wire+client half (glaciation: Vec parallel array inside DistrictGridLayer + build_district_grid + protocol.gd + _draw_gen_district — governance-UNCONSTRAINED per the D-226 growth-ceiling reading: the cap governs new top-level Option layers, not per-cell fields inside a shipped layer; elev_q precedent) is DEFERRED to T-1124''s design pass, which owns the which-fields-ship-at-district-resolution question. Freeze contract confirmed: D-239 SS6 — tint is classifier-tuning territory, no MorphologyZone vocab reopen; an ice zone would need its own explicit D-amendment (not pursued).', NULL, '2026-07-17 05:38:24', '2026-07-17 05:38:24.161', '2026-07-17 05:38:24.161', NULL, '6a9d21bf30820e1d9058b4b8a8fa64df', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'status', 'backlog', 'in_progress', NULL, '2026-07-17 05:38:38', '2026-07-17 05:38:38.565', '2026-07-17 05:38:38.565', NULL, '988ba7528d21a98ad9f40ddfab210d2c', 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 ('06FPS6RRBF9HHAED5QKTKQCY1G', 'status', 'backlog', 'in_progress', NULL, '2026-07-17 05:38:38', '2026-07-17 05:38:38.574', '2026-07-17 05:38:38.574', NULL, '4cb60e2febd859020fc2fbae4aff9e59', 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 ('06FPS6TF9XDZPKZY8DHG1T0E2R', 'status', 'backlog', 'in_progress', NULL, '2026-07-17 05:38:38', '2026-07-17 05:38:38.574', '2026-07-17 05:38:38.574', NULL, '83ccddb3c4ed268a95cbd00f4843b0cd', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'assigned_to', NULL, 'dudley', NULL, '2026-07-17 05:38:51', '2026-07-17 05:38:51.399', '2026-07-17 05:38:51.399', NULL, '1db15e907105ca1e1b2a51736a2fb5b4', 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 ('06FPS6TF9XDZPKZY8DHG1T0E2R', 'assigned_to', NULL, 'dudley', NULL, '2026-07-17 05:38:51', '2026-07-17 05:38:51.406', '2026-07-17 05:38:51.406', NULL, 'a61ffc1075244eda35e07b8037fc561a', 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 ('06FPS6RRBF9HHAED5QKTKQCY1G', 'assigned_to', NULL, 'dudley', NULL, '2026-07-17 05:38:51', '2026-07-17 05:38:51.406', '2026-07-17 05:38:51.406', NULL, 'de3fdd3e8ceac6cd698a2270b5997592', 2) ON CONFLICT(hash) DO NOTHING; diff --git a/.pql/changelog/tickets/2026-07.sql b/.pql/changelog/tickets/2026-07.sql index a50a88c1f..5aa04089c 100644 --- a/.pql/changelog/tickets/2026-07.sql +++ b/.pql/changelog/tickets/2026-07.sql @@ -1957,3 +1957,102 @@ INSERT INTO tickets (record_id, type, parent_record_id, title, description, stat 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 ('06FPS6TF9XDZPKZY8DHG1T0E2R', 'task', '06FGSJNP8YM537DJCC82ZD6544', 'Frozen bodies read temperate-green on district morphology maps — render glaciation_grade', 'T-1123 zoom-ladder finding: GJ244Ad (frozen planet_class, temp panel pegged at the -60C clamp floor, vegetation solid Barren) still renders temperate-green morphology — the 17-zone MorphologyZone vocabulary (T-1027, FROZEN per its own contract) has no ice/glacial zone, and glaciation_grade is a separate DistrictProfile field nothing renders. Do NOT reopen the frozen vocab by default: the likely fix is rendering glaciation_grade as a modifier/overlay on the morphology color (ice-tinting affected zones) in both the T-1123 probe renderer and the future T-1124 view — or, if the team decides an ice zone belongs in the vocabulary after all, that is a deliberate amendment to the freeze decision, not a drive-by. Images: .cache/screenshots/district-windows gj244ad_w256_morphology.png vs _temp.png.', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-16 20:25:45.551', '2026-07-16 20:25:45.551', NULL, '9e0446592b8a98b0db2641cb4b03192b', 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 ('06FPRZX1PJ55V7462N56ME2QZ0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'District-window map renderer — zoom-ladder PNGs from derive_district (aliveness_probe --render)', 'Jeroen (2026-07-16, reviewing the T-1120 screenshots): all Atlas captures are planetary-level; the orbit-to-jet-plane bridge (district-resolution deterministic biome maps) has never been SEEN. Recon verified the fast path: extend server/src/bin/aliveness_probe.rs with --render --window : after the existing cascade_for_body + anchor-district computation (lines 34-113), loop an NxN district window around the anchor calling derive_district(seed, body_id, params, ta, (dx,dy), climate) PER CELL (district_profile.rs:1256 — the pure on-demand 2km carrier; do NOT index bws.districts, that is the coarse gcpr=8 eager grid) and write PNGs via the png crate (pattern at heightmap.rs:184-193): morphology zones (transliterate the 17-entry MORPHOLOGY_COLORS palette from atlas_marker_overlay.gd:65-83 into a Rust [[u8;3];17]), plus elev_q / temperature_c / moisture_q / vegetation_class panels. Deliverable: zoom-ladder image sets (e.g. 256x256 and 64x64 district windows) for 2-3 bodies anchored on settlements, demonstrating the deterministic orbital->district bridge. Quarter/block rendering is OUT of scope (no windowable pure function at that tier).', 'review', 'high', 'dudley', 'server', 'D-243', '2026-07-16 19:55:31.636', '2026-07-16 20:31:20.622', NULL, '80d25588e64088d49f8e6dfac7dc380f', 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 ('06FPRZX1PJ55V7462N56ME2QZ0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'District-window map renderer — zoom-ladder PNGs from derive_district (aliveness_probe --render)', 'Jeroen (2026-07-16, reviewing the T-1120 screenshots): all Atlas captures are planetary-level; the orbit-to-jet-plane bridge (district-resolution deterministic biome maps) has never been SEEN. Recon verified the fast path: extend server/src/bin/aliveness_probe.rs with --render --window : after the existing cascade_for_body + anchor-district computation (lines 34-113), loop an NxN district window around the anchor calling derive_district(seed, body_id, params, ta, (dx,dy), climate) PER CELL (district_profile.rs:1256 — the pure on-demand 2km carrier; do NOT index bws.districts, that is the coarse gcpr=8 eager grid) and write PNGs via the png crate (pattern at heightmap.rs:184-193): morphology zones (transliterate the 17-entry MORPHOLOGY_COLORS palette from atlas_marker_overlay.gd:65-83 into a Rust [[u8;3];17]), plus elev_q / temperature_c / moisture_q / vegetation_class panels. Deliverable: zoom-ladder image sets (e.g. 256x256 and 64x64 district windows) for 2-3 bodies anchored on settlements, demonstrating the deterministic orbital->district bridge. Quarter/block rendering is OUT of scope (no windowable pure function at that tier).', 'done', 'high', 'dudley', 'server', 'D-243', '2026-07-16 19:55:31.636', '2026-07-16 20:36:46.488', NULL, 'c2a534d0423666a0d0c965c3a4d4a1a7', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'D-227 invented detail does not feed district classification — the orbit-to-jet-plane bridge ends at ~500km', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64. + +Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010.', 'backlog', 'high', NULL, 'server', 'D-227', '2026-07-16 20:25:20.427', '2026-07-17 05:29:10.685', NULL, '7483d1c081ba0a6d4fc688e3783b9402', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'D-227 invented detail does not feed district classification — the orbit-to-jet-plane bridge ends at ~500km', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64. + +Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010. + +Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data.', 'backlog', 'high', NULL, 'server', 'D-227', '2026-07-16 20:25:20.427', '2026-07-17 05:30:58.591', NULL, 'a21b8705688641ff805676bebb8a26d4', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'D-227 invented detail does not feed district classification — the orbit-to-jet-plane bridge ends at ~500km', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64. + +Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010. + +Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data. + +Jeroen, further (2026-07-17): ''more ocean is more wet and rainy, for instance'' — hydrosphere/ocean coverage as a tier-1 driver chain: large hydrosphere -> wetter, rainier climate -> HIGHER EROSION -> smoother, more mature coastlines (and more dendritic/eroded relief); small hydrosphere / thin atmosphere -> dry -> sharp, young, uneroded coasts. Erosion-proneness therefore need not be a new authored field — it is derivable from hydrosphere + atmosphere + precipitation, compounding with the tectonic/glaciation drivers. Note for implementation: hydrosphere already feeds moisture derivation (T-1034 fixed the vocab fall-through), so part of this chain exists — the new link is wet/rainy feeding COASTLINE/relief character, not just biome wetness.', 'backlog', 'high', NULL, 'server', 'D-227', '2026-07-16 20:25:20.427', '2026-07-17 05:32:00.842', NULL, 'a06a30fb996bd8f7b584dcd2efd9cd5d', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'D-227 invented detail does not feed district classification — the orbit-to-jet-plane bridge ends at ~500km', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64. + +Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010. + +Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data. + +Jeroen, further (2026-07-17): ''more ocean is more wet and rainy, for instance'' — hydrosphere/ocean coverage as a tier-1 driver chain: large hydrosphere -> wetter, rainier climate -> HIGHER EROSION -> smoother, more mature coastlines (and more dendritic/eroded relief); small hydrosphere / thin atmosphere -> dry -> sharp, young, uneroded coasts. Erosion-proneness therefore need not be a new authored field — it is derivable from hydrosphere + atmosphere + precipitation, compounding with the tectonic/glaciation drivers. Note for implementation: hydrosphere already feeds moisture derivation (T-1034 fixed the vocab fall-through), so part of this chain exists — the new link is wet/rainy feeding COASTLINE/relief character, not just biome wetness. + +--- +Gap resolution (2026-07-17, Si review + Jeroen''s rulings): + +1. TILT/D-240: DROPPED — no axial tilt input; D-240 stays intact. latitude_deg alone carries the position-tier polar effects (glaciation_grade is already latitude/temperature-derived). Tier-1 envelope CONFIRMED as what exists: planet_class + hydrosphere + atmosphere + body_radius_km (BodyParams, body_params_reader.rs:14-20). No tectonic-activity or erosion field exists — tectonic_class derives from planet_class (derive_tectonic_class always falls to its planet_class branch in production); erosion-proneness is DERIVED from hydrosphere + atmosphere + precipitation per Jeroen''s chain. Do not hunt for richer body signals; they are not there. + +2. CIRCULARITY: ACCEPT STALE INPUT — glaciation_grade reads the UNWARPED raw-bilinear ocean_fraction_q (one-step-stale, deterministic, coarse ~156km-pixel value anyway). Document on the call site. tectonic_class is cheap to hoist to the top of derive_district (needs only BodyParams). + +3. SCOPE: BOTH PATHS — the invention must feed derive_district (on-demand: probe + future T-1124 view) AND the production derive_district_profile (district_profile.rs:1062-1134, the batch path BodyWorldState stores — currently has NO scatter at all; the paths silently diverged). Implementer may unify the shared classification core if cleaner than duplicating — his call, reviewers verify. Believability goldens (server/tests/golden/believability.json) WILL regenerate — expected and acknowledged via UPDATE_GOLDEN=1; report the metric deltas (contrast/coherence blocks) in the PR as evidence of improvement, not just churn. + +Si verification facts for the implementer: +- Direct reusable precedents: climate_edge_warp (region_profile.rs:477-513) — warp-the-sampling with a distinct hash path, exactly the pattern for ocean_fraction_q; domain_warp.rs (T-1026, stateless (seed,body,pos)->offset); detail_scatter.rs voxel_mosaic (T-1084) proves slope-INDEPENDENT position-keyed scatter is established. Longitude variation comes free via position-keyed noise on world-metre coords — no explicit longitude plumbing needed. +- Fix shape (from the ticket + rulings): warp the ocean-mask sampling position (invented bays/capes), slope-independent scatter floor whose AMPLITUDE/CHARACTER varies by the two driver tiers (body envelope: planet_class/hydrosphere/atmosphere/radius -> personality incl. erosion-smoothness; position: latitude, glaciation_grade, tectonic_class, climate bands -> fjordy vs smooth vs sharp), plus seeded heterogeneity so same-coast stretches differ. All deterministic seed+body+position (D-227/D-010); continuous across district/region boundaries (D-243 edge-fuzz discipline — character never steps on a line). +- Acceptance: (a) before/after zoom ladders (aliveness_probe --render, same bodies/windows as T-1123: GJ380c/GJ820Bc/GJ244Ad at w256/w64/w16) reviewed by Jeroen — the primary artifact; (b) believability metrics moved not regressed; new coastline-legibility criterion work belongs to open Q-123, not this ticket. (c) determinism byte-compare as in T-1123. +- Performance: no per-district budget record exists; do the D-246-style check — verify derive_district cost stays same order (~8us was the T-1123 measurement) and the believability/derivation harness budgets stay green.', 'backlog', 'high', NULL, 'server', 'D-227', '2026-07-16 20:25:20.427', '2026-07-17 05:37:57.401', NULL, '538b97e5e51823c4f9599fdb4bc7e5ad', 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 ('06FPS6RRBF9HHAED5QKTKQCY1G', 'bug', '06FGSJNP8YM537DJCC82ZD6544', 'derive_vegetation is ocean-blind — district maps paint Forest across open ocean', 'T-1123 zoom-ladder finding: derive_vegetation(temp, moisture, elev, near_water) has no ocean/water-mask input — GJ380c w256 renders solid Forest across ~250km of OpenOcean districts (see .cache/screenshots/district-windows gj380c_w256_veg.png vs _morphology.png). Voxel-level water generators mask this in-world, but any district-resolution Atlas view (T-1124) will show forested oceans. Fix: vegetation classification should return a None/Marine class when the district''s morphology/ocean_fraction says water (reuse ocean_fraction_q already computed in derive_district). + +Gap resolution (2026-07-17, Si review + Jeroen): (1) GATE: morphology-derived — pass the already-computed morphology water verdict (OpenOcean/Lake) into the vegetation call; vegetation and morphology can NEVER disagree by construction; no fourth threshold is born. (2) VARIANT: Marine, appended at discriminant 6 (after RiparianThicket=5); update the load-bearing vegetation_class_discriminants_pinned test; document that Ord position is non-semantic (the density-ladder doc does not extend to Marine). (3) SEQUENCING: blocked by T-1125 (edge added) — classify against the warped/invented coastline, not the smooth one. Si facts: the ticket phrase near_water is a TRAP — derive_vegetation''s 4th param is near_perennial_water (riparian/river proximity), unrelated to ocean; do not reuse it. voxel.rs consumers are wildcard-safe (ocean districts route to the WaterBody generator upstream per D-239/T-1082) — no changes needed there. aliveness_probe.rs vegetation_rgb is an EXHAUSTIVE match and will fail to compile until it gets a Marine color — extend the palette deliberately (ocean blue distinct from morphology water tones). D-239 gets an implementation-addendum note recording the Marine variant (its §8 climate-vegetation law predates a water class).', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-16 20:25:31.484', '2026-07-17 05:38:10.917', NULL, 'd5ab3fc6645ce75ffc423aaec391d033', 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 ('06FPS6TF9XDZPKZY8DHG1T0E2R', 'task', '06FGSJNP8YM537DJCC82ZD6544', 'Frozen bodies read temperate-green on district morphology maps — render glaciation_grade', 'T-1123 zoom-ladder finding: GJ244Ad (frozen planet_class, temp panel pegged at the -60C clamp floor, vegetation solid Barren) still renders temperate-green morphology — the 17-zone MorphologyZone vocabulary (T-1027, FROZEN per its own contract) has no ice/glacial zone, and glaciation_grade is a separate DistrictProfile field nothing renders. Do NOT reopen the frozen vocab by default: the likely fix is rendering glaciation_grade as a modifier/overlay on the morphology color (ice-tinting affected zones) in both the T-1123 probe renderer and the future T-1124 view — or, if the team decides an ice zone belongs in the vocabulary after all, that is a deliberate amendment to the freeze decision, not a drive-by. Images: .cache/screenshots/district-windows gj244ad_w256_morphology.png vs _temp.png. + +Refinement (2026-07-17, Si review): READY. Upstream verified HEALTHY — glaciation derivation (derive_glaciation_grade_from_climate, district_profile.rs:339-366) is correctly wired and even gates the Fjord family; GJ244Ad moisture clears the gate floor. This is purely a rendering gap: aliveness_probe render_window_panels computes the full DistrictProfile and discards .glaciation_grade (line ~458 reads only morphology_zone). SCOPE RULING (lead): probe-only THIS ticket — per-pixel ice tint blended on the morphology panel keyed on glaciation_grade (+ optionally a 6th dedicated glaciation panel; PANEL_NAMES/bufs are array-driven, cheap). The wire+client half (glaciation: Vec parallel array inside DistrictGridLayer + build_district_grid + protocol.gd + _draw_gen_district — governance-UNCONSTRAINED per the D-226 growth-ceiling reading: the cap governs new top-level Option layers, not per-cell fields inside a shipped layer; elev_q precedent) is DEFERRED to T-1124''s design pass, which owns the which-fields-ship-at-district-resolution question. Freeze contract confirmed: D-239 SS6 — tint is classifier-tuning territory, no MorphologyZone vocab reopen; an ice zone would need its own explicit D-amendment (not pursued).', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-16 20:25:45.551', '2026-07-17 05:38:24.161', NULL, '43a14c30a1bf3855a9ca6c6d076e0d30', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'D-227 invented detail does not feed district classification — the orbit-to-jet-plane bridge ends at ~500km', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64. + +Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010. + +Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data. + +Jeroen, further (2026-07-17): ''more ocean is more wet and rainy, for instance'' — hydrosphere/ocean coverage as a tier-1 driver chain: large hydrosphere -> wetter, rainier climate -> HIGHER EROSION -> smoother, more mature coastlines (and more dendritic/eroded relief); small hydrosphere / thin atmosphere -> dry -> sharp, young, uneroded coasts. Erosion-proneness therefore need not be a new authored field — it is derivable from hydrosphere + atmosphere + precipitation, compounding with the tectonic/glaciation drivers. Note for implementation: hydrosphere already feeds moisture derivation (T-1034 fixed the vocab fall-through), so part of this chain exists — the new link is wet/rainy feeding COASTLINE/relief character, not just biome wetness. + +--- +Gap resolution (2026-07-17, Si review + Jeroen''s rulings): + +1. TILT/D-240: DROPPED — no axial tilt input; D-240 stays intact. latitude_deg alone carries the position-tier polar effects (glaciation_grade is already latitude/temperature-derived). Tier-1 envelope CONFIRMED as what exists: planet_class + hydrosphere + atmosphere + body_radius_km (BodyParams, body_params_reader.rs:14-20). No tectonic-activity or erosion field exists — tectonic_class derives from planet_class (derive_tectonic_class always falls to its planet_class branch in production); erosion-proneness is DERIVED from hydrosphere + atmosphere + precipitation per Jeroen''s chain. Do not hunt for richer body signals; they are not there. + +2. CIRCULARITY: ACCEPT STALE INPUT — glaciation_grade reads the UNWARPED raw-bilinear ocean_fraction_q (one-step-stale, deterministic, coarse ~156km-pixel value anyway). Document on the call site. tectonic_class is cheap to hoist to the top of derive_district (needs only BodyParams). + +3. SCOPE: BOTH PATHS — the invention must feed derive_district (on-demand: probe + future T-1124 view) AND the production derive_district_profile (district_profile.rs:1062-1134, the batch path BodyWorldState stores — currently has NO scatter at all; the paths silently diverged). Implementer may unify the shared classification core if cleaner than duplicating — his call, reviewers verify. Believability goldens (server/tests/golden/believability.json) WILL regenerate — expected and acknowledged via UPDATE_GOLDEN=1; report the metric deltas (contrast/coherence blocks) in the PR as evidence of improvement, not just churn. + +Si verification facts for the implementer: +- Direct reusable precedents: climate_edge_warp (region_profile.rs:477-513) — warp-the-sampling with a distinct hash path, exactly the pattern for ocean_fraction_q; domain_warp.rs (T-1026, stateless (seed,body,pos)->offset); detail_scatter.rs voxel_mosaic (T-1084) proves slope-INDEPENDENT position-keyed scatter is established. Longitude variation comes free via position-keyed noise on world-metre coords — no explicit longitude plumbing needed. +- Fix shape (from the ticket + rulings): warp the ocean-mask sampling position (invented bays/capes), slope-independent scatter floor whose AMPLITUDE/CHARACTER varies by the two driver tiers (body envelope: planet_class/hydrosphere/atmosphere/radius -> personality incl. erosion-smoothness; position: latitude, glaciation_grade, tectonic_class, climate bands -> fjordy vs smooth vs sharp), plus seeded heterogeneity so same-coast stretches differ. All deterministic seed+body+position (D-227/D-010); continuous across district/region boundaries (D-243 edge-fuzz discipline — character never steps on a line). +- Acceptance: (a) before/after zoom ladders (aliveness_probe --render, same bodies/windows as T-1123: GJ380c/GJ820Bc/GJ244Ad at w256/w64/w16) reviewed by Jeroen — the primary artifact; (b) believability metrics moved not regressed; new coastline-legibility criterion work belongs to open Q-123, not this ticket. (c) determinism byte-compare as in T-1123. +- Performance: no per-district budget record exists; do the D-246-style check — verify derive_district cost stays same order (~8us was the T-1123 measurement) and the believability/derivation harness budgets stay green.', 'in_progress', 'high', NULL, 'server', 'D-227', '2026-07-16 20:25:20.427', '2026-07-17 05:38:38.565', NULL, '764115aba836c846aba43a62b813ed36', 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 ('06FPS6RRBF9HHAED5QKTKQCY1G', 'bug', '06FGSJNP8YM537DJCC82ZD6544', 'derive_vegetation is ocean-blind — district maps paint Forest across open ocean', 'T-1123 zoom-ladder finding: derive_vegetation(temp, moisture, elev, near_water) has no ocean/water-mask input — GJ380c w256 renders solid Forest across ~250km of OpenOcean districts (see .cache/screenshots/district-windows gj380c_w256_veg.png vs _morphology.png). Voxel-level water generators mask this in-world, but any district-resolution Atlas view (T-1124) will show forested oceans. Fix: vegetation classification should return a None/Marine class when the district''s morphology/ocean_fraction says water (reuse ocean_fraction_q already computed in derive_district). + +Gap resolution (2026-07-17, Si review + Jeroen): (1) GATE: morphology-derived — pass the already-computed morphology water verdict (OpenOcean/Lake) into the vegetation call; vegetation and morphology can NEVER disagree by construction; no fourth threshold is born. (2) VARIANT: Marine, appended at discriminant 6 (after RiparianThicket=5); update the load-bearing vegetation_class_discriminants_pinned test; document that Ord position is non-semantic (the density-ladder doc does not extend to Marine). (3) SEQUENCING: blocked by T-1125 (edge added) — classify against the warped/invented coastline, not the smooth one. Si facts: the ticket phrase near_water is a TRAP — derive_vegetation''s 4th param is near_perennial_water (riparian/river proximity), unrelated to ocean; do not reuse it. voxel.rs consumers are wildcard-safe (ocean districts route to the WaterBody generator upstream per D-239/T-1082) — no changes needed there. aliveness_probe.rs vegetation_rgb is an EXHAUSTIVE match and will fail to compile until it gets a Marine color — extend the palette deliberately (ocean blue distinct from morphology water tones). D-239 gets an implementation-addendum note recording the Marine variant (its §8 climate-vegetation law predates a water class).', 'in_progress', 'medium', NULL, 'server', NULL, '2026-07-16 20:25:31.484', '2026-07-17 05:38:38.573', NULL, '8f3b6d7cab85fb02ef8e134f8a6fe6e7', 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 ('06FPS6TF9XDZPKZY8DHG1T0E2R', 'task', '06FGSJNP8YM537DJCC82ZD6544', 'Frozen bodies read temperate-green on district morphology maps — render glaciation_grade', 'T-1123 zoom-ladder finding: GJ244Ad (frozen planet_class, temp panel pegged at the -60C clamp floor, vegetation solid Barren) still renders temperate-green morphology — the 17-zone MorphologyZone vocabulary (T-1027, FROZEN per its own contract) has no ice/glacial zone, and glaciation_grade is a separate DistrictProfile field nothing renders. Do NOT reopen the frozen vocab by default: the likely fix is rendering glaciation_grade as a modifier/overlay on the morphology color (ice-tinting affected zones) in both the T-1123 probe renderer and the future T-1124 view — or, if the team decides an ice zone belongs in the vocabulary after all, that is a deliberate amendment to the freeze decision, not a drive-by. Images: .cache/screenshots/district-windows gj244ad_w256_morphology.png vs _temp.png. + +Refinement (2026-07-17, Si review): READY. Upstream verified HEALTHY — glaciation derivation (derive_glaciation_grade_from_climate, district_profile.rs:339-366) is correctly wired and even gates the Fjord family; GJ244Ad moisture clears the gate floor. This is purely a rendering gap: aliveness_probe render_window_panels computes the full DistrictProfile and discards .glaciation_grade (line ~458 reads only morphology_zone). SCOPE RULING (lead): probe-only THIS ticket — per-pixel ice tint blended on the morphology panel keyed on glaciation_grade (+ optionally a 6th dedicated glaciation panel; PANEL_NAMES/bufs are array-driven, cheap). The wire+client half (glaciation: Vec parallel array inside DistrictGridLayer + build_district_grid + protocol.gd + _draw_gen_district — governance-UNCONSTRAINED per the D-226 growth-ceiling reading: the cap governs new top-level Option layers, not per-cell fields inside a shipped layer; elev_q precedent) is DEFERRED to T-1124''s design pass, which owns the which-fields-ship-at-district-resolution question. Freeze contract confirmed: D-239 SS6 — tint is classifier-tuning territory, no MorphologyZone vocab reopen; an ice zone would need its own explicit D-amendment (not pursued).', 'in_progress', 'medium', NULL, 'server', NULL, '2026-07-16 20:25:45.551', '2026-07-17 05:38:38.574', NULL, 'd096e14e3dc8723f6b2b8521316980c4', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'D-227 invented detail does not feed district classification — the orbit-to-jet-plane bridge ends at ~500km', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64. + +Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010. + +Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data. + +Jeroen, further (2026-07-17): ''more ocean is more wet and rainy, for instance'' — hydrosphere/ocean coverage as a tier-1 driver chain: large hydrosphere -> wetter, rainier climate -> HIGHER EROSION -> smoother, more mature coastlines (and more dendritic/eroded relief); small hydrosphere / thin atmosphere -> dry -> sharp, young, uneroded coasts. Erosion-proneness therefore need not be a new authored field — it is derivable from hydrosphere + atmosphere + precipitation, compounding with the tectonic/glaciation drivers. Note for implementation: hydrosphere already feeds moisture derivation (T-1034 fixed the vocab fall-through), so part of this chain exists — the new link is wet/rainy feeding COASTLINE/relief character, not just biome wetness. + +--- +Gap resolution (2026-07-17, Si review + Jeroen''s rulings): + +1. TILT/D-240: DROPPED — no axial tilt input; D-240 stays intact. latitude_deg alone carries the position-tier polar effects (glaciation_grade is already latitude/temperature-derived). Tier-1 envelope CONFIRMED as what exists: planet_class + hydrosphere + atmosphere + body_radius_km (BodyParams, body_params_reader.rs:14-20). No tectonic-activity or erosion field exists — tectonic_class derives from planet_class (derive_tectonic_class always falls to its planet_class branch in production); erosion-proneness is DERIVED from hydrosphere + atmosphere + precipitation per Jeroen''s chain. Do not hunt for richer body signals; they are not there. + +2. CIRCULARITY: ACCEPT STALE INPUT — glaciation_grade reads the UNWARPED raw-bilinear ocean_fraction_q (one-step-stale, deterministic, coarse ~156km-pixel value anyway). Document on the call site. tectonic_class is cheap to hoist to the top of derive_district (needs only BodyParams). + +3. SCOPE: BOTH PATHS — the invention must feed derive_district (on-demand: probe + future T-1124 view) AND the production derive_district_profile (district_profile.rs:1062-1134, the batch path BodyWorldState stores — currently has NO scatter at all; the paths silently diverged). Implementer may unify the shared classification core if cleaner than duplicating — his call, reviewers verify. Believability goldens (server/tests/golden/believability.json) WILL regenerate — expected and acknowledged via UPDATE_GOLDEN=1; report the metric deltas (contrast/coherence blocks) in the PR as evidence of improvement, not just churn. + +Si verification facts for the implementer: +- Direct reusable precedents: climate_edge_warp (region_profile.rs:477-513) — warp-the-sampling with a distinct hash path, exactly the pattern for ocean_fraction_q; domain_warp.rs (T-1026, stateless (seed,body,pos)->offset); detail_scatter.rs voxel_mosaic (T-1084) proves slope-INDEPENDENT position-keyed scatter is established. Longitude variation comes free via position-keyed noise on world-metre coords — no explicit longitude plumbing needed. +- Fix shape (from the ticket + rulings): warp the ocean-mask sampling position (invented bays/capes), slope-independent scatter floor whose AMPLITUDE/CHARACTER varies by the two driver tiers (body envelope: planet_class/hydrosphere/atmosphere/radius -> personality incl. erosion-smoothness; position: latitude, glaciation_grade, tectonic_class, climate bands -> fjordy vs smooth vs sharp), plus seeded heterogeneity so same-coast stretches differ. All deterministic seed+body+position (D-227/D-010); continuous across district/region boundaries (D-243 edge-fuzz discipline — character never steps on a line). +- Acceptance: (a) before/after zoom ladders (aliveness_probe --render, same bodies/windows as T-1123: GJ380c/GJ820Bc/GJ244Ad at w256/w64/w16) reviewed by Jeroen — the primary artifact; (b) believability metrics moved not regressed; new coastline-legibility criterion work belongs to open Q-123, not this ticket. (c) determinism byte-compare as in T-1123. +- Performance: no per-district budget record exists; do the D-246-style check — verify derive_district cost stays same order (~8us was the T-1123 measurement) and the believability/derivation harness budgets stay green.', 'in_progress', 'high', 'dudley', 'server', 'D-227', '2026-07-16 20:25:20.427', '2026-07-17 05:38:51.394', NULL, 'd46e951ec6051bced263c73b6ba8c615', 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 ('06FPS6RRBF9HHAED5QKTKQCY1G', 'bug', '06FGSJNP8YM537DJCC82ZD6544', 'derive_vegetation is ocean-blind — district maps paint Forest across open ocean', 'T-1123 zoom-ladder finding: derive_vegetation(temp, moisture, elev, near_water) has no ocean/water-mask input — GJ380c w256 renders solid Forest across ~250km of OpenOcean districts (see .cache/screenshots/district-windows gj380c_w256_veg.png vs _morphology.png). Voxel-level water generators mask this in-world, but any district-resolution Atlas view (T-1124) will show forested oceans. Fix: vegetation classification should return a None/Marine class when the district''s morphology/ocean_fraction says water (reuse ocean_fraction_q already computed in derive_district). + +Gap resolution (2026-07-17, Si review + Jeroen): (1) GATE: morphology-derived — pass the already-computed morphology water verdict (OpenOcean/Lake) into the vegetation call; vegetation and morphology can NEVER disagree by construction; no fourth threshold is born. (2) VARIANT: Marine, appended at discriminant 6 (after RiparianThicket=5); update the load-bearing vegetation_class_discriminants_pinned test; document that Ord position is non-semantic (the density-ladder doc does not extend to Marine). (3) SEQUENCING: blocked by T-1125 (edge added) — classify against the warped/invented coastline, not the smooth one. Si facts: the ticket phrase near_water is a TRAP — derive_vegetation''s 4th param is near_perennial_water (riparian/river proximity), unrelated to ocean; do not reuse it. voxel.rs consumers are wildcard-safe (ocean districts route to the WaterBody generator upstream per D-239/T-1082) — no changes needed there. aliveness_probe.rs vegetation_rgb is an EXHAUSTIVE match and will fail to compile until it gets a Marine color — extend the palette deliberately (ocean blue distinct from morphology water tones). D-239 gets an implementation-addendum note recording the Marine variant (its §8 climate-vegetation law predates a water class).', 'in_progress', 'medium', 'dudley', 'server', NULL, '2026-07-16 20:25:31.484', '2026-07-17 05:38:51.406', NULL, '1b963315c1bd8f52f24d11610f511ca0', 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 ('06FPS6TF9XDZPKZY8DHG1T0E2R', 'task', '06FGSJNP8YM537DJCC82ZD6544', 'Frozen bodies read temperate-green on district morphology maps — render glaciation_grade', 'T-1123 zoom-ladder finding: GJ244Ad (frozen planet_class, temp panel pegged at the -60C clamp floor, vegetation solid Barren) still renders temperate-green morphology — the 17-zone MorphologyZone vocabulary (T-1027, FROZEN per its own contract) has no ice/glacial zone, and glaciation_grade is a separate DistrictProfile field nothing renders. Do NOT reopen the frozen vocab by default: the likely fix is rendering glaciation_grade as a modifier/overlay on the morphology color (ice-tinting affected zones) in both the T-1123 probe renderer and the future T-1124 view — or, if the team decides an ice zone belongs in the vocabulary after all, that is a deliberate amendment to the freeze decision, not a drive-by. Images: .cache/screenshots/district-windows gj244ad_w256_morphology.png vs _temp.png. + +Refinement (2026-07-17, Si review): READY. Upstream verified HEALTHY — glaciation derivation (derive_glaciation_grade_from_climate, district_profile.rs:339-366) is correctly wired and even gates the Fjord family; GJ244Ad moisture clears the gate floor. This is purely a rendering gap: aliveness_probe render_window_panels computes the full DistrictProfile and discards .glaciation_grade (line ~458 reads only morphology_zone). SCOPE RULING (lead): probe-only THIS ticket — per-pixel ice tint blended on the morphology panel keyed on glaciation_grade (+ optionally a 6th dedicated glaciation panel; PANEL_NAMES/bufs are array-driven, cheap). The wire+client half (glaciation: Vec parallel array inside DistrictGridLayer + build_district_grid + protocol.gd + _draw_gen_district — governance-UNCONSTRAINED per the D-226 growth-ceiling reading: the cap governs new top-level Option layers, not per-cell fields inside a shipped layer; elev_q precedent) is DEFERRED to T-1124''s design pass, which owns the which-fields-ship-at-district-resolution question. Freeze contract confirmed: D-239 SS6 — tint is classifier-tuning territory, no MorphologyZone vocab reopen; an ice zone would need its own explicit D-amendment (not pursued).', 'in_progress', 'medium', 'dudley', 'server', NULL, '2026-07-16 20:25:45.551', '2026-07-17 05:38:51.406', NULL, '56b6a078fc17ed79dc85768ab857dd44', 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); -- 2.54.0 From fef422bf018b7109b653678ae8e3c260871b44ba Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Fri, 17 Jul 2026 08:21:07 +0200 Subject: [PATCH 2/5] =?UTF-8?q?feat(simulation):=20T-1125/T-1126=20coastli?= =?UTF-8?q?ne=20invention=20=E2=80=94=20two-tier=20character=20model,=20sh?= =?UTF-8?q?ared=20invent=5Fprimitives,=20Marine=20vegetation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit T-1125: new atlas/coast_invention.rs — BodyCoastEnvelope (tier 1: erosion maturity derived hydro×atmo, tectonic energy; D-240 stands, no orbital inputs) + coast_character_at (tier 2: latitude, glaciation fjord response, local wetness, seeded 100-400 km heterogeneity field). C1 multi-octave warp ~16-262 km, sub-pixel cap 0.75 px, salted stream isolated from terrain scatter and climate edge-fuzz. district_profile: shared invent_primitives front-half (warped envelope sampling + slope-independent scatter floor + shoreline carving) wired into BOTH derive_district and derive_district_profile — the batch path's scatter-free area-mean aggregate is replaced by an invented centre-point sample of the same continuous field, making silent path divergence structurally impossible. T-1126: VegetationClass::Marine appended at discriminant 6, verdict morphology-derived (open_water from OpenOcean|Lake — no fourth ocean threshold); airless precedence preserved. voxel.rs exhaustive-match arm documented unreachable (WaterBody owns ocean districts). Co-Authored-By: Claude Fable 5 --- server/src/atlas/coast_invention.rs | 388 +++++++++++++++++++++++++++ server/src/atlas/detail_scatter.rs | 4 +- server/src/atlas/district_profile.rs | 359 +++++++++++++++++++------ server/src/atlas/mod.rs | 1 + server/src/atlas/voxel.rs | 5 + 5 files changed, 673 insertions(+), 84 deletions(-) create mode 100644 server/src/atlas/coast_invention.rs diff --git a/server/src/atlas/coast_invention.rs b/server/src/atlas/coast_invention.rs new file mode 100644 index 000000000..006bc2d87 --- /dev/null +++ b/server/src/atlas/coast_invention.rs @@ -0,0 +1,388 @@ +//! Coastline / terrain-detail invention character model (T-1125, D-227). +//! +//! The D-227 promise — terrain finer than the heightmap is *invented +//! deterministically* — ended at ~500 km until T-1125: district classification +//! consumed the raw bilinear envelope, so coasts were pixel-smooth and the +//! detail-scatter amplitude was slaved to the coarse heightmap slope (≈0 on +//! exactly the low-relief coasts where coastlines live). This module carries +//! the *character* half of the fix: **where the invention is jagged, where it +//! is smooth, and how strongly it perturbs**, per Jeroen's 2026-07-17 rulings: +//! +//! - **Crinkle varies.** Never one global roughness constant. Two driver tiers: +//! - **Tier 1 — body personality envelope** ([`BodyCoastEnvelope`]): derived +//! from the *existing* authored body params only — `planet_class` (via +//! `TectonicClass`), `hydrosphere`, `atmosphere`, `body_radius_km` via the +//! pixel⇄metre seam. D-240 stands: **no axial tilt, no orbital data**. No +//! erosion field exists — erosion-proneness is *derived*: more ocean → wetter, +//! rainier → higher erosion → smoother mature coasts; dry / thin-atmosphere → +//! sharp young coasts. +//! - **Tier 2 — position character** ([`coast_character_at`]): latitude, +//! driver-tier `GlaciationGrade` (high-latitude glaciated coasts go fjordy), +//! local wetness, and a seeded long-wavelength heterogeneity field +//! ([`character_field`], the T-1084 `voxel_mosaic` pattern at 100–400 km +//! octaves) so stretches of the *same* coast differ. Longitude participates +//! for free: everything is keyed on absolute world-metre coordinates. +//! +//! ## Determinism & continuity +//! +//! Pure functions of `(seed, body, position)` (D-227/D-010). The warp and the +//! character field are C¹ multi-octave value noise — character never steps on a +//! district/region line (the D-243 edge-fuzz discipline), and is identical for +//! any window size or derivation path that asks about the same world position. +//! +//! ## Distinct hash path +//! +//! The warp stream is salted ([`COAST_WARP_SALT`]) so it is never correlated +//! with the terrain detail-scatter or the climate edge-fuzz warp — the same +//! isolation convention as `region_profile::climate_edge_warp`. + +use crate::atlas::detail_scatter::value_noise; +use crate::atlas::district_profile::{BodyParams, GlaciationGrade, TectonicClass}; +use crate::seed::splitmix64; + +/// Distinct hash-path salt for the coastline warp stream (see module docs). +const COAST_WARP_SALT: u64 = 0xC0A5_71E1_1BAD_5EED; + +/// Salt separating the warp's y-channel from its x-channel. +const COAST_WARP_Y_SALT: u64 = 0xD1F7_0CEA_2B0A_D515; + +/// Salt for the tier-2 heterogeneity field (same-coast stretches differ). +const CHARACTER_FIELD_SALT: u64 = 0x0C0A_57C4_A24C_7E12; + +/// Coast-warp octave wavelengths in metres (≈16–262 km): capes and gulfs at the +/// top, coves and inlets at the bottom. All above the 4–33 km detail-scatter +/// band, and all below ~2 heightmap pixels — the warp perturbs the coast, it +/// does not rewrite continents (the heightmap stays the truth at its own scale). +const WARP_OCTAVE_WAVELENGTHS_M: [f64; 5] = [262_144.0, 131_072.0, 65_536.0, 32_768.0, 16_384.0]; + +/// Heterogeneity-field octave wavelengths in metres (≈100–400 km): the scale on +/// which one planet's coastline personality drifts from stretch to stretch. +const CHARACTER_OCTAVE_WAVELENGTHS_M: [f64; 3] = [409_600.0, 204_800.0, 102_400.0]; + +/// Hard cap on the coast-warp displacement, in working-grid pixel units. Keeps +/// the invention strictly sub-pixel so planetary-scale reads are unchanged. +const WARP_AMPLITUDE_CAP_PX: f64 = 0.75; + +// --------------------------------------------------------------------------- +// Tier 1 — body personality envelope +// --------------------------------------------------------------------------- + +/// The body-level coastline/relief personality (T-1125 tier 1) — one per body, +/// derived from authored [`BodyParams`] only (D-240 discipline; see module docs). +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct BodyCoastEnvelope { + /// 0–1 — Jeroen's erosion chain: `hydro_wetness × atmo_density`. Erosion + /// needs both water and an atmosphere to rain it back down. High = mature, + /// smoothed coasts; low = sharp young coasts. + pub erosion_maturity: f64, + /// 0–1 — from [`TectonicClass`] (planet_class-derived): relief energy. + pub tectonic_energy: f64, + /// Base octave-falloff roughness (0–1) before tier-2 modulation. + pub roughness: f64, + /// Base coast-warp amplitude in working-grid pixel units. + pub warp_amplitude_px: f64, + /// Base slope-independent detail-scatter floor, in `elev_pct` units (0–1). + pub scatter_floor: f64, +} + +/// Derive the tier-1 envelope. `tectonic` is passed in (the caller hoists +/// `derive_tectonic_class`, which needs only `BodyParams`). +/// +/// The `hydrosphere` buckets mirror `derive_moisture_q`'s T-1034 vocabulary +/// grouping — keep the two in sync when the DB vocabulary grows. +pub fn body_coast_envelope(params: &BodyParams, tectonic: TectonicClass) -> BodyCoastEnvelope { + let hydro_wetness: f64 = match params.hydrosphere.as_deref().unwrap_or("none") { + "liquid_water" | "ocean" | "ocean-coastal" | "extensive" => 1.0, + "rivers" | "rivers-lakes" | "moderate" => 0.65, + "ice" => 0.30, + "subsurface_liquid" => 0.20, + "subsurface" | "subsurface_ice" => 0.15, + "minimal" | "trace" => 0.10, + "none" => 0.0, + _ => 0.40, + }; + let atmo_density: f64 = match params.atmosphere.as_deref().unwrap_or("none") { + "dense" => 1.0, + "standard" | "breathable" => 0.85, + "toxic" => 0.70, + "thin" => 0.35, + "none" => 0.0, + _ => 0.50, + }; + let erosion_maturity = hydro_wetness * atmo_density; + let tectonic_energy: f64 = match tectonic { + TectonicClass::Volcanic => 1.0, + TectonicClass::Active | TectonicClass::TidallyForced => 0.70, + TectonicClass::Stable => 0.25, + }; + BodyCoastEnvelope { + erosion_maturity, + tectonic_energy, + // Young (uneroded) + tectonic worlds read jagged; mature wet worlds smooth. + roughness: (0.20 + 0.50 * tectonic_energy + 0.35 * (1.0 - erosion_maturity)) + .clamp(0.0, 1.0), + // Every body gets embayments; erosion widens/rounds them (rias, estuaries), + // tectonic energy adds displacement of its own. (Tuned against the T-1125 + // zoom ladder: at ~0.2 px effective amplitude the w64 view still read as + // wavy stripes — bays need to displace a meaningful fraction of a pixel.) + warp_amplitude_px: 0.30 + 0.25 * erosion_maturity + 0.20 * tectonic_energy, + // Invented relief floor: young worlds carry more uneroded relief; even + // mature worlds keep gentle rolling texture (eroded ≠ billiard-flat). + scatter_floor: 0.10 + 0.15 * tectonic_energy + 0.05 * (1.0 - erosion_maturity), + } +} + +// --------------------------------------------------------------------------- +// Tier 2 — position character +// --------------------------------------------------------------------------- + +/// The resolved invention character at one world position (T-1125 tier 2). +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct CoastCharacter { + /// Coast-warp displacement amplitude, working-grid pixel units (≤ cap). + pub warp_amplitude_px: f64, + /// 0–1 octave falloff: high keeps small-wavelength energy (jagged). + pub roughness: f64, + /// Slope-independent detail-scatter floor (`elev_pct` units). + pub scatter_floor: f64, + /// 0–1 ridged-folding blend for the scatter (fjord/tectonic sharpness). + pub ridge: f64, +} + +/// Resolve the invention character at `(wx, wy)` world metres. +/// +/// `driver_glaciation` / `driver_moisture_q` are the **one-step-stale** climate +/// drivers computed from the *unwarped* raw-bilinear primitives (the T-1125 +/// circularity ruling — see the call site in `district_profile`). +pub fn coast_character_at( + env: &BodyCoastEnvelope, + seed: u64, + wx: f64, + wy: f64, + lat_deg: f64, + driver_glaciation: GlaciationGrade, + driver_moisture_q: i32, +) -> CoastCharacter { + // Seeded heterogeneity in [0,1]: same-coast stretches differ (Jeroen). + let hetero = character_field(seed, wx, wy); + let fjord: f64 = match driver_glaciation { + GlaciationGrade::None => 0.0, + GlaciationGrade::Light => 0.30, + GlaciationGrade::Moderate => 0.70, + GlaciationGrade::Heavy => 1.0, + // Under a permanent cap the fjord carving is there but partly buried. + GlaciationGrade::IceCap => 0.90, + }; + let lat_frac = (lat_deg.abs() / 90.0).clamp(0.0, 1.0); + // Local wetness echoes the tier-1 erosion chain at position scale: the wet + // stretches of a body erode smoother than its dry stretches. + let wet_local = (driver_moisture_q.clamp(0, 100) as f64) / 100.0; + + CoastCharacter { + warp_amplitude_px: (env.warp_amplitude_px * (0.75 + 0.60 * hetero) * (1.0 + 0.50 * fjord)) + .min(WARP_AMPLITUDE_CAP_PX), + roughness: (env.roughness + 0.35 * fjord + 0.10 * lat_frac + 0.25 * (hetero - 0.5) + - 0.15 * wet_local) + .clamp(0.05, 1.0), + scatter_floor: (env.scatter_floor * (0.70 + 0.60 * hetero) + 0.10 * fjord).clamp(0.0, 0.40), + ridge: (0.15 + 0.55 * fjord + 0.35 * env.tectonic_energy).clamp(0.0, 1.0), + } +} + +// --------------------------------------------------------------------------- +// Fields +// --------------------------------------------------------------------------- + +/// Long-wavelength heterogeneity field in `[0, 1]` (the T-1084 `voxel_mosaic` +/// pattern at coast scale): drives *where along a coast* the character shifts. +pub fn character_field(seed: u64, wx: f64, wy: f64) -> f64 { + let seed = splitmix64(seed ^ CHARACTER_FIELD_SALT); + let mut sum = 0.0; + let mut amp = 1.0; + let mut norm = 0.0; + for (i, &wl) in CHARACTER_OCTAVE_WAVELENGTHS_M.iter().enumerate() { + sum += value_noise( + seed.wrapping_add((i as u64).wrapping_mul(0x1000)), + wx, + wy, + wl, + ) * amp; + norm += amp; + amp *= 0.5; + } + (((sum / norm) + 1.0) * 0.5).clamp(0.0, 1.0) +} + +/// The coastline domain-warp displacement in **working-grid pixel units**. +/// +/// Two independent C¹ fBm channels (x/y) on the salted stream, shaped by the +/// position character: `roughness` controls octave falloff (mature coasts keep +/// only the broad bays; young/fjordy coasts keep the jagged inlets) and `ridge` +/// blends ridged folding in (crease-cornered incursions — fjord arms), exactly +/// the `detail_scatter::enveloped_fbm` modulation convention. +/// +/// Apply the same offset to *every* envelope field sampled at the position +/// (elevation, slope, ocean mask) so the invented terrain moves coherently — +/// a warped-in bay carries its sea-level elevation with it. +pub fn coast_warp_px(seed: u64, wx: f64, wy: f64, ch: &CoastCharacter) -> (f64, f64) { + let sx = splitmix64(seed ^ COAST_WARP_SALT); + let sy = splitmix64(sx ^ COAST_WARP_Y_SALT); + ( + warp_fbm(sx, wx, wy, ch) * ch.warp_amplitude_px, + warp_fbm(sy, wx, wy, ch) * ch.warp_amplitude_px, + ) +} + +/// Roughness/ridge-shaped fBm in ≈`[-1, 1]` over the coast-warp octave band. +fn warp_fbm(seed: u64, wx: f64, wy: f64, ch: &CoastCharacter) -> f64 { + let mut sum = 0.0; + let mut amp = 1.0; + let mut norm = 0.0; + for (i, &wl) in WARP_OCTAVE_WAVELENGTHS_M.iter().enumerate() { + let mut n = value_noise( + seed.wrapping_add((i as u64).wrapping_mul(0x1000)), + wx, + wy, + wl, + ); + if ch.ridge > 0.0 { + let ridged = 1.0 - 2.0 * n.abs(); + n = n * (1.0 - ch.ridge) + ridged * ch.ridge; + } + sum += n * amp; + norm += amp; + // Falloff: smooth coasts damp high frequencies (0.45 — rounded rias, + // never featureless); jagged coasts keep them (0.80). Tuned on the + // ladder: at 0.35 base a mature coast lost its 16–65 km headlands + // entirely and still read as stripes at w64. + amp *= 0.45 + 0.35 * ch.roughness; + } + sum / norm +} + +#[cfg(test)] +mod tests { + use super::*; + + fn params(hydro: &str, atmo: &str, class: &str) -> BodyParams { + BodyParams { + hydrosphere: Some(hydro.to_string()), + atmosphere: Some(atmo.to_string()), + planet_class: Some(class.to_string()), + tectonic_activity: None, + latitude_deg: 0.0, + elevation_km: 0.0, + body_radius_km: Some(6000.0), + } + } + + #[test] + fn envelope_differentiates_wet_from_dry_bodies() { + // Jeroen's erosion chain: ocean+breathable erodes smooth; dry+thin stays sharp. + let wet = body_coast_envelope( + ¶ms("ocean", "breathable", "temperate"), + TectonicClass::Stable, + ); + let dry = body_coast_envelope(¶ms("minimal", "thin", "arid"), TectonicClass::Stable); + assert!(wet.erosion_maturity > dry.erosion_maturity); + assert!( + wet.roughness < dry.roughness, + "mature coasts must be smoother" + ); + assert!( + wet.warp_amplitude_px > dry.warp_amplitude_px, + "erosion widens embayments (rias) — wet worlds warp broader" + ); + } + + #[test] + fn envelope_tectonics_raise_roughness_and_relief_floor() { + let stable = body_coast_envelope( + ¶ms("ocean", "breathable", "temperate"), + TectonicClass::Stable, + ); + let volcanic = body_coast_envelope( + ¶ms("ocean", "breathable", "volcanic"), + TectonicClass::Volcanic, + ); + assert!(volcanic.roughness > stable.roughness); + assert!(volcanic.scatter_floor > stable.scatter_floor); + } + + #[test] + fn character_fjord_raises_roughness_amplitude_and_ridge() { + let env = body_coast_envelope(¶ms("ice", "thin", "frozen"), TectonicClass::Stable); + let temperate = coast_character_at(&env, 42, 1e6, 2e6, 40.0, GlaciationGrade::None, 40); + let fjordy = coast_character_at(&env, 42, 1e6, 2e6, 70.0, GlaciationGrade::Heavy, 40); + assert!(fjordy.roughness > temperate.roughness); + assert!(fjordy.warp_amplitude_px > temperate.warp_amplitude_px); + assert!(fjordy.ridge > temperate.ridge); + } + + #[test] + fn character_heterogeneity_varies_along_a_coast() { + // Same body, same latitude, positions ~600 km apart: the seeded field must + // move the character so same-coast stretches differ (Jeroen's ruling). + let env = body_coast_envelope( + ¶ms("ocean", "breathable", "temperate"), + TectonicClass::Stable, + ); + let chars: Vec = (0..8) + .map(|i| { + coast_character_at( + &env, + 7, + i as f64 * 600_000.0, + 500_000.0, + 30.0, + GlaciationGrade::None, + 50, + ) + .warp_amplitude_px + }) + .collect(); + let min = chars.iter().cloned().fold(f64::INFINITY, f64::min); + let max = chars.iter().cloned().fold(f64::NEG_INFINITY, f64::max); + assert!( + max - min > 0.02, + "character must drift along the coast (got range {})", + max - min + ); + } + + #[test] + fn warp_is_deterministic_bounded_and_continuous() { + let env = body_coast_envelope( + ¶ms("ocean", "breathable", "temperate"), + TectonicClass::Stable, + ); + let ch = coast_character_at(&env, 42, 3e6, 1e6, 30.0, GlaciationGrade::Light, 55); + let a = coast_warp_px(42, 3e6, 1e6, &ch); + let b = coast_warp_px(42, 3e6, 1e6, &ch); + assert_eq!(a, b, "warp must be deterministic"); + for i in 0..400 { + let (dx, dy) = coast_warp_px(42, i as f64 * 9_137.0, i as f64 * -7_211.0, &ch); + assert!(dx.abs() <= WARP_AMPLITUDE_CAP_PX + 1e-9); + assert!(dy.abs() <= WARP_AMPLITUDE_CAP_PX + 1e-9); + } + // C¹ continuity: a 10 m step is a tiny displacement change — the coast + // character never steps on a line (D-243 edge-fuzz discipline). + let (x0, y0) = coast_warp_px(42, 5e6, 5e6, &ch); + let (x1, y1) = coast_warp_px(42, 5e6 + 10.0, 5e6, &ch); + assert!((x0 - x1).abs() < 0.01 && (y0 - y1).abs() < 0.01); + } + + #[test] + fn warp_stream_uncorrelated_with_scatter_stream() { + // Distinct hash path: the warp at a position must not track the terrain + // detail-scatter at the same position (salted stream isolation). + let env = body_coast_envelope( + ¶ms("ocean", "breathable", "temperate"), + TectonicClass::Stable, + ); + let ch = coast_character_at(&env, 42, 1e6, 1e6, 20.0, GlaciationGrade::None, 60); + let (wdx, _) = coast_warp_px(42, 1e6, 1e6, &ch); + let scatter = crate::atlas::detail_scatter::terrain_detail(42, 1e6, 1e6, 1.0, 0.5); + assert_ne!(wdx, scatter); + } +} diff --git a/server/src/atlas/detail_scatter.rs b/server/src/atlas/detail_scatter.rs index 207ccffb0..54d9280f2 100644 --- a/server/src/atlas/detail_scatter.rs +++ b/server/src/atlas/detail_scatter.rs @@ -49,7 +49,9 @@ fn lattice(seed: u64, ix: i64, iy: i64) -> f64 { /// Smooth (C¹) value-noise sample in `[-1, 1]` at world `(wx, wy)` for one /// wavelength. Smoothstep interpolation keeps lattice cell boundaries crease-free. -fn value_noise(seed: u64, wx: f64, wy: f64, wavelength_m: f64) -> f64 { +/// `pub(crate)`: also the noise substrate of the T-1125 coastline invention +/// ([`crate::atlas::coast_invention`]), which salts its own seed stream. +pub(crate) fn value_noise(seed: u64, wx: f64, wy: f64, wavelength_m: f64) -> f64 { let fx = wx / wavelength_m; let fy = wy / wavelength_m; let (x0, y0) = (fx.floor(), fy.floor()); diff --git a/server/src/atlas/district_profile.rs b/server/src/atlas/district_profile.rs index 2ab9b3045..fe9887738 100644 --- a/server/src/atlas/district_profile.rs +++ b/server/src/atlas/district_profile.rs @@ -22,6 +22,7 @@ use std::collections::BTreeMap; use serde::{Deserialize, Serialize}; +use crate::atlas::coast_invention; use crate::atlas::features::TerrainAnalysis; use crate::atlas::region_profile::{self, RegionProfile}; use crate::atlas::scale::{self, BasinDirection, RegionPos}; @@ -117,6 +118,13 @@ pub enum VegetationClass { /// Riparian Thicket — 1–3 tile band along perennial waterways in Forest zones. /// Takes precedence over Forest when adjacent to a perennial river. RiparianThicket = 5, + /// Open water (T-1126): the district's morphology verdict is OpenOcean/Lake — + /// no land vegetation. Set from the already-derived `MorphologyZone`, never + /// from a water threshold of its own (vegetation and morphology can never + /// disagree by construction). NOTE: the `Ord` position is NON-SEMANTIC — + /// the D-239 §8 Forest>Scrub>Barren density-ladder reading does not extend + /// to `Marine`; the discriminant is append-only serialisation order. + Marine = 6, } // --------------------------------------------------------------------------- @@ -377,17 +385,31 @@ pub fn derive_glaciation_grade_from_climate( /// `near_perennial_water` signals whether this district is within the riparian /// band (i.e. the district or an adjacent district carries a perennial waterway). /// When true the riparian sub-variant is returned: `RiparianThicket` over -/// Forest, `RiparianScrub` over Scrub/Barren. +/// Forest, `RiparianScrub` over Scrub/Barren. It is a RIVER-proximity signal — +/// unrelated to `open_water`, which is the ocean/lake verdict. +/// +/// `open_water` (T-1126) is the **morphology-derived** water verdict — `true` +/// when the district's already-derived `MorphologyZone` is OpenOcean/Lake. +/// Callers pass the verdict in; this function must NEVER re-derive it from an +/// ocean-fraction threshold of its own (no fourth threshold — vegetation and +/// morphology can never disagree by construction). Airless takes precedence: +/// an airless body has no climate/vegetation branch at all (D-239 §2), so its +/// seas read `Absent`, not `Marine`. pub fn derive_vegetation( temperature_c: Option, moisture_q: i32, elev_q: i32, near_perennial_water: bool, + open_water: bool, ) -> VegetationClass { // Airless: entire climate/vegetation branch absent. let Some(temp_c) = temperature_c else { return VegetationClass::Absent; }; + // Open water (T-1126): the morphology verdict, passed in — see doc above. + if open_water { + return VegetationClass::Marine; + } let temp_i = temp_c as i32; // Extreme cold → Barren regardless of water proximity: below ~−50 °C mean @@ -1032,6 +1054,140 @@ pub fn derive_moisture_q( (ceiling - lat_penalty - elev_penalty - cont_penalty).clamp(0, 100) } +// --------------------------------------------------------------------------- +// T-1125 — invented primitives (shared by both derivation paths) +// --------------------------------------------------------------------------- + +/// The invented `(slope_q, elev_q, ocean_fraction_q)` triple (T-1125, D-227). +struct InventedPrimitives { + slope_q: i32, + elev_q: i32, + ocean_fraction_q: i32, +} + +/// Compose the invented district primitives at one position — the shared front +/// half of BOTH derivation paths ([`derive_district`] on-demand and +/// [`derive_district_profile`] batch), so the D-227 invention can never diverge +/// between them again (the pre-T-1125 failure: the batch path was scatter-free). +/// +/// Steps: +/// 1. **Driver tier (one-step-stale, T-1125 circularity ruling):** the raw +/// *unwarped* bilinear envelope feeds a provisional moisture/temperature → +/// [`derive_glaciation_grade_from_climate`] — the coast character reads +/// these stale drivers; it never reads the warped values it produces. The +/// raw inputs are the coarse ~40–160 km/pixel envelope anyway, so one step +/// of staleness is far below the signal's own resolution. +/// 2. **Character:** tier-1 body envelope ([`coast_invention::body_coast_envelope`]; +/// `derive_tectonic_class` hoisted here — it needs only `BodyParams`) + +/// tier-2 position character ([`coast_invention::coast_character_at`]). +/// 3. **Invented coastline:** every envelope field (elevation, slope, ocean +/// mask) is sampled at the SAME warped position, so terrain moves coherently +/// — a warped-in bay carries its sea-level elevation with it. +/// 4. **Slope-independent scatter:** the character's `scatter_floor` breaks the +/// old amplitude-slaved-to-coarse-slope collapse (invention ≈ 0 exactly on +/// low-relief coasts); `ridge` carries fjord/tectonic sharpness. +/// +/// `body_params` must already carry the district's `latitude_deg`. +#[allow(clippy::too_many_arguments)] +fn invent_primitives( + seed: SeedChain, + body_params: &BodyParams, + climate: &ClimateConstants, + ta: &TerrainAnalysis, + px: f64, + py: f64, + world_x_m: f64, + world_y_m: f64, + region_baseline_c: Option, +) -> InventedPrimitives { + // ── 1. Driver tier: UNWARPED raw-bilinear climate (one-step-stale). ───── + let raw_elev_q = + ((bilinear(&ta.elev_pct, ta.w, ta.h, px, py) as f64 * 100.0).round() as i32).clamp(0, 100); + let raw_ocean_q = ((bilinear_bool(&ta.ocean_mask, ta.w, ta.h, px, py) as f64 * 100.0).round() + as i32) + .clamp(0, 100); + let driver_params = BodyParams { + elevation_km: (raw_elev_q as f64 / 100.0) * MAX_REGION_ELEVATION_KM, + ..body_params.clone() + }; + let driver_temp = match region_baseline_c { + Some(b) => derive_district_temperature_c(Some(b), &driver_params, climate, 0.0), + None => derive_temperature_c(&driver_params, climate, seed.seed()), + }; + let driver_moisture = derive_moisture_q(&driver_params, raw_elev_q, raw_ocean_q, climate); + let driver_glaciation = derive_glaciation_grade_from_climate(driver_temp, driver_moisture); + + // ── 2. Character: body personality envelope + position modulation. ────── + let tectonic = derive_tectonic_class(body_params); + let envelope = coast_invention::body_coast_envelope(body_params, tectonic); + let ch = coast_invention::coast_character_at( + &envelope, + seed.seed(), + world_x_m, + world_y_m, + body_params.latitude_deg, + driver_glaciation, + driver_moisture, + ); + + // ── 3. Invented coastline: warp the whole envelope sampling. ──────────── + let (wdx, wdy) = coast_invention::coast_warp_px(seed.seed(), world_x_m, world_y_m, &ch); + let (spx, spy) = (px + wdx, py + wdy); + let elev_pct = bilinear(&ta.elev_pct, ta.w, ta.h, spx, spy) as f64; + let slope_deg = bilinear(&ta.slope_deg, ta.w, ta.h, spx, spy) as f64; + let ocean_frac = bilinear_bool(&ta.ocean_mask, ta.w, ta.h, spx, spy) as f64; + + // ── 4. Slope-independent scatter (floor + ridge character). ───────────── + let local_slope = (slope_deg / 45.0).clamp(0.0, 1.0); + let env_amp = (local_slope + ch.scatter_floor).clamp(0.0, 1.0); + let ruggedness = (0.6 * local_slope + 0.5 * ch.ridge).clamp(0.0, 1.0); + let scatter = crate::atlas::detail_scatter::terrain_detail( + seed.seed(), + world_x_m, + world_y_m, + env_amp, + ruggedness, + ); + + // Shoreline carving (T-1125): glacial / tectonically-young SHORES are cut + // steep — fjord walls and cliff coasts. The gentle scatter floor alone can + // never voice the D-239 steep coastal families (Fjord gates at slope_q ≥ 40, + // CliffCoast at ≥ 55), so where the invented position is coastal + // (`shoreline` peaks at the ocean-fraction midpoint) and the character is + // ridged (fjord/tectonic), the normalized scatter contributes real slope — + // patchy by construction (it reuses the scatter noise), so a glaciated + // coast grows fjord SEGMENTS, not a uniform fjord stripe. + let shoreline = (ocean_frac * (1.0 - ocean_frac) * 4.0).clamp(0.0, 1.0); + let carve = 0.55 * ch.ridge * shoreline * (scatter.abs() / env_amp.max(0.05)).clamp(0.0, 1.0); + + InventedPrimitives { + elev_q: (((elev_pct + scatter) * 100.0).round() as i32).clamp(0, 100), + slope_q: (((local_slope + ruggedness * scatter.abs() + carve) * 100.0).round() as i32) + .clamp(0, 100), + ocean_fraction_q: ((ocean_frac * 100.0).round() as i32).clamp(0, 100), + } +} + +/// Fractional working-grid position → absolute world metres — the inverse of +/// [`derive_district`]'s district→pixel mapping (D-204 elastic seam), used by +/// the batch path so both derivation paths key the invention noise fields on +/// the same world-metre convention. Radius-less bodies fall back to the +/// 1-working-pixel = 1-district convention `derive_district` uses. +fn pixel_to_world_m(px: f64, py: f64, w: usize, h: usize, radius_km: Option) -> (f64, f64) { + match radius_km { + Some(r) if r > 0.0 => { + let wx = px / w.max(1) as f64 * (std::f64::consts::TAU * r * 1000.0); + let lat_frac = if h > 1 { + py / (h - 1) as f64 - 0.5 + } else { + 0.0 + }; + (wx, lat_frac * (std::f64::consts::PI * r * 1000.0)) + } + _ => (px * scale::DISTRICT_M as f64, py * scale::DISTRICT_M as f64), + } +} + // --------------------------------------------------------------------------- // Public derivation function // --------------------------------------------------------------------------- @@ -1075,43 +1231,27 @@ pub fn derive_district_profile( let h = ta.h; let gcpr = grid_cells_per_district.max(1); - // Compute aggregate terrain statistics over the cells in this district. - // All arithmetic is integer or quantized-integer (D-010). - let mut slope_sum: i64 = 0; - let mut elev_sum: i64 = 0; - let mut ocean_count: i64 = 0; - let mut cell_count: i64 = 0; - + // T-1125: the batch path runs the SAME invention as the on-demand path — an + // invented centre-point sample of the continuous field (warped coastline + + // slope-independent scatter via `invent_primitives`) instead of the former + // scatter-free cell-aggregate mean. One invention path, shared with + // [`derive_district`], so the two can never silently diverge again; the + // pseudo-grid samples the same truth the 2 km carrier does. (The former mean + // smoothed away exactly the variance the believability contrast metrics + // measure — and carried no invention at all.) let row_start = (ry as usize).saturating_mul(gcpr).min(h); let row_end = row_start.saturating_add(gcpr).min(h); let col_start = (rx as usize).saturating_mul(gcpr).min(w); let col_end = col_start.saturating_add(gcpr).min(w); - - for r in row_start..row_end { - for c in col_start..col_end { - let i = r * w + c; - // Quantize slope to 0–100: slope_deg is ~[0, 45°]; divide by 45 × 100. - // This is a float-to-int boundary inside the aggregation; downstream - // decisions use `slope_q` (integer). - slope_sum += ((ta.slope_deg[i] / 45.0).clamp(0.0, 1.0) * 100.0) as i64; - elev_sum += (ta.elev_pct[i] * 100.0) as i64; - ocean_count += ta.ocean_mask[i] as i64; - cell_count += 1; - } - } - - let (slope_q, elev_q, ocean_fraction_q) = if cell_count > 0 { - let sq = (slope_sum / cell_count) as i32; - let eq = (elev_sum / cell_count) as i32; - let oq = (ocean_count * 100 / cell_count) as i32; - (sq, eq, oq) - } else { - (0, 0, 0) - }; + let px = (col_start as f64 + col_end.saturating_sub(1).max(col_start) as f64) / 2.0; + let py = (row_start as f64 + row_end.saturating_sub(1).max(row_start) as f64) / 2.0; + let (world_x_m, world_y_m) = pixel_to_world_m(px, py, w, h, body_params.body_radius_km); // D-243 §3/§4: compute the edge-fuzz-blended region baseline for this district, // then pass it through build_district_profile so the two-phase derivation path runs. // The warp uses `seed.seed()` (the body-scoped seed) for domain separation. + // Hoisted above the primitives (T-1125): the invention's driver tier needs + // the baseline for its one-step-stale climate estimate. let region_baseline_c = region_profile::region_baseline_at_district( seed.seed(), body_id, @@ -1122,13 +1262,25 @@ pub fn derive_district_profile( Some(region_cache), ); + let prims = invent_primitives( + seed, + body_params, + climate, + ta, + px, + py, + world_x_m, + world_y_m, + region_baseline_c, + ); + build_district_profile( seed, body_params, climate, - slope_q, - elev_q, - ocean_fraction_q, + prims.slope_q, + prims.elev_q, + prims.ocean_fraction_q, region_baseline_c, basin_direction, ) @@ -1200,11 +1352,8 @@ fn build_district_profile( let glaciation_grade = derive_glaciation_grade_from_climate(temperature_c, moisture_q); let river_threshold = derive_river_threshold(tectonic_class, precipitation_class); - // Vegetation class (T-1025, D-239 §8). No riparian signal at district scale yet - // (requires perennial waterway map from L2+); default to false for now. - // L2 ChunkContext will override per-tile once drainage data is threaded through. - let vegetation_class = derive_vegetation(temperature_c, moisture_q, elev_q, false); - + // Morphology precedes vegetation (T-1126): the vegetation call consumes the + // morphology water verdict, so the two can never disagree by construction. let morphology_zone = derive_morphology_zone( tectonic_class, glaciation_grade, @@ -1214,6 +1363,16 @@ fn build_district_profile( moisture_q, ); + // Vegetation class (T-1025, D-239 §8). No riparian signal at district scale yet + // (requires perennial waterway map from L2+); default to false for now. + // L2 ChunkContext will override per-tile once drainage data is threaded through. + // open_water = the morphology verdict (T-1126) — never a threshold of its own. + let open_water = matches!( + morphology_zone, + MorphologyZone::OpenOcean | MorphologyZone::Lake + ); + let vegetation_class = derive_vegetation(temperature_c, moisture_q, elev_q, false, open_water); + DistrictProfile { morphology_zone, tectonic_class, @@ -1291,32 +1450,6 @@ pub fn derive_district( } }; - // Bilinear-interpolated Layer-1 envelope at the district position. - let elev_pct = bilinear(&ta.elev_pct, ta.w, ta.h, px, py) as f64; - let slope_deg = bilinear(&ta.slope_deg, ta.w, ta.h, px, py) as f64; - let ocean_frac = bilinear_bool(&ta.ocean_mask, ta.w, ta.h, px, py) as f64; - - // Envelope + adaptive ruggedness from the local heightmap slope (steep - // heightmap ⇒ relief headroom ⇒ rugged). Both 0–1. - let local_slope = (slope_deg / 45.0).clamp(0.0, 1.0); - let envelope = local_slope; - let ruggedness = local_slope; - let scatter = crate::atlas::detail_scatter::terrain_detail( - seed.seed(), - world_x_m, - world_y_m, - envelope, - ruggedness, - ); - - // Compose the primitives. Scatter perturbs elevation (mid-scale relief); slope - // gains a ruggedness-weighted bump so the morphology classifier responds - // (adaptive: rugged highs → mountain/pass, flats → plains). - let elev_q = (((elev_pct + scatter) * 100.0).round() as i32).clamp(0, 100); - let slope_q = - (((local_slope + ruggedness * scatter.abs()) * 100.0).round() as i32).clamp(0, 100); - let ocean_fraction_q = ((ocean_frac * 100.0).round() as i32).clamp(0, 100); - let params = BodyParams { latitude_deg: lat_deg, ..body_params.clone() @@ -1326,6 +1459,8 @@ pub fn derive_district( // this district. No pre-built cache here — the on-demand path derives the four // surrounding region baselines directly. Pure, deterministic, cheap. // `seed.seed()` (the body-scoped seed value) ensures body-unique warp separation. + // Hoisted above the primitives (T-1125): the invention's driver tier needs + // the baseline for its one-step-stale climate estimate. let region_baseline_c = region_profile::region_baseline_at_district( seed.seed(), body_id, @@ -1336,6 +1471,21 @@ pub fn derive_district( None, // no pre-built cache; derive on-the-fly ); + // T-1125: invented primitives — warped coastline (invented bays/capes) + + // slope-independent character-driven scatter. Shared with the batch path + // (`derive_district_profile`) via `invent_primitives`. + let prims = invent_primitives( + seed, + ¶ms, + climate, + ta, + px, + py, + world_x_m, + world_y_m, + region_baseline_c, + ); + // derive_district is the on-demand path (arbitrary DistrictPos, no L1 working // grid). basin_direction is an ACCEPTED LIMITATION here: it defaults to North // (a fallback, not a computed value) because the D8 thalweg is only available @@ -1347,9 +1497,9 @@ pub fn derive_district( seed, ¶ms, climate, - slope_q, - elev_q, - ocean_fraction_q, + prims.slope_q, + prims.elev_q, + prims.ocean_fraction_q, region_baseline_c, BasinDirection::default(), ) @@ -1647,9 +1797,14 @@ mod tests { } #[test] - fn derive_district_flat_envelope_invents_no_relief() { - // A perfectly flat heightmap (no slope) → envelope 0 → scatter invents - // nothing → elev_q equals the interpolated base (the envelope rule). + fn derive_district_flat_envelope_bounded_invention() { + // Pre-T-1125 this pinned "flat heightmap → ZERO invented relief" — the + // envelope-as-amplitude reading that collapsed the D-227 invention on + // exactly the low-relief coasts where coastlines live. T-1125 (Jeroen's + // crinkle-varies ruling) replaces the zero with a bounded FLOOR: a flat + // world gains gentle invented relief that varies from place to place, + // never mountains, and the invention cannot conjure water that is not + // in the ocean mask. let (w, h) = (64u32, 32u32); let flat = BodyHeightmap { body_id: "flat".into(), @@ -1661,13 +1816,27 @@ mod tests { let ta = test_ta(&flat); let climate = ClimateConstants::default(); let p = earth_params(); - let d = derive_district(test_seed(), "flat", &p, &ta, (500, 100), &climate); - // Flat land everywhere → slope 0 → no invented relief, no ocean. - assert_eq!( - d.slope_q, 0, - "flat heightmap must yield zero district slope" + let a = derive_district(test_seed(), "flat", &p, &ta, (500, 100), &climate); + let b = derive_district(test_seed(), "flat", &p, &ta, (900, 40), &climate); + // No ocean can be invented on a fully-landlocked flat world: the coast + // warp displaces sampling, it never fabricates mask content. + assert_eq!(a.ocean_fraction_q, 0, "above sea level → no ocean"); + assert_eq!(b.ocean_fraction_q, 0, "above sea level → no ocean"); + // Bounded invention: gentle relief only — an authored plain must never + // sprout mountain-grade slopes (the envelope rule survives as a ceiling). + assert!(a.slope_q <= 25, "invented slope too steep: {}", a.slope_q); + assert!(b.slope_q <= 25, "invented slope too steep: {}", b.slope_q); + // And the invention must actually vary between distant districts — the + // old zero rule left every flat district identical (the T-1125 finding). + assert!( + a.elev_q != b.elev_q || a.slope_q != b.slope_q, + "flat-world districts must differ under the invention floor \ + (a: elev_q={} slope_q={}, b: elev_q={} slope_q={})", + a.elev_q, + a.slope_q, + b.elev_q, + b.slope_q ); - assert_eq!(d.ocean_fraction_q, 0, "above sea level → no ocean"); } #[test] @@ -2325,7 +2494,7 @@ mod tests { fn vegetation_airless_is_absent() { // D-239 §2: airless body → vegetation branch absent. assert_eq!( - derive_vegetation(None, 50, 20, false), + derive_vegetation(None, 50, 20, false, false), VegetationClass::Absent ); } @@ -2334,7 +2503,7 @@ mod tests { fn vegetation_warm_moist_low_elevation_is_forest() { // Warm + moist + low elevation → Forest. assert_eq!( - derive_vegetation(Some(20.0), 60, 10, false), + derive_vegetation(Some(20.0), 60, 10, false, false), VegetationClass::Forest ); } @@ -2343,7 +2512,7 @@ mod tests { fn vegetation_high_elevation_is_barren() { // Warm + moist but very high elevation → Barren (above treeline). assert_eq!( - derive_vegetation(Some(20.0), 60, 90, false), + derive_vegetation(Some(20.0), 60, 90, false, false), VegetationClass::Barren ); } @@ -2356,7 +2525,7 @@ mod tests { let moisture = 60_i32; let mut prev: Option = None; for elev_q in (0..=100).step_by(5) { - let v = derive_vegetation(temp, moisture, elev_q, false); + let v = derive_vegetation(temp, moisture, elev_q, false, false); if let Some(p) = prev { // Absent not reachable here (has atmosphere); skip riparian variants. let is_base = matches!( @@ -2383,14 +2552,14 @@ mod tests { #[test] fn vegetation_riparian_upgrades_scrub_to_riparian_scrub() { // A scrub-zone district near perennial water → RiparianScrub. - let v = derive_vegetation(Some(20.0), 60, 85, true); // high elev = scrub zone + let v = derive_vegetation(Some(20.0), 60, 85, true, false); // high elev = scrub zone assert_eq!(v, VegetationClass::RiparianScrub); } #[test] fn vegetation_riparian_upgrades_forest_to_riparian_thicket() { // A forest-zone district near perennial water → RiparianThicket. - let v = derive_vegetation(Some(20.0), 60, 10, true); // low elev = forest zone + let v = derive_vegetation(Some(20.0), 60, 10, true, false); // low elev = forest zone assert_eq!(v, VegetationClass::RiparianThicket); } @@ -2403,13 +2572,37 @@ mod tests { assert_eq!(VegetationClass::Forest as u8, 3); assert_eq!(VegetationClass::RiparianScrub as u8, 4); assert_eq!(VegetationClass::RiparianThicket as u8, 5); + // T-1126: Marine appended (open-water verdict). Ord position is + // NON-SEMANTIC — the density ladder does not extend to Marine. + assert_eq!(VegetationClass::Marine as u8, 6); + } + + #[test] + fn vegetation_open_water_is_marine_and_airless_wins() { + // T-1126: the morphology water verdict → Marine… + assert_eq!( + derive_vegetation(Some(15.0), 80, 5, false, true), + VegetationClass::Marine + ); + // …but airless takes precedence: no climate/vegetation branch at all + // (D-239 §2) — an airless sea is Absent, not Marine. + assert_eq!( + derive_vegetation(None, 80, 5, false, true), + VegetationClass::Absent + ); + // The riparian flag is river-proximity, unrelated to open water: a + // riparian river district on land is NOT Marine. + assert_eq!( + derive_vegetation(Some(15.0), 80, 5, true, false), + VegetationClass::RiparianThicket + ); } #[test] fn vegetation_hyper_arid_is_barren_not_absent() { // With atmosphere but zero moisture → Barren (not Absent). assert_eq!( - derive_vegetation(Some(20.0), 0, 10, false), + derive_vegetation(Some(20.0), 0, 10, false, false), VegetationClass::Barren ); } diff --git a/server/src/atlas/mod.rs b/server/src/atlas/mod.rs index b75751b90..7b3e206cd 100644 --- a/server/src/atlas/mod.rs +++ b/server/src/atlas/mod.rs @@ -12,6 +12,7 @@ pub mod body_world_state; pub mod cascade; pub mod chunk_context; pub mod city_context_reader; +pub mod coast_invention; pub mod detail_scatter; pub mod district_mix; pub mod district_profile; diff --git a/server/src/atlas/voxel.rs b/server/src/atlas/voxel.rs index 36da38804..3240d7ecf 100644 --- a/server/src/atlas/voxel.rs +++ b/server/src/atlas/voxel.rs @@ -160,6 +160,11 @@ impl Vegetation { VegetationClass::Forest => Vegetation::Forest, VegetationClass::RiparianScrub => Vegetation::Thicket, VegetationClass::RiparianThicket => Vegetation::Thicket, + // T-1126: open-water districts route to the WaterBody generator + // upstream (D-239/T-1082) and never reach the land-vegetation + // baseline; the arm exists for exhaustiveness and maps to bare + // ground exactly like Absent if one ever slips through. + VegetationClass::Marine => Vegetation::Barren, } } } -- 2.54.0 From 6aafa7758359e768ef77000e88700fa8f0e4637a Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Fri, 17 Jul 2026 08:21:27 +0200 Subject: [PATCH 3/5] feat(simulation): T-1127 glaciation ice tint + Marine color in zoom-ladder probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit aliveness_probe --render gains a 6th glaciation panel; glaciated districts get an ice tint keyed on GlaciationGrade, and Marine vegetation renders [0,96,172] — distinct from the morphology water tones so district-carrier vs voxel-generator disagreement stays visible in ladders. Co-Authored-By: Claude Fable 5 --- server/src/bin/aliveness_probe.rs | 65 ++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/server/src/bin/aliveness_probe.rs b/server/src/bin/aliveness_probe.rs index 74d5e1cad..d073364c5 100644 --- a/server/src/bin/aliveness_probe.rs +++ b/server/src/bin/aliveness_probe.rs @@ -37,7 +37,8 @@ use settled_reach_server::atlas::believability::{ use settled_reach_server::atlas::body_world_state::BodyWorldState; use settled_reach_server::atlas::chunk_context::derive_chunk_context; use settled_reach_server::atlas::district_profile::{ - derive_district, BodyParams, ClimateConstants, DistrictProfile, VegetationClass, + derive_district, BodyParams, ClimateConstants, DistrictProfile, GlaciationGrade, + VegetationClass, }; use settled_reach_server::atlas::features::TerrainAnalysis; use settled_reach_server::atlas::scale::{ @@ -335,8 +336,17 @@ const MORPHOLOGY_RGB: [[u8; 3]; 17] = [ [64, 115, 102], // 16 Wetland ]; -/// The five attribute panels, in output order. -const PANEL_NAMES: [&str; 5] = ["morphology", "elev", "temp", "moisture", "veg"]; +/// The attribute panels, in output order. `glaciation` is T-1127's dedicated +/// panel; the morphology panel additionally carries a per-pixel ice tint keyed +/// on the same field (see [`apply_ice_tint`]). +const PANEL_NAMES: [&str; 6] = [ + "morphology", + "elev", + "temp", + "moisture", + "veg", + "glaciation", +]; /// Render the district window around the principal settlement and write one PNG /// per panel to `out_dir`, plus a one-line manifest per image and a determinism @@ -447,7 +457,7 @@ fn render_window_panels( ) -> Vec<(&'static str, Vec)> { let n_i = n as i32; let half = n_i / 2; - let mut bufs: [Vec; 5] = std::array::from_fn(|_| vec![0u8; (n * n) as usize * 3]); + let mut bufs: [Vec; 6] = std::array::from_fn(|_| vec![0u8; (n * n) as usize * 3]); for row in 0..n_i { for col in 0..n_i { // Row 0 = northmost: smaller dy = further north (derive_district maps @@ -455,11 +465,19 @@ fn render_window_panels( let dp = (centre.0 - half + col, centre.1 - half + row); let prof = derive_district(seed, body_id, params, ta, dp, climate); let i = ((row * n_i + col) * 3) as usize; - set_rgb(&mut bufs[0], i, morphology_rgb(prof.morphology_zone)); + // T-1127: the morphology color carries a per-pixel ice tint keyed on + // glaciation_grade (a render modifier — the frozen D-239 §6 zone + // vocabulary is untouched; tint = classifier-tuning territory). + set_rgb( + &mut bufs[0], + i, + apply_ice_tint(morphology_rgb(prof.morphology_zone), prof.glaciation_grade), + ); set_rgb(&mut bufs[1], i, gray_rgb(prof.elev_q)); set_rgb(&mut bufs[2], i, temperature_rgb(prof.temperature_c)); set_rgb(&mut bufs[3], i, moisture_rgb(prof.moisture_q)); set_rgb(&mut bufs[4], i, vegetation_rgb(prof.vegetation_class)); + set_rgb(&mut bufs[5], i, glaciation_rgb(prof.glaciation_grade)); } } for buf in &mut bufs { @@ -566,6 +584,43 @@ fn vegetation_rgb(v: VegetationClass) -> [u8; 3] { VegetationClass::Forest => [30, 110, 40], // closed canopy VegetationClass::RiparianScrub => [70, 170, 120], // waterway band (open) VegetationClass::RiparianThicket => [10, 130, 90], // waterway band (dense) + // T-1126: open water — an ocean blue deliberately DISTINCT from the + // morphology water tones (OpenOcean 26,51,115 / Lake 51,102,166) so a + // veg-panel Marine cell is never mistaken for a morphology overlay. + VegetationClass::Marine => [0, 96, 172], + } +} + +/// T-1127: blend the morphology color toward glacial ice-white by +/// `glaciation_grade` — a RENDER modifier only (the D-239 §6 frozen 17-zone +/// vocabulary is untouched; an actual ice zone would need its own D-amendment). +/// +/// `Light` deliberately does NOT tint: grade 1 is glacial-erosion *signatures* +/// (U-valleys, moraines — landform history on coasts as warm as +5 °C mean), +/// not ice cover; visible ice starts where D-239 §5 gates glacial forms, +/// grade ≥ Moderate. The dedicated glaciation panel still shows Light. +fn apply_ice_tint(base: [u8; 3], grade: GlaciationGrade) -> [u8; 3] { + let t = match grade { + GlaciationGrade::None | GlaciationGrade::Light => return base, + GlaciationGrade::Moderate => 0.30, + GlaciationGrade::Heavy => 0.50, + GlaciationGrade::IceCap => 0.70, + }; + lerp_rgb( + [base[0] as f32, base[1] as f32, base[2] as f32], + [228.0, 240.0, 250.0], // glacial ice white-blue + t, + ) +} + +/// T-1127: dedicated glaciation panel — categorical dark→ice ramp. +fn glaciation_rgb(grade: GlaciationGrade) -> [u8; 3] { + match grade { + GlaciationGrade::None => [30, 30, 30], + GlaciationGrade::Light => [96, 120, 150], + GlaciationGrade::Moderate => [150, 180, 210], + GlaciationGrade::Heavy => [200, 222, 240], + GlaciationGrade::IceCap => [240, 248, 255], } } -- 2.54.0 From aa1471069b0a4b62f64ad6de83ca37a77cb2d5ec Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Fri, 17 Jul 2026 08:21:57 +0200 Subject: [PATCH 4/5] test(simulation): derivation-harness call sites + believability golden regen for coastline invention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mechanical call-site updates (7) for the invent_primitives signature; believability golden regenerated via UPDATE_GOLDEN=1. Deltas are deliberate: slope_q/elev_q spreads widen, vegetation_classes +1 (Marine), water-districts-wet coherence reaches 46/46 and 19/19 (T-1082 class closed — classification and coastline are the same line). Reductions (ocean_fraction distinct, morphology_zones on GJ244Ad) are the eager pseudo-grid point-sampling coasts honestly instead of area-averaging them into synthetic intermediates; believability coast-sampling resolution is Q-123 calibration territory. All 37 derivation-harness law checks unchanged. Co-Authored-By: Claude Fable 5 --- server/tests/derivation_harness.rs | 14 ++++---- server/tests/golden/believability.json | 48 +++++++++++++------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/server/tests/derivation_harness.rs b/server/tests/derivation_harness.rs index 582483b2c..e409687da 100644 --- a/server/tests/derivation_harness.rs +++ b/server/tests/derivation_harness.rs @@ -841,7 +841,7 @@ fn law_climate_vegetation_no_skip_temperature_sweep() { let mut last: Option = None; for t_i in (-60i32..=30).rev().step_by(5) { let temp = Some(t_i as f32); - let vc = derive_vegetation(temp, moisture_q, elev_q, false); + let vc = derive_vegetation(temp, moisture_q, elev_q, false, false); if let Some(prev) = last { if prev == VegetationClass::Forest && vc == VegetationClass::Barren { panic!( @@ -861,7 +861,7 @@ fn law_climate_vegetation_no_skip_elevation_sweep() { let moisture_q = 55; let mut last: Option = None; for elev_q in (0i32..=100).step_by(5) { - let vc = derive_vegetation(temp, moisture_q, elev_q, false); + let vc = derive_vegetation(temp, moisture_q, elev_q, false, false); if let Some(prev) = last { if prev == VegetationClass::Forest && vc == VegetationClass::Barren { panic!( @@ -898,7 +898,7 @@ fn law_climate_vegetation_full_grid_no_skip() { for elev_q in (0i32..=100).step_by(10) { let mut last: Option = None; for &temp in &temps_desc { - let vc = derive_vegetation(temp, moisture_q, elev_q, false); + let vc = derive_vegetation(temp, moisture_q, elev_q, false, false); if let Some(prev) = last { if prev == VegetationClass::Forest && vc == VegetationClass::Barren { panic!( @@ -919,7 +919,7 @@ fn law_climate_vegetation_riparian_near_perennial_water() { // near_perennial_water=true must produce a Riparian variant in viable zones. // Forest zone → RiparianThicket. - let vc_forest = derive_vegetation(Some(18.0), 60, 20, true); + let vc_forest = derive_vegetation(Some(18.0), 60, 20, true, false); assert!( matches!( vc_forest, @@ -930,7 +930,7 @@ fn law_climate_vegetation_riparian_near_perennial_water() { ); // Scrub zone → RiparianScrub. - let vc_scrub = derive_vegetation(Some(5.0), 30, 50, true); + let vc_scrub = derive_vegetation(Some(5.0), 30, 50, true, false); assert!( matches!( vc_scrub, @@ -941,7 +941,7 @@ fn law_climate_vegetation_riparian_near_perennial_water() { ); // Hyper-arid Barren zone + perennial water → RiparianScrub oasis (D-239 §8). - let vc_arid = derive_vegetation(Some(20.0), 3, 10, true); + let vc_arid = derive_vegetation(Some(20.0), 3, 10, true, false); assert_eq!( vc_arid, VegetationClass::RiparianScrub, @@ -957,7 +957,7 @@ fn law_climate_vegetation_airless_always_absent() { for moisture_q in [0, 30, 70, 100] { for elev_q in [0, 50, 100] { for near_water in [false, true] { - let vc = derive_vegetation(None, moisture_q, elev_q, near_water); + let vc = derive_vegetation(None, moisture_q, elev_q, near_water, false); assert_eq!( vc, VegetationClass::Absent, diff --git a/server/tests/golden/believability.json b/server/tests/golden/believability.json index da2796ca3..0e2f02e82 100644 --- a/server/tests/golden/believability.json +++ b/server/tests/golden/believability.json @@ -6,38 +6,38 @@ "voxel_sampled_districts": 64, "contrast": { "moisture_q": { - "min": 32, + "min": 31, "max": 80, - "distinct": 49 + "distinct": 48 }, "elev_q": { "min": 0, - "max": 98, - "distinct": 99 + "max": 100, + "distinct": 101 }, "slope_q": { "min": 0, - "max": 13, - "distinct": 14 + "max": 30, + "distinct": 30 }, "ocean_fraction_q": { "min": 0, "max": 100, - "distinct": 65 + "distinct": 44 }, "morphology_zones": 9, - "vegetation_classes": 3, + "vegetation_classes": 4, "terrain_materials": 4, - "voxel_relief_m": 20, + "voxel_relief_m": 24, "micro_habitat_distinct": 2 }, "coherence": { - "water_districts": 49, - "water_districts_wet": 44, + "water_districts": 46, + "water_districts_wet": 46, "drainage_samples": 0, "drainage_monotonic": 0, "vegetation_samples": 64, - "vegetated_districts": 8 + "vegetated_districts": 7 } }, { @@ -49,32 +49,32 @@ "moisture_q": { "min": 0, "max": 20, - "distinct": 21 + "distinct": 20 }, "elev_q": { "min": 0, - "max": 99, - "distinct": 100 + "max": 100, + "distinct": 101 }, "slope_q": { "min": 0, - "max": 6, - "distinct": 7 + "max": 15, + "distinct": 14 }, "ocean_fraction_q": { "min": 0, "max": 100, - "distinct": 65 + "distinct": 38 }, - "morphology_zones": 9, - "vegetation_classes": 1, - "terrain_materials": 4, - "voxel_relief_m": 19, + "morphology_zones": 6, + "vegetation_classes": 2, + "terrain_materials": 2, + "voxel_relief_m": 21, "micro_habitat_distinct": 0 }, "coherence": { - "water_districts": 25, - "water_districts_wet": 22, + "water_districts": 19, + "water_districts_wet": 19, "drainage_samples": 0, "drainage_monotonic": 0, "vegetation_samples": 64, -- 2.54.0 From a86dab37567bd549c5d2060b25cb44921a3a79c8 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Fri, 17 Jul 2026 08:22:20 +0200 Subject: [PATCH 5/5] docs(meta): D-227 T-1125 invention-character amendment + D-239 T-1126 Marine amendment D-227: records the two-tier driver model (body envelope from authored params only, D-240 tilt exclusion upheld; position tier with fjord/ latitude/wetness/heterogeneity), the shared invent_primitives path, the scatter floor-vs-ceiling reading, shoreline carving, and the one-step-stale circularity ruling. D-239: Marine appended at discriminant 6, morphology-derived verdict, airless precedence, non-semantic Ord position. Co-Authored-By: Claude Fable 5 --- governance/decisions/architecture.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/governance/decisions/architecture.md b/governance/decisions/architecture.md index 6af6617a0..4ec699bc5 100644 --- a/governance/decisions/architecture.md +++ b/governance/decisions/architecture.md @@ -1618,6 +1618,7 @@ Technical foundation decisions that constrain implementation: engine, client-ser - **Rationale:** A world that stores its tiles cannot scale to body-sized 3-D volumes and bloats saves; a pure-function world with a transient cache + a sparse mutator log scales to any size, makes saves trivially small, and is the only model under which "dig anywhere, to any depth" is free (the subsurface was always computable — digging just reveals it). It also forces the determinism discipline the whole cascade needs anyway. The downward floor cap fell because it was solving a problem — per-layer storage cost — that derive-don't-store eliminates. - **Open sub-questions:** the geology-model fidelity (simple depth-horizon stack vs tectonic-grade folding/faults) and how far `FloorMaterial` is derived now vs deferred to the city layers (both tracked in D-228 / Q-101); the mutator op schema (Q-103). - **Implementation:** Phase 4+ (epic T-750). The caching substrate exists at the atlas level (`BodyWorldStateCache`, D-203/D-225); the tile/voxel cache, mutator overlay, and geology derivation land as the cascade reaches the tile layers. No migration needed pre-save (D-202 `schema_version` lineage covers future drift once saves exist). +- **Amended 2026-07-17 (T-1125 — the invention carries geographic content into district classification; two-tier driver model):** the "invented deterministically (interpolation + domain warp + detail-scatter)" clause is now implemented *with character* at the district tier, closing the T-1123 finding that classification consumed only the raw bilinear envelope (pixel-smooth coasts; scatter amplitude slaved to coarse heightmap slope ≈ 0 exactly on low-relief coasts). Mechanism (`atlas/coast_invention.rs` + `district_profile::invent_primitives`, shared by the on-demand `derive_district` AND batch `derive_district_profile` paths so they can never silently diverge): **(a) coastline domain-warp** — every envelope field (elevation, slope, ocean mask) is bilinearly sampled at the same warp-displaced position (C¹ multi-octave value noise, ≈16–262 km band, sub-pixel amplitude cap 0.75 px, distinct salted hash stream — never correlated with the terrain scatter or climate edge-fuzz), inventing bays/capes/fjord inlets while the heightmap stays the truth at its own scale; **(b) slope-independent scatter floor** — the detail-scatter envelope gains a character-driven floor (invention no longer collapses on flat coasts; the old "flat envelope → zero invention" reading is superseded — the envelope rule survives as a *ceiling*: gentle bounded relief, never mountains on an authored plain); **(c) shoreline carving** — ridged character contributes real slope in shoreline patches so the steep coastal families (Fjord/CliffCoast) can fire where glacially/tectonically justified. **Crinkle varies (Jeroen's ruling): two driver tiers, zero new authored data.** Tier 1 (body personality envelope): `planet_class` (via `TectonicClass`) + `hydrosphere` + `atmosphere` + `body_radius_km` (via the pixel⇄metre seam) only — **D-240 stands: no orbital/tilt inputs**; erosion-proneness is *derived* (more ocean → wetter/rainier → higher erosion → smoother mature coasts; dry/thin-atmosphere → sharp young coasts). Tier 2 (position): latitude, driver-tier `GlaciationGrade` (fjordy high-latitude glaciated coasts), local wetness, and a seeded ~100–400 km heterogeneity field so stretches of the same coast differ; longitude participates via absolute world-metre noise keying. **Circularity ruling:** the driver-tier climate (glaciation/moisture) reads the *unwarped* raw-bilinear primitives — one-step-stale by design, documented at the call site. All pure `(seed, body, position)` (D-010); character never steps on a district/region line (D-243 edge-fuzz discipline). - **Raised by:** Jeroen (derive-don't-store, volumetric, drop-the-floor-cap directives) + Claude, atlas-derivation workshop, 2026-05-25. - **Cross-reference:** [D-010](#d-010) (determinism — now save-critical), [D-222](#d-222) (subtile/tile/chunk hierarchy), [D-110](#d-110) (signed z-levels), [D-225](#d-225) (layer-stream proxy + cache pattern), [D-203](#d-203) (LRU cache tier), [D-224](#d-224) (SeedChain — feeds `derive`), [D-228](#d-228) (composite tile schema — the derived value type), [Q-101](../questions/architecture.md#q-101) (refinement contract), [Q-103](../questions/architecture.md#q-103) (mutator op schema), [Q-104](../questions/architecture.md#q-104) (floor↔voxel-z mapping) - **Dissent:** None @@ -1830,6 +1831,7 @@ Technical foundation decisions that constrain implementation: engine, client-ser - **Implementation note (T-1025/T-1027, 2026-06-08):** §6's frozen 17-zone `MorphologyZone` enum is realised in `server/src/simulation/generator.rs` (`repr(u8)`, discriminant-pinned). The §5 8-family gated classifier + §7 compatibility-matrix invariants live in `derive_morphology_zone` (`region_profile.rs`). **16 of the 17 zones are reachable at RegionProfile scale; `BraidedPlain` is the exception** — distinguishing it from `Delta` needs a lithology signal (§8 Gravel→braided) that `RegionProfile` does not carry, so `BraidedPlain` is **deferred to ChunkContext** sub-classification. The §7 compatibility invariant is enforced as a **classifier-gate-ordering** property (a build-time test that a single region cannot yield a forbidden pair), per §7's "build-time test" language; genuine cross-region sharp seams (cliff↔fjord, lithology faults) remain permitted. §2 climate-derived fields (`precipitation_class`, `glaciation_grade`, `vegetation_class`) all derive from the temperature(+moisture) primitive (T-1025). - **Amendment (T-1082, 2026-06-28):** §5's family set grows from **8 to 9** — a `WaterBody` generator family is added for the water zones **OpenOcean / Lake / TidalFlat**, which previously fell through to the dry-land `AlluvialPlain` fallback (the D-245 believability bug: oceans rendered as dry forested land — `water=Dry` + `Forest` on the sea). `WaterBody` lays `Water::Deep`/`Shallow` (a Shallow shoal band on the district-anchored coast line `coast_anchor_m`, Deep beyond; TidalFlat is all-Shallow), a seabed `TerrainMaterial` (`Rock` on steep districts / `Sand` on gentle / `Wetland` for tidal mud), `Vegetation::Barren`, the water surface at `elevation_m = 0` (the §8 mouths-at-sea-level convention), and seasonal `Ice` via `derive_cover` (frozen seas). This **refines §6**: open ocean / lake / tidal flat are still derived zone *labels*, but their *generator* is now `WaterBody`, not `AlluvialPlain` — so §6's "tidal flat … not [a] distinct generator famil[y]" is superseded for the dispatch of those three zones. `Wetland` stays a land zone on `AlluvialPlain` (a marsh — saturated ground, not open water). Lives in `server/src/atlas/voxel.rs` (`MorphologyFamily::WaterBody` + `generate_water_body`, dispatched by `zone_to_family`); verified by the T-1083 believability harness (water-renders-wet flips PASS for Arbour + Edict). The §5 8-family decision-tree classifier (`derive_morphology_zone`) is unchanged — this is a voxel-tier *generator* family, selected by zone, not a new RegionProfile classifier branch. - **Amendment (T-1080, 2026-06-28):** §2's **moisture** primitive is now a **body ceiling × per-district spatial gradient**, not a single body constant. The hydrosphere+atmosphere value (`derive_moisture_q`) is the *wettest* a district on the body can be; per-district **latitude** (equator wet → pole dry), **elevation** (orographic / rain-shadow), and **continentality** (`100 − ocean_fraction_q`, coast wet → interior dry) subtract from it. Integer (D-010); coefficients in `ClimateConstants` / `climate_constants.toml` `[moisture_gradient]` (provisional, Q-123 calibrates). This mirrors D-240's latitude-graded *temperature* model and fixes the T-1080 believability bug (`moisture_q = 80` for all 2 048 districts → uniform vegetation/terrain — "nothing to fuzz"). It **propagates**: `precipitation_class`, `vegetation_class`, and the `morphology_zone` Wetland gate all read `moisture_q`, so those diversify for free. Lives in `district_profile.rs::derive_moisture_q`; verified by the T-1083 believability harness (moisture-gradient flips PASS, `distinct` 1 → ~49 on Arbour). The body-scale ceiling keeps each world's character; the gradient varies it within. +- **Amendment (T-1126, 2026-07-17):** §8's climate→vegetation law predates a water class — `derive_vegetation` was ocean-blind, so district-resolution maps painted Forest across open ocean (T-1123 finding; the voxel tier masked it in-world via the T-1082 `WaterBody` generator, but the district carrier itself disagreed with its own morphology). `VegetationClass` gains **`Marine`, appended at discriminant 6** (after `RiparianThicket = 5`; the load-bearing discriminant-pin test is extended). The verdict is **morphology-derived**: `build_district_profile` derives `morphology_zone` first and passes `open_water = (OpenOcean | Lake)` into the vegetation call — vegetation and morphology can never disagree by construction, and **no fourth ocean threshold exists**. Precedence: airless (`temperature_c == None`) still wins — an airless body has no climate/vegetation branch at all (§2), so its seas read `Absent`, not `Marine`. The `Ord` position of `Marine` is **non-semantic**: the §8 Forest→Scrub→Barren density-ladder reading does not extend to it (discriminants are append-only serialisation order). `near_perennial_water` (the riparian river-proximity signal) is unrelated and unchanged. - **Amendment (T-1081, 2026-06-28):** the voxel tier now carries **mid-scale relief** (D-243 §2's invented terrain). Before, the family generators set `elevation_m` from `elev_q` at a compressed scale plus only ±4 m per-voxel micro-scatter — the walkable surface read flat (the D-245 "0–3 m, no hills to navigate by" bug). A `voxel_relief` pass (`detail_scatter.rs`, the `terrain_detail` fBm at a **0.13–1 km sub-district octave band** — all finer than the 2 km district, so it never competes with `elev_q`'s district-scale role — rather than the district 4–40 km band; body-global `SeedDomain::VoxelRelief` seed, position-keyed; the `f64` perturbation truncated to integer metres before assignment — D-010) is added post-dispatch in `derive_voxel_column`. Two load-bearing choices: **(a) the relief envelope is `slope_q·3 + elev_q`, not slope alone** — the coarse heightmap (~40–78 km/px) yields `slope_q ≈ 0` even on high ground (observed max 13 on Arbour), so gating on slope would invent nothing; folding elevation in makes high terrain read rugged and coastal flats stay gentle. **(b) Only the flat families** (AlluvialPlain / LavaField / BraidedDelta / DuneStrand / MeanderReach) take it — CliffCoast / FjordWall / IncisedGorge already generate strong internal relief and a position-varying field would warp those features (e.g. drown a gorge's wall-to-floor drop); WaterBody stays at sea level. The relief **span** (`VOXEL_RELIEF_SPAN_M = 100`) and the believability voxel-relief threshold (≥ 8 m) are provisional (Q-123 calibrates, like the moisture coefficients). The span is held modest deliberately: relief sits on the compressed `elev_q/N` base (max ~50 m) and `elevation_m` clamps at 0 (sea level), so an oversized span clamps away on low ground (drowning the relief, biasing it positive) — a bigger span belongs with the deferred absolute-elevation model. Verified by the T-1083 harness — a new `voxel_relief` contrast metric (mean within-district elevation range across a district-spanning transect, since a single 64 m sample chunk is narrower than the relief band) flips PASS (≈22 m on Arbour + Edict). **Deferred:** the *absolute* elevation span is still the compressed `elev_q/N` base — a per-body hypsometric relief model (so a body's true max relief sets the ceiling AND gives the relief headroom to grow without clamping) is a later refinement; the mid-scale relief gives navigable hills now without it. - **Cross-reference:** [D-227](#d-227) (derive-don't-store voxel model), [D-228](#d-228) (composite tile axes / cohesion / seasonal state), [D-210](#d-210) (temperature proxy — formalised), [D-203](#d-203) (BodyWorldState cache), [D-206](#d-206) (background analysis pass), [D-208](#d-208) (drainage / D8), [D-010](#d-010) (determinism), [D-234](#d-234) (street/footprint geometry — consumes morphology), [D-142](content.md#d-142) (zone types), [D-217](#d-217) (tile condition), [Q-102](../questions/architecture.md#q-102) (cohesion = the warp), [Q-103](../questions/architecture.md#q-103) (mutator schema — open), [Q-105](../questions/architecture.md#q-105) (seasonal/clock state — temperature/ElevationDelta forward contract) -- 2.54.0