feat(assets): biome and terrain as two maps, and the bake that writes them (T-1295)
D-258 rulings 5 and 6: the pre-seed biome input is class ids only, on two maps, because compute_biome was already computing them apart and throwing one away. It gave every land pixel its Whittaker (climate) class, then painted terrain over it (ocean bands, ice, altitude snow, mountain rock, lava and ash, the dry, lunar and ferric ground). planet_simulation: - compute_biome_layers returns (biome, terrain). Each rule declares the layer it writes, by RULE not by class: ice from the cold climate box is biome, altitude snow is terrain with the lowland biome kept beneath. Rules that place life (thermophiles, mats, crust, the oasis rings) write the biome and clear the terrain under them. - 255 means "nothing on this layer", not 0, because class 0 is ocean_deep, itself terrain. Ids stay literal. - compute_biome is now compose_layers() of the pair, so the renderers are untouched. Proven byte-identical, dtype included, on 65 real bodies: every 8th standard-atmosphere body plus every thin, thick, reducing and trace one, captured before the edit and compared after it. - simulate() carries both maps as biome_layer / terrain_layer. reach atlas planet bake-biome [--body --limit --dry-run --check]: - Writes biomemap.png + terrainmap.png (8-bit, 1024x512, tEXt: layer, none, classes, decision) beside each heightmap. Scope is the heightmap bake set with an atmosphere (257 bodies; airless are deferred, per D-258). - Pre-seed by construction: simulate() is keyed on the body frontmatter's seed, and no world seed is accepted anywhere. - --check re-simulates and compares DECODED PIXELS, never bytes, since PNG encoding drifts across Pillow/zlib versions (T-1291) and a check that trips on that gets muted. Mutation-proven: one flipped pixel exits 1 and names the file; the restored file exits 0. - import_heightmaps' body lookup is extracted as body_def_for and shared, not copied. Tests (make test-tooling): rock keeps its biome beneath, thin-atmosphere ground is terrain with only scattered life, water is terrain only, no pixel is empty on both maps, and stacking equals the rendered grid. Routing rock through the biome map fails two of them by name. The router drift test covers the new verb. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1312,3 +1312,117 @@ Scope:
|
||||
Blocked by T-1295. Unblocks T-1296 and T-1297.
|
||||
|
||||
RE-SHAPED 2026-09-25 (D-258 amendment "ids, not fields"). Load TWO id maps, biomemap.png and terrainmap.png, and keep per-class fractions for each at the 512x256 downsample. There are no baked temperature or moisture fields. Class properties come from the biome and terrain tables in systems.db.', NULL, '2026-09-25 12:03:26', '2026-09-25 12:03:26.043', '2026-09-25 12:03:26.043', NULL, '239084659c268d3f9ca24dd6b859f517', 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 ('06GDGD2G9K7C8SPH57RCNB8V8C', 'description', 'D-258 amendment 2026-09-25, rulings 1-4. Bake, per body, the Python planet simulator''s biome class grid TOGETHER WITH the absolute temperature (kelvin, before the display normalisation in simulate()) and moisture [0,1] it was classified from. One model supplies all three, so they agree by construction.
|
||||
|
||||
PRE-SEED: simulate() is keyed on the body frontmatter''s seed only. The world seed must never enter this bake. Worlds stay recognisable across playthroughs; the ground varies server-side.
|
||||
|
||||
MEASURE FIRST, then choose the format. The server works at 512x256 (GRID_W/GRID_H downsample), so a 1024x512 bake may be twice the resolution the server can use on each axis. Temperature and moisture are smooth fields that compress far worse than a class grid. Across ~2,600 bodies, the repository cost of candidate formats must be measured: 8-bit class PNG, 16-bit temperature, 8-bit moisture, at 1024x512 and at 512x256. Report the numbers before committing a bake.
|
||||
|
||||
DO NOT re-render reliefmap.png. Its render drifts by a few hundred bytes (T-1291). The bake gets its own mode, a verb on reach atlas planet, that writes only the new file(s). The heightmap is byte-identical on re-simulation, so the simulator is deterministic.
|
||||
|
||||
Class ids become append-only (the gas-palette selection_order discipline in biomes.toml). Gas giants get nothing. Whatever the chosen files are, register them where the atlas and canvas registries expect: the canvas-version gate (tooling/canvas_sources.py) must see a change to them.
|
||||
|
||||
MEASURED 2026-09-25. Probe: simulate() on 24 bodies spaced evenly across the in-scope set, each field encoded as optimised PNG. The probe was deleted after the run.
|
||||
|
||||
SCOPE (Jeroen, 2026-09-25): airless bodies are out of scope for now. The format and class vocabulary must still have room for them, since classes 19-36 already cover lava, dust, ferric and lunar ground. The heightmap bake set is 267 bodies. By atmosphere: standard 231, thin 23, thick 1, reducing 1, trace 1, none 8, NULL 2. That leaves 257 bodies with an atmosphere in scope.
|
||||
|
||||
PER BODY (mean / max) AND TOTAL OVER 257 BODIES:
|
||||
biome u8, 1024x512 11.7 / 16.2 KiB 2.9 MiB
|
||||
biome u8, 512 (plurality) 5.4 / 7.5 KiB 1.4 MiB
|
||||
temp u16 0.01 K, 1024 80.8 / 352 KiB 20.3 MiB
|
||||
temp u16 0.01 K, 512 29.0 / 124 KiB 7.3 MiB
|
||||
temp u8 1 K, 1024 14.8 / 54 KiB 3.7 MiB
|
||||
temp u8 1 K, 512 6.1 / 22 KiB 1.5 MiB
|
||||
moist u8, 1024 57.2 / 80 KiB 14.4 MiB
|
||||
moist u8, 512 22.8 / 32 KiB 5.7 MiB
|
||||
packed RGB (biome,t8,m8) 1024 96.7 / 172 KiB 24.3 MiB (worse than the three separate files at the same precision: 83.7 KiB)
|
||||
|
||||
REFERENCE: the committed heightmap.png averages 678 KiB per in-scope body, and reliefmap.png 295 KiB. Together that is about 245 MiB already in the tree.
|
||||
|
||||
Bake time: simulate() takes about 6.3 s per body, so the full bake is about 27 min.
|
||||
|
||||
FINDING: the simulator''s temperature is often clamped flat. temperature_bands in biomes.toml clamps each planet class ("fiction wins over physics"). On most temperate bodies that pins the field to exactly the band, 275-305 K. On some bodies it pins it to one value: GJ204c and GJ68e are 275 K everywhere, and GJ601Ad (275-281) and GJ722c (275-280) are nearly flat. On those bodies the biome variety comes from moisture alone.
|
||||
|
||||
RE-SHAPED 2026-09-25 (D-258 amendment "ids, not fields"). This supersedes the three-field bake above.
|
||||
|
||||
Bake two u8 id maps per in-scope body (257 bodies with an atmosphere) at 1024x512:
|
||||
- biomemap.png: the Whittaker class, taken BEFORE compute_biome''s terrain overrides. It covers every land pixel and is empty on thin-atmosphere bodies.
|
||||
- terrainmap.png: the terrain override class, 0 where there is none.
|
||||
Per-pixel temperature and moisture are NOT baked.
|
||||
|
||||
Also in scope here: the class tables (biome: climate envelope, flora, cover; terrain: substrate, landform hints), authored as TOML and imported into systems.db by reach ledger import under the generator stamp.
|
||||
|
||||
Done so far: the mountain-rock units fix in compute_biome, with tooling/test_planet_biome_rules.py (in make test-tooling). The rule compared kelvin to 0.35, so it could never fire; it now uses the world''s own normalised temperature. The test is mutation-proven. Sulfuric scrub is unreachable (it converts rock at elev_norm 0.25-0.65, but rock exists only above 0.65); it is left as found, since fixing it is a design call.
|
||||
|
||||
Open: story features (the anomaly scatter, ~3% of land). Their placement and rate are for a balancing pass.', 'D-258 amendment 2026-09-25, rulings 1-4. Bake, per body, the Python planet simulator''s biome class grid TOGETHER WITH the absolute temperature (kelvin, before the display normalisation in simulate()) and moisture [0,1] it was classified from. One model supplies all three, so they agree by construction.
|
||||
|
||||
PRE-SEED: simulate() is keyed on the body frontmatter''s seed only. The world seed must never enter this bake. Worlds stay recognisable across playthroughs; the ground varies server-side.
|
||||
|
||||
MEASURE FIRST, then choose the format. The server works at 512x256 (GRID_W/GRID_H downsample), so a 1024x512 bake may be twice the resolution the server can use on each axis. Temperature and moisture are smooth fields that compress far worse than a class grid. Across ~2,600 bodies, the repository cost of candidate formats must be measured: 8-bit class PNG, 16-bit temperature, 8-bit moisture, at 1024x512 and at 512x256. Report the numbers before committing a bake.
|
||||
|
||||
DO NOT re-render reliefmap.png. Its render drifts by a few hundred bytes (T-1291). The bake gets its own mode, a verb on reach atlas planet, that writes only the new file(s). The heightmap is byte-identical on re-simulation, so the simulator is deterministic.
|
||||
|
||||
Class ids become append-only (the gas-palette selection_order discipline in biomes.toml). Gas giants get nothing. Whatever the chosen files are, register them where the atlas and canvas registries expect: the canvas-version gate (tooling/canvas_sources.py) must see a change to them.
|
||||
|
||||
MEASURED 2026-09-25. Probe: simulate() on 24 bodies spaced evenly across the in-scope set, each field encoded as optimised PNG. The probe was deleted after the run.
|
||||
|
||||
SCOPE (Jeroen, 2026-09-25): airless bodies are out of scope for now. The format and class vocabulary must still have room for them, since classes 19-36 already cover lava, dust, ferric and lunar ground. The heightmap bake set is 267 bodies. By atmosphere: standard 231, thin 23, thick 1, reducing 1, trace 1, none 8, NULL 2. That leaves 257 bodies with an atmosphere in scope.
|
||||
|
||||
PER BODY (mean / max) AND TOTAL OVER 257 BODIES:
|
||||
biome u8, 1024x512 11.7 / 16.2 KiB 2.9 MiB
|
||||
biome u8, 512 (plurality) 5.4 / 7.5 KiB 1.4 MiB
|
||||
temp u16 0.01 K, 1024 80.8 / 352 KiB 20.3 MiB
|
||||
temp u16 0.01 K, 512 29.0 / 124 KiB 7.3 MiB
|
||||
temp u8 1 K, 1024 14.8 / 54 KiB 3.7 MiB
|
||||
temp u8 1 K, 512 6.1 / 22 KiB 1.5 MiB
|
||||
moist u8, 1024 57.2 / 80 KiB 14.4 MiB
|
||||
moist u8, 512 22.8 / 32 KiB 5.7 MiB
|
||||
packed RGB (biome,t8,m8) 1024 96.7 / 172 KiB 24.3 MiB (worse than the three separate files at the same precision: 83.7 KiB)
|
||||
|
||||
REFERENCE: the committed heightmap.png averages 678 KiB per in-scope body, and reliefmap.png 295 KiB. Together that is about 245 MiB already in the tree.
|
||||
|
||||
Bake time: simulate() takes about 6.3 s per body, so the full bake is about 27 min.
|
||||
|
||||
FINDING: the simulator''s temperature is often clamped flat. temperature_bands in biomes.toml clamps each planet class ("fiction wins over physics"). On most temperate bodies that pins the field to exactly the band, 275-305 K. On some bodies it pins it to one value: GJ204c and GJ68e are 275 K everywhere, and GJ601Ad (275-281) and GJ722c (275-280) are nearly flat. On those bodies the biome variety comes from moisture alone.
|
||||
|
||||
RE-SHAPED 2026-09-25 (D-258 amendment "ids, not fields"). This supersedes the three-field bake above.
|
||||
|
||||
Bake two u8 id maps per in-scope body (257 bodies with an atmosphere) at 1024x512:
|
||||
- biomemap.png: the Whittaker class, taken BEFORE compute_biome''s terrain overrides. It covers every land pixel and is empty on thin-atmosphere bodies.
|
||||
- terrainmap.png: the terrain override class, 0 where there is none.
|
||||
Per-pixel temperature and moisture are NOT baked.
|
||||
|
||||
Also in scope here: the class tables (biome: climate envelope, flora, cover; terrain: substrate, landform hints), authored as TOML and imported into systems.db by reach ledger import under the generator stamp.
|
||||
|
||||
Done so far: the mountain-rock units fix in compute_biome, with tooling/test_planet_biome_rules.py (in make test-tooling). The rule compared kelvin to 0.35, so it could never fire; it now uses the world''s own normalised temperature. The test is mutation-proven. Sulfuric scrub is unreachable (it converts rock at elev_norm 0.25-0.65, but rock exists only above 0.65); it is left as found, since fixing it is a design call.
|
||||
|
||||
Open: story features (the anomaly scatter, ~3% of land). Their placement and rate are for a balancing pass.
|
||||
|
||||
SCOPE MOVE 2026-09-25: the class tables (biome and terrain, TOML imported into systems.db) move to T-1297, their first consumer. Nothing in this ticket reads them, and their columns should be set by what the climate model needs, not guessed ahead of it. T-1295 is now the two id maps, the bake verb, and the simulator split.
|
||||
|
||||
Refactor proof: compute_biome was split into compute_biome_layers, and compose_layers(biome, terrain) is byte-identical to the old single grid on 65 real bodies. That covers every standard-atmosphere body at a stride of 8, plus every thin, thick, reducing and trace body. The baseline was captured before the edit and compared after it.', NULL, '2026-09-25 13:08:42', '2026-09-25 13:08:42.649', '2026-09-25 13:08:42.649', NULL, '1ef15787992bad10ef8809972ab8a1fc', 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 ('06GDGD2Q9SK71WJVVFJA4PTBRM', 'description', 'D-258 amendment 2026-09-25, ruling 2: the baked biome input drives the climate model, not the other way round. The server''s climate model stops originating climate at gridunit scale on baked bodies. Today that origination is latitude, lapse, greenhouse and hydrosphere in derive_temperature_c and derive_moisture_q. It scatters SUB-GRIDUNIT around the baked temperature and moisture instead: lapse from invented relief, the D-243 edge-fuzz, and local noise.
|
||||
|
||||
COHERENCE WITH THE PICK (T-1296): correlate the scatter with the picked class, so a picked wet class does not sit on scattered-dry ground. One way is to order the local classes along the moisture axis and drive the pick and the moisture scatter from the same noise value. The steep-drains-dry correlation composition.rs already uses is the precedent.
|
||||
|
||||
UNITS: the Python bake is kelvin and moisture [0,1]; the server is deci-degrees C and moisture_q 0-100. Convert once at load.
|
||||
|
||||
D-239''s climate-to-class laws (the treeline, the riparian band) are NOT repealed; they operate inside the scatter. Airless, gas and unbaked bodies keep the current derive.
|
||||
|
||||
Blocked by T-1215. Expect every golden on baked bodies to move. Each move must be explained before regenerating, as in T-1213.
|
||||
|
||||
RE-SHAPED 2026-09-25 (D-258 amendment "ids, not fields"). No per-pixel temperature or moisture is baked. A cell''s climate is its biome classes'' envelope values from the biome table (systems.db), weighted by the bilinear class mix, and the climate model scatters sub-gridunit within that. Terrain classes carry no climate: under rock or ice, climate comes from the biome map beneath (baked pre-override), cooled by lapse from elevation.', 'D-258 amendment 2026-09-25, ruling 2: the baked biome input drives the climate model, not the other way round. The server''s climate model stops originating climate at gridunit scale on baked bodies. Today that origination is latitude, lapse, greenhouse and hydrosphere in derive_temperature_c and derive_moisture_q. It scatters SUB-GRIDUNIT around the baked temperature and moisture instead: lapse from invented relief, the D-243 edge-fuzz, and local noise.
|
||||
|
||||
COHERENCE WITH THE PICK (T-1296): correlate the scatter with the picked class, so a picked wet class does not sit on scattered-dry ground. One way is to order the local classes along the moisture axis and drive the pick and the moisture scatter from the same noise value. The steep-drains-dry correlation composition.rs already uses is the precedent.
|
||||
|
||||
UNITS: the Python bake is kelvin and moisture [0,1]; the server is deci-degrees C and moisture_q 0-100. Convert once at load.
|
||||
|
||||
D-239''s climate-to-class laws (the treeline, the riparian band) are NOT repealed; they operate inside the scatter. Airless, gas and unbaked bodies keep the current derive.
|
||||
|
||||
Blocked by T-1215. Expect every golden on baked bodies to move. Each move must be explained before regenerating, as in T-1213.
|
||||
|
||||
RE-SHAPED 2026-09-25 (D-258 amendment "ids, not fields"). No per-pixel temperature or moisture is baked. A cell''s climate is its biome classes'' envelope values from the biome table (systems.db), weighted by the bilinear class mix, and the climate model scatters sub-gridunit within that. Terrain classes carry no climate: under rock or ice, climate comes from the biome map beneath (baked pre-override), cooled by lapse from elevation.
|
||||
|
||||
SCOPE ADDED 2026-09-25 (moved from T-1295): the class tables. Author a biome table and a terrain table as TOML. The source today is tooling/domains/atlas/planet/biomes.toml, and whether it moves into wiki/ is still open. Import them into systems.db via reach ledger import, under the generator stamp: register the TOML in tooling/generator_sources.py, add the DDL to systems-schema.sql and MIGRATION_SQL. Columns are whatever this ticket''s climate model needs: at minimum the biome climate envelope (from the Whittaker boxes, whose ids 9, 11, 16 and 17 have two boxes each). Terrain classes carry no climate.
|
||||
|
||||
Layer and id facts from T-1295: 255 means none on both maps; ids are literal biomes.toml ids; and layer is decided per rule, so class 17 (ice) appears in BOTH tables.', NULL, '2026-09-25 13:08:43', '2026-09-25 13:08:43.221', '2026-09-25 13:08:43.221', NULL, '58e03d1b8272be23f3fa899120aab7d9', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
|
||||
@@ -1476,3 +1476,65 @@ Scope:
|
||||
Blocked by T-1295. Unblocks T-1296 and T-1297.
|
||||
|
||||
RE-SHAPED 2026-09-25 (D-258 amendment "ids, not fields"). Load TWO id maps, biomemap.png and terrainmap.png, and keep per-class fractions for each at the 512x256 downsample. There are no baked temperature or moisture fields. Class properties come from the biome and terrain tables in systems.db.', 'backlog', 'high', NULL, NULL, 'D-258', '2026-07-26 21:54:27.551', '2026-09-25 12:03:26.043', NULL, '7abe455db4683c4b7179c0e82a9ccb58', 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;
|
||||
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 ('06GDGD2G9K7C8SPH57RCNB8V8C', 'task', '06FT0TX2W0BA10PRR7NMJ2362M', 'Bake the biome input pre-seed: class id + absolute temperature + moisture per body (D-258 2026-09-25)', 'D-258 amendment 2026-09-25, rulings 1-4. Bake, per body, the Python planet simulator''s biome class grid TOGETHER WITH the absolute temperature (kelvin, before the display normalisation in simulate()) and moisture [0,1] it was classified from. One model supplies all three, so they agree by construction.
|
||||
|
||||
PRE-SEED: simulate() is keyed on the body frontmatter''s seed only. The world seed must never enter this bake. Worlds stay recognisable across playthroughs; the ground varies server-side.
|
||||
|
||||
MEASURE FIRST, then choose the format. The server works at 512x256 (GRID_W/GRID_H downsample), so a 1024x512 bake may be twice the resolution the server can use on each axis. Temperature and moisture are smooth fields that compress far worse than a class grid. Across ~2,600 bodies, the repository cost of candidate formats must be measured: 8-bit class PNG, 16-bit temperature, 8-bit moisture, at 1024x512 and at 512x256. Report the numbers before committing a bake.
|
||||
|
||||
DO NOT re-render reliefmap.png. Its render drifts by a few hundred bytes (T-1291). The bake gets its own mode, a verb on reach atlas planet, that writes only the new file(s). The heightmap is byte-identical on re-simulation, so the simulator is deterministic.
|
||||
|
||||
Class ids become append-only (the gas-palette selection_order discipline in biomes.toml). Gas giants get nothing. Whatever the chosen files are, register them where the atlas and canvas registries expect: the canvas-version gate (tooling/canvas_sources.py) must see a change to them.
|
||||
|
||||
MEASURED 2026-09-25. Probe: simulate() on 24 bodies spaced evenly across the in-scope set, each field encoded as optimised PNG. The probe was deleted after the run.
|
||||
|
||||
SCOPE (Jeroen, 2026-09-25): airless bodies are out of scope for now. The format and class vocabulary must still have room for them, since classes 19-36 already cover lava, dust, ferric and lunar ground. The heightmap bake set is 267 bodies. By atmosphere: standard 231, thin 23, thick 1, reducing 1, trace 1, none 8, NULL 2. That leaves 257 bodies with an atmosphere in scope.
|
||||
|
||||
PER BODY (mean / max) AND TOTAL OVER 257 BODIES:
|
||||
biome u8, 1024x512 11.7 / 16.2 KiB 2.9 MiB
|
||||
biome u8, 512 (plurality) 5.4 / 7.5 KiB 1.4 MiB
|
||||
temp u16 0.01 K, 1024 80.8 / 352 KiB 20.3 MiB
|
||||
temp u16 0.01 K, 512 29.0 / 124 KiB 7.3 MiB
|
||||
temp u8 1 K, 1024 14.8 / 54 KiB 3.7 MiB
|
||||
temp u8 1 K, 512 6.1 / 22 KiB 1.5 MiB
|
||||
moist u8, 1024 57.2 / 80 KiB 14.4 MiB
|
||||
moist u8, 512 22.8 / 32 KiB 5.7 MiB
|
||||
packed RGB (biome,t8,m8) 1024 96.7 / 172 KiB 24.3 MiB (worse than the three separate files at the same precision: 83.7 KiB)
|
||||
|
||||
REFERENCE: the committed heightmap.png averages 678 KiB per in-scope body, and reliefmap.png 295 KiB. Together that is about 245 MiB already in the tree.
|
||||
|
||||
Bake time: simulate() takes about 6.3 s per body, so the full bake is about 27 min.
|
||||
|
||||
FINDING: the simulator''s temperature is often clamped flat. temperature_bands in biomes.toml clamps each planet class ("fiction wins over physics"). On most temperate bodies that pins the field to exactly the band, 275-305 K. On some bodies it pins it to one value: GJ204c and GJ68e are 275 K everywhere, and GJ601Ad (275-281) and GJ722c (275-280) are nearly flat. On those bodies the biome variety comes from moisture alone.
|
||||
|
||||
RE-SHAPED 2026-09-25 (D-258 amendment "ids, not fields"). This supersedes the three-field bake above.
|
||||
|
||||
Bake two u8 id maps per in-scope body (257 bodies with an atmosphere) at 1024x512:
|
||||
- biomemap.png: the Whittaker class, taken BEFORE compute_biome''s terrain overrides. It covers every land pixel and is empty on thin-atmosphere bodies.
|
||||
- terrainmap.png: the terrain override class, 0 where there is none.
|
||||
Per-pixel temperature and moisture are NOT baked.
|
||||
|
||||
Also in scope here: the class tables (biome: climate envelope, flora, cover; terrain: substrate, landform hints), authored as TOML and imported into systems.db by reach ledger import under the generator stamp.
|
||||
|
||||
Done so far: the mountain-rock units fix in compute_biome, with tooling/test_planet_biome_rules.py (in make test-tooling). The rule compared kelvin to 0.35, so it could never fire; it now uses the world''s own normalised temperature. The test is mutation-proven. Sulfuric scrub is unreachable (it converts rock at elev_norm 0.25-0.65, but rock exists only above 0.65); it is left as found, since fixing it is a design call.
|
||||
|
||||
Open: story features (the anomaly scatter, ~3% of land). Their placement and rate are for a balancing pass.
|
||||
|
||||
SCOPE MOVE 2026-09-25: the class tables (biome and terrain, TOML imported into systems.db) move to T-1297, their first consumer. Nothing in this ticket reads them, and their columns should be set by what the climate model needs, not guessed ahead of it. T-1295 is now the two id maps, the bake verb, and the simulator split.
|
||||
|
||||
Refactor proof: compute_biome was split into compute_biome_layers, and compose_layers(biome, terrain) is byte-identical to the old single grid on 65 real bodies. That covers every standard-atmosphere body at a stride of 8, plus every thin, thick, reducing and trace body. The baseline was captured before the edit and compared after it.', 'in_progress', 'high', NULL, 'tooling', 'D-258', '2026-09-25 10:55:02.220', '2026-09-25 13:08:42.644', NULL, 'e14813897f0881511a2a36b60c68fd0d', 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;
|
||||
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 ('06GDGD2Q9SK71WJVVFJA4PTBRM', 'task', '06FT0TX2W0BA10PRR7NMJ2362M', 'Climate model takes the baked input: temperature and moisture scatter sub-gridunit around it', 'D-258 amendment 2026-09-25, ruling 2: the baked biome input drives the climate model, not the other way round. The server''s climate model stops originating climate at gridunit scale on baked bodies. Today that origination is latitude, lapse, greenhouse and hydrosphere in derive_temperature_c and derive_moisture_q. It scatters SUB-GRIDUNIT around the baked temperature and moisture instead: lapse from invented relief, the D-243 edge-fuzz, and local noise.
|
||||
|
||||
COHERENCE WITH THE PICK (T-1296): correlate the scatter with the picked class, so a picked wet class does not sit on scattered-dry ground. One way is to order the local classes along the moisture axis and drive the pick and the moisture scatter from the same noise value. The steep-drains-dry correlation composition.rs already uses is the precedent.
|
||||
|
||||
UNITS: the Python bake is kelvin and moisture [0,1]; the server is deci-degrees C and moisture_q 0-100. Convert once at load.
|
||||
|
||||
D-239''s climate-to-class laws (the treeline, the riparian band) are NOT repealed; they operate inside the scatter. Airless, gas and unbaked bodies keep the current derive.
|
||||
|
||||
Blocked by T-1215. Expect every golden on baked bodies to move. Each move must be explained before regenerating, as in T-1213.
|
||||
|
||||
RE-SHAPED 2026-09-25 (D-258 amendment "ids, not fields"). No per-pixel temperature or moisture is baked. A cell''s climate is its biome classes'' envelope values from the biome table (systems.db), weighted by the bilinear class mix, and the climate model scatters sub-gridunit within that. Terrain classes carry no climate: under rock or ice, climate comes from the biome map beneath (baked pre-override), cooled by lapse from elevation.
|
||||
|
||||
SCOPE ADDED 2026-09-25 (moved from T-1295): the class tables. Author a biome table and a terrain table as TOML. The source today is tooling/domains/atlas/planet/biomes.toml, and whether it moves into wiki/ is still open. Import them into systems.db via reach ledger import, under the generator stamp: register the TOML in tooling/generator_sources.py, add the DDL to systems-schema.sql and MIGRATION_SQL. Columns are whatever this ticket''s climate model needs: at minimum the biome climate envelope (from the Whittaker boxes, whose ids 9, 11, 16 and 17 have two boxes each). Terrain classes carry no climate.
|
||||
|
||||
Layer and id facts from T-1295: 255 means none on both maps; ids are literal biomes.toml ids; and layer is decided per rule, so class 17 (ice) appears in BOTH tables.', 'backlog', 'high', NULL, 'server', 'D-258', '2026-09-25 10:55:04.014', '2026-09-25 13:08:43.221', NULL, '944729a58c334e95c504f040b35a9178', 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;
|
||||
|
||||
Reference in New Issue
Block a user