feat(simulation): district moisture spatial gradient (T-1080)

moisture_q was a single body constant (derive_moisture_q took only &BodyParams →
80 for every one of 2048 ocean-world districts), so vegetation/terrain/ecotones
were uniform — "nothing to fuzz" (the T-1080 believability bug).

- district_profile.rs: derive_moisture_q is now a body ceiling (hydrosphere+atmosphere)
  × per-district gradient — latitude (equator wet → pole dry), elevation (orographic/
  rain-shadow), continentality (100 − ocean_fraction_q). Integer/D-010, no new plumbing
  (elev_q/ocean_fraction_q/latitude_deg already reach build_district_profile). Mirrors
  D-240's temperature model. Coefficients in ClimateConstants + climate_constants.toml
  [moisture_gradient] (provisional; Q-123 calibrates). Tuned so a wet body stays mostly
  green with drier patches and a frozen body clamps mostly barren. + gradient unit test.
- region_profile.rs: region-tier moisture gets the region-latitude gradient (elevation/
  continentality are district-scale).
- Propagates to precipitation_class / vegetation_class / morphology_zone (all read
  moisture_q). D-239 §2 amended. Believability golden regenerated.

Probe (Arbour @ yolo): moisture_q distinct 1 → 49 (spread 0 → 48); 5/7 → 6/7 criteria.
Edict (frozen): moisture 0..20 ×21 — gradient within its low ceiling. Remaining fail
(vegetation-present) is the metric dividing by all-sampled incl. water on a 2/3-ocean
world — a Q-123 calibration concern, not a climate defect.

