From a516b06de9055b64dcb4a799c52bc740d65eeaaf Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Mon, 8 Jun 2026 11:25:34 +0200 Subject: [PATCH] chore(meta): T-1023/1024/1026 done (PR #158 merged) D-239 three-carrier foundation merged to main. Review found a correctness bug (per-region elevation) + hollow determinism tests, both fixed before merge. Co-Authored-By: Claude Opus 4.8 (1M context) --- .pql/changelog/ticket_history/2026-06.sql | 3 ++ .pql/changelog/tickets/2026-06.sql | 52 +++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/.pql/changelog/ticket_history/2026-06.sql b/.pql/changelog/ticket_history/2026-06.sql index 8e0ea80fb..08d406487 100644 --- a/.pql/changelog/ticket_history/2026-06.sql +++ b/.pql/changelog/ticket_history/2026-06.sql @@ -159,3 +159,6 @@ INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1023', 'status', 'in_progress', 'review', NULL, '2026-06-08 08:29:23', '2026-06-08 08:29:23', '2026-06-08 08:29:23', NULL, '8904055c99e79e3dd82daa71213f0a52', 1) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1024', 'status', 'in_progress', 'review', NULL, '2026-06-08 08:29:23', '2026-06-08 08:29:23', '2026-06-08 08:29:23', NULL, 'be0172225d63ca2b62ae66c87c67c81c', 1) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1026', 'status', 'in_progress', 'review', NULL, '2026-06-08 08:29:23', '2026-06-08 08:29:23', '2026-06-08 08:29:23', NULL, 'fab20a3ec965caf76b345755960d34c1', 1) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1024', 'status', 'review', 'done', NULL, '2026-06-08 09:25:20', '2026-06-08 09:25:20', '2026-06-08 09:25:20', NULL, '5ca038594b689711a66c6e83026a016f', 1) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1026', 'status', 'review', 'done', NULL, '2026-06-08 09:25:20', '2026-06-08 09:25:20', '2026-06-08 09:25:20', NULL, '5ffb75de5040e8df7a1733749530e50b', 1) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1023', 'status', 'review', 'done', NULL, '2026-06-08 09:25:20', '2026-06-08 09:25:20', '2026-06-08 09:25:20', NULL, '7f59fbad04adbf04bb9277610d52c38d', 1) ON CONFLICT(hash) DO NOTHING; diff --git a/.pql/changelog/tickets/2026-06.sql b/.pql/changelog/tickets/2026-06.sql index 4815efd83..fc0b9b7ec 100644 --- a/.pql/changelog/tickets/2026-06.sql +++ b/.pql/changelog/tickets/2026-06.sql @@ -478,3 +478,55 @@ the struct so those fields can be added without churn. Cache populate hooks into the same GenCompletion::BodyAnalyzed drain arm (server/src/atlas/plugin.rs) that T-1022 just wired for L3->L4 dispatch.', 'review', 'high', NULL, 'server', 'D-239', '2026-06-07 10:33:47', '2026-06-08 08:29:23', NULL, '9b322b272dc45f834bfcb961fd4361d3', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_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 (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1032', 'task', 'T-974', 'Dispatch RegionProfile layer in production — read BodyParams from DB at AnalyzeBody enqueue', 'The D-239 carrier foundation (T-1023/1024/1026, PR #158) built RegionProfile + climate derivation and wired the cascade to run the RegionProfile layer when GenWorkItem::AnalyzeBody carries body_params. But every production enqueue site currently passes body_params: None (see layer_proxy.rs TODO), so the region/climate layer only runs in unit tests, never in production. Wire the DB read: at AnalyzeBody enqueue, load each body''s physical params (axial_tilt_deg, spectral_class, star_type, hydrosphere, atmosphere, orbital_period_days, lat/elevation) from the bodies/star_systems tables, build BodyParams, and pass Some(Box::new(..)). Then BodyWorldState.regions populates in the D-206 background pass as T-1023 intended. Mirrors the T-1022 dormant-until-dispatched pattern.', 'backlog', 'high', NULL, 'server', 'D-239', '2026-06-08 08:29:23', '2026-06-08 08:29:23', NULL, '25fc159038801859f823dc9e8e9bae63', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_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 (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1023', 'task', 'T-974', 'RegionProfile carrier (~1km) + derived gating params + BodyWorldState cache', 'First stage of the D-239 three-carrier chain. Define the RegionProfile struct (morphology_zone, tectonic_class, glaciation_grade, slope/lithology/elevation summaries, and a derived per-body-class RIVER_THRESHOLD via (hydrosphere,tectonic_activity,precipitation_class)->threshold, NOT the global 200). Pure region-derivation fn(seed,atlas,body-params,pos). Cache in BodyWorldState (D-203, ~6k regions/body) populated in the D-206 background pass so the Atlas reads zone labels without triggering voxel derivation. No authoring at this layer — all gating params derived from body params. D-239 sections 1,10. + +--- +Refinement (/whats-next 2026-06-07) — Si review: READY. + +Scope note: the existing MorphologyZone enum (server/src/simulation/generator.rs, +12 variants) does NOT need reconciling to the D-239 §6 frozen 17-zone vocabulary +in this ticket — that reconciliation is T-1027''s job (Morphology classifier — +17-zone frozen vocab + 8-family gate). T-1023 uses the existing MorphologyZone +(or a placeholder field) and does not block on the vocabulary change. + +Companion note: T-1024 will add temperature_c: Option + a moisture primitive +as fields on the RegionProfile struct defined here (see T-1024 refinement). Define +the struct so those fields can be added without churn. + +Cache populate hooks into the same GenCompletion::BodyAnalyzed drain arm +(server/src/atlas/plugin.rs) that T-1022 just wired for L3->L4 dispatch.', 'done', 'high', NULL, 'server', 'D-239', '2026-06-07 10:33:47', '2026-06-08 09:25:20', NULL, '1b7e6691e4d11507ea0caffaec425588', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_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 (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1026', 'task', 'T-974', 'Anti-squaring domain warp — stateless f64-to-voxel', 'D-239 section 4. Stateless hash-based pure fn(seed,body_id,pos) — no lookup table / thread-local cache (order-independent across threads/platforms). Keep f64 sub-metre precision to the final voxel coordinate, then truncate (a cast, NOT a comparison) to the integer voxel address -> IEEE-754-deterministic across targets. +/-8m range makes FMA-contraction ULP variance harmless (no platform guards). Cross-target determinism test. Position math only — D-010 integer discipline preserved on all material/morphology decisions.', 'done', 'high', NULL, 'server', 'D-239', '2026-06-07 10:33:47', '2026-06-08 09:25:20', NULL, '54a3fb2829c10789592dda59d6d6be45', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_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 (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1024', 'task', 'T-974', 'District climate primitives — temperature + moisture (2x2km)', 'D-239 section 2. Scalar district temperature in degrees C, NULLABLE (no atmosphere -> null -> climate branch absent; airless surface ice is geology per D-227). Derived from sun (luminosity+insolation), planet (latitude+elevation lapse+orbital/axial phase=season term, day-phase=diurnal term), atmosphere (greenhouse->base, heat-retention->diurnal-swing amplitude; thick air=small swing). Separate moisture primitive from hydrosphere. Formalises D-210''s temperature proxy. Tiles inherit district values. + +--- +Refinement (/whats-next 2026-06-07) — gaps resolved with Jeroen: + +1. Data inputs (hybrid): import axial_tilt_deg into the bodies table from the + already-authored planet-gen body-def TOMLs (via the import pipeline); derive + the rest at runtime in Rust — luminosity from spectral_class lookup, orbital + distance (AU) from orbital_period_days via Kepler''s third law + star mass. + Do NOT re-encode the full Python tuning tables in Rust beyond these. + +2. Carrier rung: temperature is carried as `temperature_c: Option` (+ a + moisture primitive) ON the T-1023 RegionProfile struct — NOT a new carrier + tier. Compute on a 2x2km climate grid; regions sample it. The "~1km vs 2km" + label is cosmetic. Needs a one-line D-239 clarification (see note below). + +3. Diurnal-swing values: reuse the tuned greenhouse offsets from + tooling/planet-gen planet_simulation.py; add a NEW small atmosphere -> + diurnal_amplitude_C table (5 classes; airless/thin large swing ~40-60C, + dense/thick small swing ~2-5C). Store these constants in a SOURCE TOML + (source-canonical, tunable without recompiling), not hardcoded in Rust. + +4. Season term: produce a MEAN-ANNUAL scalar temperature (orbital phase fixed, + e.g. equinox); carry diurnal swing as an amplitude only — NO time argument. + No clock dependency. The dynamic clock-bound seasonal branch stays deferred + to Q-105 / Phase 5. Per Jeroen: these calculations don''t need perfection; + plausible-and-present beats precise. + +Blocked-by T-1023 (temperature fields live on RegionProfile, which T-1023 defines). + +D-239 follow-up: the implementer should add a one-line clarification to D-239 +(§1/§2) recording that district temperature is a nullable field on RegionProfile +sampled from a 2km climate grid, and that climate physics inputs come via the +hybrid import/derive strategy above. Fold into the implementation PR.', 'done', 'high', NULL, 'server', 'D-239', '2026-06-07 10:33:47', '2026-06-08 09:25:20', NULL, '6fcb917433d79731b32acab023d5e802', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_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);