diff --git a/.pql/changelog/ticket_history/2026-06.sql b/.pql/changelog/ticket_history/2026-06.sql index 89d11bab4..8e0ea80fb 100644 --- a/.pql/changelog/ticket_history/2026-06.sql +++ b/.pql/changelog/ticket_history/2026-06.sql @@ -156,3 +156,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', 'backlog', 'in_progress', NULL, '2026-06-07 16:46:01', '2026-06-07 16:46:01', '2026-06-07 16:46:01', NULL, '69beda3a00dc991cac42f9b84a4a2e78', 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', 'backlog', 'in_progress', NULL, '2026-06-07 16:46:01', '2026-06-07 16:46:01', '2026-06-07 16:46:01', NULL, '8b2967986ce3433c4ef79e8a95a19d96', 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', 'backlog', 'in_progress', NULL, '2026-06-07 16:46:01', '2026-06-07 16:46:01', '2026-06-07 16:46:01', NULL, 'ba35702257d615d2efe0fc1e1110c857', 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', '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; diff --git a/.pql/changelog/tickets/2026-06.sql b/.pql/changelog/tickets/2026-06.sql index b4832311d..4815efd83 100644 --- a/.pql/changelog/tickets/2026-06.sql +++ b/.pql/changelog/tickets/2026-06.sql @@ -425,3 +425,56 @@ 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.', 'in_progress', 'high', NULL, 'server', 'D-239', '2026-06-07 10:33:47', '2026-06-07 16:46:01', NULL, 'cbef65fab33ff3bc14e65881ac77fbda', 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.', 'review', 'high', NULL, 'server', 'D-239', '2026-06-07 10:33:47', '2026-06-08 08:29:23', NULL, '16f4043c5545bbaa377f7a76ac66d41b', 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.', 'review', 'high', NULL, 'server', 'D-239', '2026-06-07 10:33:47', '2026-06-08 08:29:23', NULL, '1c8f039bf50d810b3126d1cdc506df02', 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.', '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);