clippy --all-targets -D warnings clean; 1580 lib tests + harnesses pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-28 17:25:18 +02:00
co-authored by Claude Opus 4.8
parent cdc55032df
commit bb5069aa13
7 changed files with 171 additions and 23 deletions
+22
View File
@@ -2717,3 +2717,25 @@ Refinement (Si pass + user ruling, 2026-06-28) — resolves the ''direction to d
- SCOPE: Wetland zone STAYS AlluvialPlain (it is land — a marsh, not open water; only the 3 named zones get WaterBody). Transient tidal wet/dry alternation deferred to Q-105.
- DETERMINISM: integer arithmetic only (D-010); warp before the integer voxel address, as the other families.
- VERIFICATION: the T-1083 believability harness must flip ''water renders wet'' toward green for Arbour + Edict; update the golden (UPDATE_GOLDEN) to capture the improvement.', NULL, '2026-06-28 13:10:56', '2026-06-28 13:10:56.408', '2026-06-28 13:10:56.408', NULL, 'f9737eab39f048ff84b8478ca11f5215', 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 ('06FGSJTXTADJRHYEE1WTAENYAG', 'status', 'backlog', 'in_progress', NULL, '2026-06-28 13:42:27', '2026-06-28 13:42:27.248', '2026-06-28 13:42:27.248', NULL, '556cdb48b70c650c73c13cff9781817e', 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 ('06FGSJTXTADJRHYEE1WTAENYAG', 'description', 'Probe evidence (Arbour @ yolo): moisture_q=80, precip=SuperHumid, veg_class=Forest in ~every one of 2048 districts; the voxel layer then derives terrain=Wetland 100% everywhere. The derivation is LAW-CORRECT (flat + wet -> Wetland, D-239 section 8) — the defect is upstream: the district-scale climate/moisture field has near-zero cross-district variance, so there is no gradient to drive ecotones, material variety, or vegetation bands. Without gradient the ''edge-fuzzed climate'' promise is invisible (nothing to fuzz). Direction to discuss: where moisture/precip SHOULD vary (latitude, orography/rain-shadow, continentality, distance-to-water) and how to introduce believable spatial gradient into the district derivation. Believability gap, not a crash.', 'Probe evidence (Arbour @ yolo): moisture_q=80, precip=SuperHumid, veg_class=Forest in ~every one of 2048 districts; the voxel layer then derives terrain=Wetland 100% everywhere. The derivation is LAW-CORRECT (flat + wet -> Wetland, D-239 section 8) — the defect is upstream: the district-scale climate/moisture field has near-zero cross-district variance, so there is no gradient to drive ecotones, material variety, or vegetation bands. Without gradient the ''edge-fuzzed climate'' promise is invisible (nothing to fuzz). Direction to discuss: where moisture/precip SHOULD vary (latitude, orography/rain-shadow, continentality, distance-to-water) and how to introduce believable spatial gradient into the district derivation. Believability gap, not a crash.
---
Refinement (Si pass, 2026-06-28) — READY (no design fork; localized fix).
ROOT CAUSE: derive_moisture_q (district_profile.rs) takes only &BodyParams → returns a body-scale constant (ocean → 80) identical for all districts. Structurally cannot vary.
FIX: restructure derive_moisture_q to body-ceiling (from hydrosphere) modulated by per-district factors, mirroring derive_temperature_c''s shape (envelope → latitude lerp → elevation lapse → seed nudge), integer/D-010:
- LATITUDE band: equator wetter → pole drier (uses latitude_deg, already threaded for temperature).
- ELEVATION lapse: high elev_q → drier (orographic/rain-shadow).
- CONTINENTALITY: far from water → drier, via TerrainAnalysis.water_dist (Chebyshev cells-to-water, already computed in features.rs; aggregate a per-district mean in the existing derive_district_profile cell loop).
- small deterministic seed nudge (±N) for per-district variety.
Signature: derive_moisture_q(params, elev_q, ocean_fraction_q, water_dist_q). build_district_profile forwards them; derive_district_profile accumulates water_dist.
PROPAGATION: precipitation_class (derive_precipitation_class_from_climate) + vegetation_class (derive_vegetation) + morphology Wetland gate (slope_q<=5 && moisture_q>=60) all read moisture_q → diversify automatically.
TUNING: add [moisture_gradient] to server/data/climate_constants.toml + matching ClimateConstants struct defaults (kept in sync by hand until T-1032 wires TOML loading, per the existing constraint).
DETERMINISM: integer (water_dist u16, integer division, clamp 0..100). D-010 ok.
VERIFY: believability harness gate moisture_q.distinct>=3 flips FAIL→PASS; vegetation-present should rise too. Magnitude calibration is Q-123 (provisional coefficients now). Files: district_profile.rs, climate_constants.toml.', NULL, '2026-06-28 13:50:47', '2026-06-28 13:50:47.912', '2026-06-28 13:50:47.912', NULL, '47bb1324b99af91b19f50ee8f8d3825a', 2) ON CONFLICT(hash) DO NOTHING;
+22
View File
@@ -3650,3 +3650,25 @@ Refinement (Si pass + user ruling, 2026-06-28) — resolves the ''direction to d
- SCOPE: Wetland zone STAYS AlluvialPlain (it is land — a marsh, not open water; only the 3 named zones get WaterBody). Transient tidal wet/dry alternation deferred to Q-105.
- DETERMINISM: integer arithmetic only (D-010); warp before the integer voxel address, as the other families.
- VERIFICATION: the T-1083 believability harness must flip ''water renders wet'' toward green for Arbour + Edict; update the golden (UPDATE_GOLDEN) to capture the improvement.', 'in_progress', 'high', NULL, 'server', 'D-239', '2026-06-28 05:54:58.429', '2026-06-28 13:10:56.408', NULL, '8f55fc31d54598d0756308b8e6ef645c', 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 ('06FGSJTXTADJRHYEE1WTAENYAG', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'District climate fields lack spatial gradient — uniform moisture/precip/vegetation yields monotone terrain', 'Probe evidence (Arbour @ yolo): moisture_q=80, precip=SuperHumid, veg_class=Forest in ~every one of 2048 districts; the voxel layer then derives terrain=Wetland 100% everywhere. The derivation is LAW-CORRECT (flat + wet -> Wetland, D-239 section 8) — the defect is upstream: the district-scale climate/moisture field has near-zero cross-district variance, so there is no gradient to drive ecotones, material variety, or vegetation bands. Without gradient the ''edge-fuzzed climate'' promise is invisible (nothing to fuzz). Direction to discuss: where moisture/precip SHOULD vary (latitude, orography/rain-shadow, continentality, distance-to-water) and how to introduce believable spatial gradient into the district derivation. Believability gap, not a crash.', 'in_progress', 'high', NULL, 'server', 'D-239', '2026-06-28 05:54:42.258', '2026-06-28 13:42:27.248', NULL, '8daa8d5b388dbc0aa004da1f0abd970f', 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 ('06FGSJTXTADJRHYEE1WTAENYAG', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'District climate fields lack spatial gradient — uniform moisture/precip/vegetation yields monotone terrain', 'Probe evidence (Arbour @ yolo): moisture_q=80, precip=SuperHumid, veg_class=Forest in ~every one of 2048 districts; the voxel layer then derives terrain=Wetland 100% everywhere. The derivation is LAW-CORRECT (flat + wet -> Wetland, D-239 section 8) — the defect is upstream: the district-scale climate/moisture field has near-zero cross-district variance, so there is no gradient to drive ecotones, material variety, or vegetation bands. Without gradient the ''edge-fuzzed climate'' promise is invisible (nothing to fuzz). Direction to discuss: where moisture/precip SHOULD vary (latitude, orography/rain-shadow, continentality, distance-to-water) and how to introduce believable spatial gradient into the district derivation. Believability gap, not a crash.
---
Refinement (Si pass, 2026-06-28) — READY (no design fork; localized fix).
ROOT CAUSE: derive_moisture_q (district_profile.rs) takes only &BodyParams → returns a body-scale constant (ocean → 80) identical for all districts. Structurally cannot vary.
FIX: restructure derive_moisture_q to body-ceiling (from hydrosphere) modulated by per-district factors, mirroring derive_temperature_c''s shape (envelope → latitude lerp → elevation lapse → seed nudge), integer/D-010:
- LATITUDE band: equator wetter → pole drier (uses latitude_deg, already threaded for temperature).
- ELEVATION lapse: high elev_q → drier (orographic/rain-shadow).
- CONTINENTALITY: far from water → drier, via TerrainAnalysis.water_dist (Chebyshev cells-to-water, already computed in features.rs; aggregate a per-district mean in the existing derive_district_profile cell loop).
- small deterministic seed nudge (±N) for per-district variety.
Signature: derive_moisture_q(params, elev_q, ocean_fraction_q, water_dist_q). build_district_profile forwards them; derive_district_profile accumulates water_dist.
PROPAGATION: precipitation_class (derive_precipitation_class_from_climate) + vegetation_class (derive_vegetation) + morphology Wetland gate (slope_q<=5 && moisture_q>=60) all read moisture_q → diversify automatically.
TUNING: add [moisture_gradient] to server/data/climate_constants.toml + matching ClimateConstants struct defaults (kept in sync by hand until T-1032 wires TOML loading, per the existing constraint).
DETERMINISM: integer (water_dist u16, integer division, clamp 0..100). D-010 ok.
VERIFY: believability harness gate moisture_q.distinct>=3 flips FAIL→PASS; vegetation-present should rise too. Magnitude calibration is Q-123 (provisional coefficients now). Files: district_profile.rs, climate_constants.toml.', 'in_progress', 'high', NULL, 'server', 'D-239', '2026-06-28 05:54:42.258', '2026-06-28 13:50:47.912', NULL, '4f5ee94f5fd8f5e447968619a520f915', 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);