feat(simulation): intra-class micro-habitat mosaic v1 — vegetation/water texture (T-1084, D-246)
Sub-chunk mosaic that modulates the D-228 axes into a spatially-coherent,
relief/moisture-conditioned mosaic of micro-habitats within a terrain class —
the fix for the aliveness probe's uniform "Wetland 100%".
- seed.rs: SeedDomain::VoxelMosaic = 12 (append-only; pinned-discriminant guard)
- voxel.rs: five new D-228 axis values (Vegetation::{Meadow,Deadfall,Lichen},
TerrainMaterial::{Hardpan,Scree}); the per-family-gated mosaic pass
- detail_scatter.rs: voxel_mosaic() selection field (~8-64 m band)
- atlas/mosaic.rs: surface-class palette + integer-basis-point weighted selection
with moisture-affinity conditioning; temperate/native baseline palettes
- data/mosaic_constants.toml: source-canonical palette mirror
- believability.rs: micro_habitat_distinct metric + D-245 intra-class criterion
v1 scope (D-246-faithful): modulates vegetation + water only, in the vegetated
classes (Wetland/Forest/Grassland), so the D-239 §8 lithology law is untouched;
water is gated on the T-1040 has_active_channel hydrology rule. Material variants
(hardpan/scree) and the extreme classes are a §8-aware follow-up; the biome-aware
palette variants (T-1085 vocab) extend the baseline before generation.
Goldens regenerated (believability + derivation). Full server suite green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -39,3 +39,167 @@ DECISIONS (user-resolved):
|
||||
CODE TOUCHPOINTS (Si-verified 2026-07-02): detail_scatter.rs:87-96 (voxel_relief pattern to extend), seed.rs:84-118 (SeedDomain; pinned-discriminant test ~:267), voxel.rs:1907-1943 (scatter_vegetation — the per-voxel salt-and-pepper this replaces), voxel.rs:445-495 (post-dispatch modulation seam), believability.rs (ContrastMetrics; evaluate_criteria ~:319). Overlaps to reconcile in the axis table: tube_depression (voxel.rs:710-717, Lava vocab) and braided-delta gravel-bar scrub (voxel.rs:1069-1092, Gravel vocab).
|
||||
|
||||
D-record written: D-246 (governance/decisions/architecture.md) pins all five resolved decisions + the full 32-row vocab→D-228 axis table + palette-key rule. D-228 amended to add the five new axis values (Vegetation::{Meadow,Deadfall,Lichen}, TerrainMaterial::{Hardpan,Scree}). D-246 is now the governing record for implementation; decision_ref D-227 kept as the derive-don''t-store parent.', NULL, '2026-07-02 18:26:37', '2026-07-02 18:26:37.203', '2026-07-02 18:26:37.203', NULL, 'c38329ce7eea51cd8aa53b0c5769da76', 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 ('06FJ91R8RV38BJXGB2V55FP3B8', 'description', 'Operationalize the D-247 native-biosphere premise. Full brief appended.', 'Operationalize the D-247 native-biosphere premise. Full brief appended.
|
||||
|
||||
---
|
||||
BRIEF (2026-07-02) — governing record: D-247 (governance/decisions/content.md); consumer: D-246 / T-1084 mosaic register.
|
||||
|
||||
WHAT THIS IS
|
||||
D-247 established that habitable Reach worlds were alive before humans (the settlement timeline forbids terraforming from dead rock), and that an AUTHORED per-world chirality bit gates whether native life is edible. This ticket turns that canon into data + reconciled wiki + the vocabulary the mosaic needs. Owner: Miri (worldbuilding/continuity). Stakeholder: Burnelli (food-import economics). Co-maintainer for the vocab: Araminta (visual grammar, per the T-995 ObjectTag precedent).
|
||||
|
||||
THE MODEL (locked in D-247 — do not re-litigate, apply it):
|
||||
- `biosphere_class ∈ {NativeCompatible, NativeMirror, TerraformedSterile, Airless}`. It is an AUTHORED INPUT, never seed-derived — it follows a world''s economic/cultural identity.
|
||||
- Compatible ("levo") = edible; coupling *permits* an authored hazard but does not force it (most are benign gardens).
|
||||
- Mirror ("dextro") = inedible but *guaranteed* inert (can''t infect/poison — safe to study).
|
||||
- "Safe" ≠ "edible" — decoupled. Bio-safe is guaranteed on Mirror; food-safe is Compatible-only.
|
||||
- Achiral seam (always works either way): breathe air, drink water, mine ore, burn/build with native biomass. Only eat/metabolize/medicate/infect/chirally-poison are gated.
|
||||
- ~50/50 Compatible:Mirror is a DESIGN TARGET across canon-silent worlds, not a per-world draw.
|
||||
|
||||
WORKSTREAMS
|
||||
1. CHIRALITY ASSIGNMENT (Miri). Give every inhabited body a biosphere_class by authored identity: principled default from `economic_base_primary` + `planet_class` (food/drink exporters → Compatible; dead+maintained-atmosphere → TerraformedSterile), explicit overrides where canon speaks. Locked exemplars: Arbour (GJ-338B)=Compatible; Solandar (GJ-827)=Mirror; Keid (GJ-166A)=Compatible (preserved *from exploitation* of edible alien life — the stronger conservation motive; Solandar is the Mirror-inert preserve); Oshima (GJ-79)=TerraformedSterile. Hit the ~50/50 target across the silent remainder.
|
||||
2. WIKI CONTINUITY (Miri). Reconcile the entries that already gesture at native biochemistry so each is consistent with its assigned bit + the mechanism: GJ-827, GJ-524, GJ-166A, GJ-684A, GJ-197, GJ-79, GJ-124 (resolve whether Hạ Long''s "protein" is native-Compatible harvest or Earth aquaculture). Additive/explanatory, not contradictory — preserve deliberately-vague lines as intentional.
|
||||
3. biosphere_class AUTHORING PATH (asset-pipeline discipline — schema + source + values together, per .claude/rules/asset-pipeline.md). Add `biosphere_class` to wiki body frontmatter (where `substrate:` already lives), a nullable `bodies.biosphere_class` column in systems-schema.sql + MIGRATION_SQL, and importer read. NOT needed for D-246; lands with this pass.
|
||||
4. ALIEN-ECOLOGY VOCABULARY (Miri + Araminta) — feeds T-1084 directly. The mosaic palette is keyed on surface-class × climate-biome × biosphere-register (D-246 Palette-key). Provide the micro-habitat NAMES per biome × register: native-mirror niches get *alien* clade names (Solandar''s "five kingdoms" flavor); introduced-Earth niches get Earth names (wheatfield, orchard, pasture). Fill the fixed niche spine (canopy / understory / wet-hollow / pioneer / bare) per climate-biome (savanna gallery-copse, tundra thermokarst / lichen-heath, boreal muskeg / deadfall, …). DETERMINISM: the vocab must be fixed before T-1084 generates its first world (a later addition re-rolls affected voxels). Table size is not a perf/storage concern — author generously but to taste (~100k is headroom, not a target).
|
||||
|
||||
BURNELLI (stakeholder, follow-on): a food-supply demand primitive keyed on biosphere_class (Mirror + frontier → import dependency; Compatible → local-food capable). Phase-2 economy consumer; does not block this ticket.
|
||||
|
||||
CONSTRAINTS
|
||||
- Authored input, not derivation (Arbour''s estate food must never be coin-flipped inedible).
|
||||
- Semantic axes stay clade-agnostic (Forest = canopy whether oak or mega-fungus); the register skins niches, it does not change gameplay semantics.
|
||||
- The mosaic geometry is chirality-blind — this work does NOT touch voxel geometry, only edibility/economy/naming/vocab.', NULL, '2026-07-02 20:33:09', '2026-07-02 20:33:09.514', '2026-07-02 20:33:09.514', NULL, '02a8635cdb5f42ec0a512d12804b3f35', 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 ('06FJ91R8RV38BJXGB2V55FP3B8', 'description', 'Operationalize the D-247 native-biosphere premise. Full brief appended.
|
||||
|
||||
---
|
||||
BRIEF (2026-07-02) — governing record: D-247 (governance/decisions/content.md); consumer: D-246 / T-1084 mosaic register.
|
||||
|
||||
WHAT THIS IS
|
||||
D-247 established that habitable Reach worlds were alive before humans (the settlement timeline forbids terraforming from dead rock), and that an AUTHORED per-world chirality bit gates whether native life is edible. This ticket turns that canon into data + reconciled wiki + the vocabulary the mosaic needs. Owner: Miri (worldbuilding/continuity). Stakeholder: Burnelli (food-import economics). Co-maintainer for the vocab: Araminta (visual grammar, per the T-995 ObjectTag precedent).
|
||||
|
||||
THE MODEL (locked in D-247 — do not re-litigate, apply it):
|
||||
- `biosphere_class ∈ {NativeCompatible, NativeMirror, TerraformedSterile, Airless}`. It is an AUTHORED INPUT, never seed-derived — it follows a world''s economic/cultural identity.
|
||||
- Compatible ("levo") = edible; coupling *permits* an authored hazard but does not force it (most are benign gardens).
|
||||
- Mirror ("dextro") = inedible but *guaranteed* inert (can''t infect/poison — safe to study).
|
||||
- "Safe" ≠ "edible" — decoupled. Bio-safe is guaranteed on Mirror; food-safe is Compatible-only.
|
||||
- Achiral seam (always works either way): breathe air, drink water, mine ore, burn/build with native biomass. Only eat/metabolize/medicate/infect/chirally-poison are gated.
|
||||
- ~50/50 Compatible:Mirror is a DESIGN TARGET across canon-silent worlds, not a per-world draw.
|
||||
|
||||
WORKSTREAMS
|
||||
1. CHIRALITY ASSIGNMENT (Miri). Give every inhabited body a biosphere_class by authored identity: principled default from `economic_base_primary` + `planet_class` (food/drink exporters → Compatible; dead+maintained-atmosphere → TerraformedSterile), explicit overrides where canon speaks. Locked exemplars: Arbour (GJ-338B)=Compatible; Solandar (GJ-827)=Mirror; Keid (GJ-166A)=Compatible (preserved *from exploitation* of edible alien life — the stronger conservation motive; Solandar is the Mirror-inert preserve); Oshima (GJ-79)=TerraformedSterile. Hit the ~50/50 target across the silent remainder.
|
||||
2. WIKI CONTINUITY (Miri). Reconcile the entries that already gesture at native biochemistry so each is consistent with its assigned bit + the mechanism: GJ-827, GJ-524, GJ-166A, GJ-684A, GJ-197, GJ-79, GJ-124 (resolve whether Hạ Long''s "protein" is native-Compatible harvest or Earth aquaculture). Additive/explanatory, not contradictory — preserve deliberately-vague lines as intentional.
|
||||
3. biosphere_class AUTHORING PATH (asset-pipeline discipline — schema + source + values together, per .claude/rules/asset-pipeline.md). Add `biosphere_class` to wiki body frontmatter (where `substrate:` already lives), a nullable `bodies.biosphere_class` column in systems-schema.sql + MIGRATION_SQL, and importer read. NOT needed for D-246; lands with this pass.
|
||||
4. ALIEN-ECOLOGY VOCABULARY (Miri + Araminta) — feeds T-1084 directly. The mosaic palette is keyed on surface-class × climate-biome × biosphere-register (D-246 Palette-key). Provide the micro-habitat NAMES per biome × register: native-mirror niches get *alien* clade names (Solandar''s "five kingdoms" flavor); introduced-Earth niches get Earth names (wheatfield, orchard, pasture). Fill the fixed niche spine (canopy / understory / wet-hollow / pioneer / bare) per climate-biome (savanna gallery-copse, tundra thermokarst / lichen-heath, boreal muskeg / deadfall, …). DETERMINISM: the vocab must be fixed before T-1084 generates its first world (a later addition re-rolls affected voxels). Table size is not a perf/storage concern — author generously but to taste (~100k is headroom, not a target).
|
||||
|
||||
BURNELLI (stakeholder, follow-on): a food-supply demand primitive keyed on biosphere_class (Mirror + frontier → import dependency; Compatible → local-food capable). Phase-2 economy consumer; does not block this ticket.
|
||||
|
||||
CONSTRAINTS
|
||||
- Authored input, not derivation (Arbour''s estate food must never be coin-flipped inedible).
|
||||
- Semantic axes stay clade-agnostic (Forest = canopy whether oak or mega-fungus); the register skins niches, it does not change gameplay semantics.
|
||||
- The mosaic geometry is chirality-blind — this work does NOT touch voxel geometry, only edibility/economy/naming/vocab.', 'Operationalize the D-247 native-biosphere premise. Full brief appended.
|
||||
|
||||
---
|
||||
BRIEF (2026-07-02) — governing record: D-247 (governance/decisions/content.md); consumer: D-246 / T-1084 mosaic register.
|
||||
|
||||
WHAT THIS IS
|
||||
D-247 established that habitable Reach worlds were alive before humans (the settlement timeline forbids terraforming from dead rock), and that an AUTHORED per-world chirality bit gates whether native life is edible. This ticket turns that canon into data + reconciled wiki + the vocabulary the mosaic needs. Owner: Miri (worldbuilding/continuity). Stakeholder: Burnelli (food-import economics). Co-maintainer for the vocab: Araminta (visual grammar, per the T-995 ObjectTag precedent).
|
||||
|
||||
THE MODEL (locked in D-247 — do not re-litigate, apply it):
|
||||
- `biosphere_class ∈ {NativeCompatible, NativeMirror, TerraformedSterile, Airless}`. It is an AUTHORED INPUT, never seed-derived — it follows a world''s economic/cultural identity.
|
||||
- Compatible ("levo") = edible; coupling *permits* an authored hazard but does not force it (most are benign gardens).
|
||||
- Mirror ("dextro") = inedible but *guaranteed* inert (can''t infect/poison — safe to study).
|
||||
- "Safe" ≠ "edible" — decoupled. Bio-safe is guaranteed on Mirror; food-safe is Compatible-only.
|
||||
- Achiral seam (always works either way): breathe air, drink water, mine ore, burn/build with native biomass. Only eat/metabolize/medicate/infect/chirally-poison are gated.
|
||||
- ~50/50 Compatible:Mirror is a DESIGN TARGET across canon-silent worlds, not a per-world draw.
|
||||
|
||||
WORKSTREAMS
|
||||
1. CHIRALITY ASSIGNMENT (Miri). Give every inhabited body a biosphere_class by authored identity: principled default from `economic_base_primary` + `planet_class` (food/drink exporters → Compatible; dead+maintained-atmosphere → TerraformedSterile), explicit overrides where canon speaks. Locked exemplars: Arbour (GJ-338B)=Compatible; Solandar (GJ-827)=Mirror; Keid (GJ-166A)=Compatible (preserved *from exploitation* of edible alien life — the stronger conservation motive; Solandar is the Mirror-inert preserve); Oshima (GJ-79)=TerraformedSterile. Hit the ~50/50 target across the silent remainder.
|
||||
2. WIKI CONTINUITY (Miri). Reconcile the entries that already gesture at native biochemistry so each is consistent with its assigned bit + the mechanism: GJ-827, GJ-524, GJ-166A, GJ-684A, GJ-197, GJ-79, GJ-124 (resolve whether Hạ Long''s "protein" is native-Compatible harvest or Earth aquaculture). Additive/explanatory, not contradictory — preserve deliberately-vague lines as intentional.
|
||||
3. biosphere_class AUTHORING PATH (asset-pipeline discipline — schema + source + values together, per .claude/rules/asset-pipeline.md). Add `biosphere_class` to wiki body frontmatter (where `substrate:` already lives), a nullable `bodies.biosphere_class` column in systems-schema.sql + MIGRATION_SQL, and importer read. NOT needed for D-246; lands with this pass.
|
||||
4. ALIEN-ECOLOGY VOCABULARY (Miri + Araminta) — feeds T-1084 directly. The mosaic palette is keyed on surface-class × climate-biome × biosphere-register (D-246 Palette-key). Provide the micro-habitat NAMES per biome × register: native-mirror niches get *alien* clade names (Solandar''s "five kingdoms" flavor); introduced-Earth niches get Earth names (wheatfield, orchard, pasture). Fill the fixed niche spine (canopy / understory / wet-hollow / pioneer / bare) per climate-biome (savanna gallery-copse, tundra thermokarst / lichen-heath, boreal muskeg / deadfall, …). DETERMINISM: the vocab must be fixed before T-1084 generates its first world (a later addition re-rolls affected voxels). Table size is not a perf/storage concern — author generously but to taste (~100k is headroom, not a target).
|
||||
|
||||
BURNELLI (stakeholder, follow-on): a food-supply demand primitive keyed on biosphere_class (Mirror + frontier → import dependency; Compatible → local-food capable). Phase-2 economy consumer; does not block this ticket.
|
||||
|
||||
CONSTRAINTS
|
||||
- Authored input, not derivation (Arbour''s estate food must never be coin-flipped inedible).
|
||||
- Semantic axes stay clade-agnostic (Forest = canopy whether oak or mega-fungus); the register skins niches, it does not change gameplay semantics.
|
||||
- The mosaic geometry is chirality-blind — this work does NOT touch voxel geometry, only edibility/economy/naming/vocab.
|
||||
|
||||
Chirality decisions resolved (2026-07-02, with Jeroen):
|
||||
- Locked exemplars: Arbour=NativeCompatible, Solandar=NativeMirror, Keid=NativeCompatible, Oshima=TerraformedSterile.
|
||||
- Miri assignments accepted: GJ-524=Mirror, Freyburg(GJ-684A)=Compatible, Hạ Long(GJ-124)=Compatible (native-Compatible harvest, per her rationale).
|
||||
- USER OVERRIDES: Puerto Último(GJ-197)=NativeCompatible (breadbasket identity over Miri''s balance-motivated Mirror). Edict/Sirius(GJ-244A)=Airless — keep the world harsh/frozen (deliberately-inconvenient below-grade transit capital is load-bearing canon); no climate change; below-grade sealed habitat = no open-air biosphere.
|
||||
- Two-gate default rule ACCEPTED for canon-silent worlds: Gate 0 (habitability from atmosphere/hydrosphere frontmatter → Airless / TerraformedSterile / Alive), Gate 1 (Compatible vs Mirror from economic_base_primary; agriculture→Compatible near-lock, penal→Mirror, research→50/50 lever).
|
||||
- NAMING: Keid''s habitable body renamed Cadwal→(pending final pick; rec Linnaeus — pairs with Solander, its student naturalist; or Beagle). Capital stays ''Vance'' (already the author tribute). Miri''s IP flag (Cadwal = Vance''s exact book title) is the driver.
|
||||
- Still to APPLY: the 7 additive wiki continuity edits (Miri specified exact text), the biosphere_class frontmatter+schema+importer plumbing (workstream 3), and the Cadwal→<name> rename across wiki + the GJ166A atlas proposal.', NULL, '2026-07-02 21:23:34', '2026-07-02 21:23:34.735', '2026-07-02 21:23:34.735', NULL, '80cb1aabee1e9defb9795e677e7ee58d', 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 ('06FJ91R8RV38BJXGB2V55FP3B8', 'description', 'Operationalize the D-247 native-biosphere premise. Full brief appended.
|
||||
|
||||
---
|
||||
BRIEF (2026-07-02) — governing record: D-247 (governance/decisions/content.md); consumer: D-246 / T-1084 mosaic register.
|
||||
|
||||
WHAT THIS IS
|
||||
D-247 established that habitable Reach worlds were alive before humans (the settlement timeline forbids terraforming from dead rock), and that an AUTHORED per-world chirality bit gates whether native life is edible. This ticket turns that canon into data + reconciled wiki + the vocabulary the mosaic needs. Owner: Miri (worldbuilding/continuity). Stakeholder: Burnelli (food-import economics). Co-maintainer for the vocab: Araminta (visual grammar, per the T-995 ObjectTag precedent).
|
||||
|
||||
THE MODEL (locked in D-247 — do not re-litigate, apply it):
|
||||
- `biosphere_class ∈ {NativeCompatible, NativeMirror, TerraformedSterile, Airless}`. It is an AUTHORED INPUT, never seed-derived — it follows a world''s economic/cultural identity.
|
||||
- Compatible ("levo") = edible; coupling *permits* an authored hazard but does not force it (most are benign gardens).
|
||||
- Mirror ("dextro") = inedible but *guaranteed* inert (can''t infect/poison — safe to study).
|
||||
- "Safe" ≠ "edible" — decoupled. Bio-safe is guaranteed on Mirror; food-safe is Compatible-only.
|
||||
- Achiral seam (always works either way): breathe air, drink water, mine ore, burn/build with native biomass. Only eat/metabolize/medicate/infect/chirally-poison are gated.
|
||||
- ~50/50 Compatible:Mirror is a DESIGN TARGET across canon-silent worlds, not a per-world draw.
|
||||
|
||||
WORKSTREAMS
|
||||
1. CHIRALITY ASSIGNMENT (Miri). Give every inhabited body a biosphere_class by authored identity: principled default from `economic_base_primary` + `planet_class` (food/drink exporters → Compatible; dead+maintained-atmosphere → TerraformedSterile), explicit overrides where canon speaks. Locked exemplars: Arbour (GJ-338B)=Compatible; Solandar (GJ-827)=Mirror; Keid (GJ-166A)=Compatible (preserved *from exploitation* of edible alien life — the stronger conservation motive; Solandar is the Mirror-inert preserve); Oshima (GJ-79)=TerraformedSterile. Hit the ~50/50 target across the silent remainder.
|
||||
2. WIKI CONTINUITY (Miri). Reconcile the entries that already gesture at native biochemistry so each is consistent with its assigned bit + the mechanism: GJ-827, GJ-524, GJ-166A, GJ-684A, GJ-197, GJ-79, GJ-124 (resolve whether Hạ Long''s "protein" is native-Compatible harvest or Earth aquaculture). Additive/explanatory, not contradictory — preserve deliberately-vague lines as intentional.
|
||||
3. biosphere_class AUTHORING PATH (asset-pipeline discipline — schema + source + values together, per .claude/rules/asset-pipeline.md). Add `biosphere_class` to wiki body frontmatter (where `substrate:` already lives), a nullable `bodies.biosphere_class` column in systems-schema.sql + MIGRATION_SQL, and importer read. NOT needed for D-246; lands with this pass.
|
||||
4. ALIEN-ECOLOGY VOCABULARY (Miri + Araminta) — feeds T-1084 directly. The mosaic palette is keyed on surface-class × climate-biome × biosphere-register (D-246 Palette-key). Provide the micro-habitat NAMES per biome × register: native-mirror niches get *alien* clade names (Solandar''s "five kingdoms" flavor); introduced-Earth niches get Earth names (wheatfield, orchard, pasture). Fill the fixed niche spine (canopy / understory / wet-hollow / pioneer / bare) per climate-biome (savanna gallery-copse, tundra thermokarst / lichen-heath, boreal muskeg / deadfall, …). DETERMINISM: the vocab must be fixed before T-1084 generates its first world (a later addition re-rolls affected voxels). Table size is not a perf/storage concern — author generously but to taste (~100k is headroom, not a target).
|
||||
|
||||
BURNELLI (stakeholder, follow-on): a food-supply demand primitive keyed on biosphere_class (Mirror + frontier → import dependency; Compatible → local-food capable). Phase-2 economy consumer; does not block this ticket.
|
||||
|
||||
CONSTRAINTS
|
||||
- Authored input, not derivation (Arbour''s estate food must never be coin-flipped inedible).
|
||||
- Semantic axes stay clade-agnostic (Forest = canopy whether oak or mega-fungus); the register skins niches, it does not change gameplay semantics.
|
||||
- The mosaic geometry is chirality-blind — this work does NOT touch voxel geometry, only edibility/economy/naming/vocab.
|
||||
|
||||
Chirality decisions resolved (2026-07-02, with Jeroen):
|
||||
- Locked exemplars: Arbour=NativeCompatible, Solandar=NativeMirror, Keid=NativeCompatible, Oshima=TerraformedSterile.
|
||||
- Miri assignments accepted: GJ-524=Mirror, Freyburg(GJ-684A)=Compatible, Hạ Long(GJ-124)=Compatible (native-Compatible harvest, per her rationale).
|
||||
- USER OVERRIDES: Puerto Último(GJ-197)=NativeCompatible (breadbasket identity over Miri''s balance-motivated Mirror). Edict/Sirius(GJ-244A)=Airless — keep the world harsh/frozen (deliberately-inconvenient below-grade transit capital is load-bearing canon); no climate change; below-grade sealed habitat = no open-air biosphere.
|
||||
- Two-gate default rule ACCEPTED for canon-silent worlds: Gate 0 (habitability from atmosphere/hydrosphere frontmatter → Airless / TerraformedSterile / Alive), Gate 1 (Compatible vs Mirror from economic_base_primary; agriculture→Compatible near-lock, penal→Mirror, research→50/50 lever).
|
||||
- NAMING: Keid''s habitable body renamed Cadwal→(pending final pick; rec Linnaeus — pairs with Solander, its student naturalist; or Beagle). Capital stays ''Vance'' (already the author tribute). Miri''s IP flag (Cadwal = Vance''s exact book title) is the driver.
|
||||
- Still to APPLY: the 7 additive wiki continuity edits (Miri specified exact text), the biosphere_class frontmatter+schema+importer plumbing (workstream 3), and the Cadwal→<name> rename across wiki + the GJ166A atlas proposal.', 'Operationalize the D-247 native-biosphere premise. Full brief appended.
|
||||
|
||||
---
|
||||
BRIEF (2026-07-02) — governing record: D-247 (governance/decisions/content.md); consumer: D-246 / T-1084 mosaic register.
|
||||
|
||||
WHAT THIS IS
|
||||
D-247 established that habitable Reach worlds were alive before humans (the settlement timeline forbids terraforming from dead rock), and that an AUTHORED per-world chirality bit gates whether native life is edible. This ticket turns that canon into data + reconciled wiki + the vocabulary the mosaic needs. Owner: Miri (worldbuilding/continuity). Stakeholder: Burnelli (food-import economics). Co-maintainer for the vocab: Araminta (visual grammar, per the T-995 ObjectTag precedent).
|
||||
|
||||
THE MODEL (locked in D-247 — do not re-litigate, apply it):
|
||||
- `biosphere_class ∈ {NativeCompatible, NativeMirror, TerraformedSterile, Airless}`. It is an AUTHORED INPUT, never seed-derived — it follows a world''s economic/cultural identity.
|
||||
- Compatible ("levo") = edible; coupling *permits* an authored hazard but does not force it (most are benign gardens).
|
||||
- Mirror ("dextro") = inedible but *guaranteed* inert (can''t infect/poison — safe to study).
|
||||
- "Safe" ≠ "edible" — decoupled. Bio-safe is guaranteed on Mirror; food-safe is Compatible-only.
|
||||
- Achiral seam (always works either way): breathe air, drink water, mine ore, burn/build with native biomass. Only eat/metabolize/medicate/infect/chirally-poison are gated.
|
||||
- ~50/50 Compatible:Mirror is a DESIGN TARGET across canon-silent worlds, not a per-world draw.
|
||||
|
||||
WORKSTREAMS
|
||||
1. CHIRALITY ASSIGNMENT (Miri). Give every inhabited body a biosphere_class by authored identity: principled default from `economic_base_primary` + `planet_class` (food/drink exporters → Compatible; dead+maintained-atmosphere → TerraformedSterile), explicit overrides where canon speaks. Locked exemplars: Arbour (GJ-338B)=Compatible; Solandar (GJ-827)=Mirror; Keid (GJ-166A)=Compatible (preserved *from exploitation* of edible alien life — the stronger conservation motive; Solandar is the Mirror-inert preserve); Oshima (GJ-79)=TerraformedSterile. Hit the ~50/50 target across the silent remainder.
|
||||
2. WIKI CONTINUITY (Miri). Reconcile the entries that already gesture at native biochemistry so each is consistent with its assigned bit + the mechanism: GJ-827, GJ-524, GJ-166A, GJ-684A, GJ-197, GJ-79, GJ-124 (resolve whether Hạ Long''s "protein" is native-Compatible harvest or Earth aquaculture). Additive/explanatory, not contradictory — preserve deliberately-vague lines as intentional.
|
||||
3. biosphere_class AUTHORING PATH (asset-pipeline discipline — schema + source + values together, per .claude/rules/asset-pipeline.md). Add `biosphere_class` to wiki body frontmatter (where `substrate:` already lives), a nullable `bodies.biosphere_class` column in systems-schema.sql + MIGRATION_SQL, and importer read. NOT needed for D-246; lands with this pass.
|
||||
4. ALIEN-ECOLOGY VOCABULARY (Miri + Araminta) — feeds T-1084 directly. The mosaic palette is keyed on surface-class × climate-biome × biosphere-register (D-246 Palette-key). Provide the micro-habitat NAMES per biome × register: native-mirror niches get *alien* clade names (Solandar''s "five kingdoms" flavor); introduced-Earth niches get Earth names (wheatfield, orchard, pasture). Fill the fixed niche spine (canopy / understory / wet-hollow / pioneer / bare) per climate-biome (savanna gallery-copse, tundra thermokarst / lichen-heath, boreal muskeg / deadfall, …). DETERMINISM: the vocab must be fixed before T-1084 generates its first world (a later addition re-rolls affected voxels). Table size is not a perf/storage concern — author generously but to taste (~100k is headroom, not a target).
|
||||
|
||||
BURNELLI (stakeholder, follow-on): a food-supply demand primitive keyed on biosphere_class (Mirror + frontier → import dependency; Compatible → local-food capable). Phase-2 economy consumer; does not block this ticket.
|
||||
|
||||
CONSTRAINTS
|
||||
- Authored input, not derivation (Arbour''s estate food must never be coin-flipped inedible).
|
||||
- Semantic axes stay clade-agnostic (Forest = canopy whether oak or mega-fungus); the register skins niches, it does not change gameplay semantics.
|
||||
- The mosaic geometry is chirality-blind — this work does NOT touch voxel geometry, only edibility/economy/naming/vocab.
|
||||
|
||||
Chirality decisions resolved (2026-07-02, with Jeroen):
|
||||
- Locked exemplars: Arbour=NativeCompatible, Solandar=NativeMirror, Keid=NativeCompatible, Oshima=TerraformedSterile.
|
||||
- Miri assignments accepted: GJ-524=Mirror, Freyburg(GJ-684A)=Compatible, Hạ Long(GJ-124)=Compatible (native-Compatible harvest, per her rationale).
|
||||
- USER OVERRIDES: Puerto Último(GJ-197)=NativeCompatible (breadbasket identity over Miri''s balance-motivated Mirror). Edict/Sirius(GJ-244A)=Airless — keep the world harsh/frozen (deliberately-inconvenient below-grade transit capital is load-bearing canon); no climate change; below-grade sealed habitat = no open-air biosphere.
|
||||
- Two-gate default rule ACCEPTED for canon-silent worlds: Gate 0 (habitability from atmosphere/hydrosphere frontmatter → Airless / TerraformedSterile / Alive), Gate 1 (Compatible vs Mirror from economic_base_primary; agriculture→Compatible near-lock, penal→Mirror, research→50/50 lever).
|
||||
- NAMING: Keid''s habitable body renamed Cadwal→(pending final pick; rec Linnaeus — pairs with Solander, its student naturalist; or Beagle). Capital stays ''Vance'' (already the author tribute). Miri''s IP flag (Cadwal = Vance''s exact book title) is the driver.
|
||||
- Still to APPLY: the 7 additive wiki continuity edits (Miri specified exact text), the biosphere_class frontmatter+schema+importer plumbing (workstream 3), and the Cadwal→<name> rename across wiki + the GJ166A atlas proposal.
|
||||
|
||||
NAMING CONFIRMED: Keid''s habitable body Cadwal → Linnaeus (the great classifier; ties to Solander/Solandar taxonomy + the ''five kingdoms''). Capital stays Vance.', NULL, '2026-07-02 21:24:12', '2026-07-02 21:24:12.734', '2026-07-02 21:24:12.734', NULL, 'b13c9f1ce1d47437fdb599e6efce7f0e', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
|
||||
@@ -39,3 +39,106 @@ DECISIONS (user-resolved):
|
||||
CODE TOUCHPOINTS (Si-verified 2026-07-02): detail_scatter.rs:87-96 (voxel_relief pattern to extend), seed.rs:84-118 (SeedDomain; pinned-discriminant test ~:267), voxel.rs:1907-1943 (scatter_vegetation — the per-voxel salt-and-pepper this replaces), voxel.rs:445-495 (post-dispatch modulation seam), believability.rs (ContrastMetrics; evaluate_criteria ~:319). Overlaps to reconcile in the axis table: tube_depression (voxel.rs:710-717, Lava vocab) and braided-delta gravel-bar scrub (voxel.rs:1069-1092, Gravel vocab).
|
||||
|
||||
D-record written: D-246 (governance/decisions/architecture.md) pins all five resolved decisions + the full 32-row vocab→D-228 axis table + palette-key rule. D-228 amended to add the five new axis values (Vegetation::{Meadow,Deadfall,Lichen}, TerrainMaterial::{Hardpan,Scree}). D-246 is now the governing record for implementation; decision_ref D-227 kept as the derive-don''t-store parent.', 'in_progress', 'high', NULL, 'server', 'D-227', '2026-06-28 05:55:34.396', '2026-07-02 18:26:37.202', NULL, '7edbe9dc6461eea90bf94ebcfe0b0687', 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 ('06FJ91R8RV38BJXGB2V55FP3B8', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'Biosphere register (D-247) — chirality bits, wiki continuity, biosphere_class authoring + alien-ecology vocab', 'Operationalize the D-247 native-biosphere premise. Full brief appended.', 'backlog', 'medium', 'miri', NULL, 'D-247', '2026-07-02 20:30:57.222', '2026-07-02 20:30:57.222', NULL, 'e7f137504714f54f2c0cb1a1481609d3', 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 ('06FJ91R8RV38BJXGB2V55FP3B8', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'Biosphere register (D-247) — chirality bits, wiki continuity, biosphere_class authoring + alien-ecology vocab', 'Operationalize the D-247 native-biosphere premise. Full brief appended.
|
||||
|
||||
---
|
||||
BRIEF (2026-07-02) — governing record: D-247 (governance/decisions/content.md); consumer: D-246 / T-1084 mosaic register.
|
||||
|
||||
WHAT THIS IS
|
||||
D-247 established that habitable Reach worlds were alive before humans (the settlement timeline forbids terraforming from dead rock), and that an AUTHORED per-world chirality bit gates whether native life is edible. This ticket turns that canon into data + reconciled wiki + the vocabulary the mosaic needs. Owner: Miri (worldbuilding/continuity). Stakeholder: Burnelli (food-import economics). Co-maintainer for the vocab: Araminta (visual grammar, per the T-995 ObjectTag precedent).
|
||||
|
||||
THE MODEL (locked in D-247 — do not re-litigate, apply it):
|
||||
- `biosphere_class ∈ {NativeCompatible, NativeMirror, TerraformedSterile, Airless}`. It is an AUTHORED INPUT, never seed-derived — it follows a world''s economic/cultural identity.
|
||||
- Compatible ("levo") = edible; coupling *permits* an authored hazard but does not force it (most are benign gardens).
|
||||
- Mirror ("dextro") = inedible but *guaranteed* inert (can''t infect/poison — safe to study).
|
||||
- "Safe" ≠ "edible" — decoupled. Bio-safe is guaranteed on Mirror; food-safe is Compatible-only.
|
||||
- Achiral seam (always works either way): breathe air, drink water, mine ore, burn/build with native biomass. Only eat/metabolize/medicate/infect/chirally-poison are gated.
|
||||
- ~50/50 Compatible:Mirror is a DESIGN TARGET across canon-silent worlds, not a per-world draw.
|
||||
|
||||
WORKSTREAMS
|
||||
1. CHIRALITY ASSIGNMENT (Miri). Give every inhabited body a biosphere_class by authored identity: principled default from `economic_base_primary` + `planet_class` (food/drink exporters → Compatible; dead+maintained-atmosphere → TerraformedSterile), explicit overrides where canon speaks. Locked exemplars: Arbour (GJ-338B)=Compatible; Solandar (GJ-827)=Mirror; Keid (GJ-166A)=Compatible (preserved *from exploitation* of edible alien life — the stronger conservation motive; Solandar is the Mirror-inert preserve); Oshima (GJ-79)=TerraformedSterile. Hit the ~50/50 target across the silent remainder.
|
||||
2. WIKI CONTINUITY (Miri). Reconcile the entries that already gesture at native biochemistry so each is consistent with its assigned bit + the mechanism: GJ-827, GJ-524, GJ-166A, GJ-684A, GJ-197, GJ-79, GJ-124 (resolve whether Hạ Long''s "protein" is native-Compatible harvest or Earth aquaculture). Additive/explanatory, not contradictory — preserve deliberately-vague lines as intentional.
|
||||
3. biosphere_class AUTHORING PATH (asset-pipeline discipline — schema + source + values together, per .claude/rules/asset-pipeline.md). Add `biosphere_class` to wiki body frontmatter (where `substrate:` already lives), a nullable `bodies.biosphere_class` column in systems-schema.sql + MIGRATION_SQL, and importer read. NOT needed for D-246; lands with this pass.
|
||||
4. ALIEN-ECOLOGY VOCABULARY (Miri + Araminta) — feeds T-1084 directly. The mosaic palette is keyed on surface-class × climate-biome × biosphere-register (D-246 Palette-key). Provide the micro-habitat NAMES per biome × register: native-mirror niches get *alien* clade names (Solandar''s "five kingdoms" flavor); introduced-Earth niches get Earth names (wheatfield, orchard, pasture). Fill the fixed niche spine (canopy / understory / wet-hollow / pioneer / bare) per climate-biome (savanna gallery-copse, tundra thermokarst / lichen-heath, boreal muskeg / deadfall, …). DETERMINISM: the vocab must be fixed before T-1084 generates its first world (a later addition re-rolls affected voxels). Table size is not a perf/storage concern — author generously but to taste (~100k is headroom, not a target).
|
||||
|
||||
BURNELLI (stakeholder, follow-on): a food-supply demand primitive keyed on biosphere_class (Mirror + frontier → import dependency; Compatible → local-food capable). Phase-2 economy consumer; does not block this ticket.
|
||||
|
||||
CONSTRAINTS
|
||||
- Authored input, not derivation (Arbour''s estate food must never be coin-flipped inedible).
|
||||
- Semantic axes stay clade-agnostic (Forest = canopy whether oak or mega-fungus); the register skins niches, it does not change gameplay semantics.
|
||||
- The mosaic geometry is chirality-blind — this work does NOT touch voxel geometry, only edibility/economy/naming/vocab.', 'backlog', 'medium', 'miri', NULL, 'D-247', '2026-07-02 20:30:57.222', '2026-07-02 20:33:09.514', NULL, '648fccf9411f626aaf04a2c5f0a0c9fa', 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 ('06FJ91R8RV38BJXGB2V55FP3B8', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'Biosphere register (D-247) — chirality bits, wiki continuity, biosphere_class authoring + alien-ecology vocab', 'Operationalize the D-247 native-biosphere premise. Full brief appended.
|
||||
|
||||
---
|
||||
BRIEF (2026-07-02) — governing record: D-247 (governance/decisions/content.md); consumer: D-246 / T-1084 mosaic register.
|
||||
|
||||
WHAT THIS IS
|
||||
D-247 established that habitable Reach worlds were alive before humans (the settlement timeline forbids terraforming from dead rock), and that an AUTHORED per-world chirality bit gates whether native life is edible. This ticket turns that canon into data + reconciled wiki + the vocabulary the mosaic needs. Owner: Miri (worldbuilding/continuity). Stakeholder: Burnelli (food-import economics). Co-maintainer for the vocab: Araminta (visual grammar, per the T-995 ObjectTag precedent).
|
||||
|
||||
THE MODEL (locked in D-247 — do not re-litigate, apply it):
|
||||
- `biosphere_class ∈ {NativeCompatible, NativeMirror, TerraformedSterile, Airless}`. It is an AUTHORED INPUT, never seed-derived — it follows a world''s economic/cultural identity.
|
||||
- Compatible ("levo") = edible; coupling *permits* an authored hazard but does not force it (most are benign gardens).
|
||||
- Mirror ("dextro") = inedible but *guaranteed* inert (can''t infect/poison — safe to study).
|
||||
- "Safe" ≠ "edible" — decoupled. Bio-safe is guaranteed on Mirror; food-safe is Compatible-only.
|
||||
- Achiral seam (always works either way): breathe air, drink water, mine ore, burn/build with native biomass. Only eat/metabolize/medicate/infect/chirally-poison are gated.
|
||||
- ~50/50 Compatible:Mirror is a DESIGN TARGET across canon-silent worlds, not a per-world draw.
|
||||
|
||||
WORKSTREAMS
|
||||
1. CHIRALITY ASSIGNMENT (Miri). Give every inhabited body a biosphere_class by authored identity: principled default from `economic_base_primary` + `planet_class` (food/drink exporters → Compatible; dead+maintained-atmosphere → TerraformedSterile), explicit overrides where canon speaks. Locked exemplars: Arbour (GJ-338B)=Compatible; Solandar (GJ-827)=Mirror; Keid (GJ-166A)=Compatible (preserved *from exploitation* of edible alien life — the stronger conservation motive; Solandar is the Mirror-inert preserve); Oshima (GJ-79)=TerraformedSterile. Hit the ~50/50 target across the silent remainder.
|
||||
2. WIKI CONTINUITY (Miri). Reconcile the entries that already gesture at native biochemistry so each is consistent with its assigned bit + the mechanism: GJ-827, GJ-524, GJ-166A, GJ-684A, GJ-197, GJ-79, GJ-124 (resolve whether Hạ Long''s "protein" is native-Compatible harvest or Earth aquaculture). Additive/explanatory, not contradictory — preserve deliberately-vague lines as intentional.
|
||||
3. biosphere_class AUTHORING PATH (asset-pipeline discipline — schema + source + values together, per .claude/rules/asset-pipeline.md). Add `biosphere_class` to wiki body frontmatter (where `substrate:` already lives), a nullable `bodies.biosphere_class` column in systems-schema.sql + MIGRATION_SQL, and importer read. NOT needed for D-246; lands with this pass.
|
||||
4. ALIEN-ECOLOGY VOCABULARY (Miri + Araminta) — feeds T-1084 directly. The mosaic palette is keyed on surface-class × climate-biome × biosphere-register (D-246 Palette-key). Provide the micro-habitat NAMES per biome × register: native-mirror niches get *alien* clade names (Solandar''s "five kingdoms" flavor); introduced-Earth niches get Earth names (wheatfield, orchard, pasture). Fill the fixed niche spine (canopy / understory / wet-hollow / pioneer / bare) per climate-biome (savanna gallery-copse, tundra thermokarst / lichen-heath, boreal muskeg / deadfall, …). DETERMINISM: the vocab must be fixed before T-1084 generates its first world (a later addition re-rolls affected voxels). Table size is not a perf/storage concern — author generously but to taste (~100k is headroom, not a target).
|
||||
|
||||
BURNELLI (stakeholder, follow-on): a food-supply demand primitive keyed on biosphere_class (Mirror + frontier → import dependency; Compatible → local-food capable). Phase-2 economy consumer; does not block this ticket.
|
||||
|
||||
CONSTRAINTS
|
||||
- Authored input, not derivation (Arbour''s estate food must never be coin-flipped inedible).
|
||||
- Semantic axes stay clade-agnostic (Forest = canopy whether oak or mega-fungus); the register skins niches, it does not change gameplay semantics.
|
||||
- The mosaic geometry is chirality-blind — this work does NOT touch voxel geometry, only edibility/economy/naming/vocab.
|
||||
|
||||
Chirality decisions resolved (2026-07-02, with Jeroen):
|
||||
- Locked exemplars: Arbour=NativeCompatible, Solandar=NativeMirror, Keid=NativeCompatible, Oshima=TerraformedSterile.
|
||||
- Miri assignments accepted: GJ-524=Mirror, Freyburg(GJ-684A)=Compatible, Hạ Long(GJ-124)=Compatible (native-Compatible harvest, per her rationale).
|
||||
- USER OVERRIDES: Puerto Último(GJ-197)=NativeCompatible (breadbasket identity over Miri''s balance-motivated Mirror). Edict/Sirius(GJ-244A)=Airless — keep the world harsh/frozen (deliberately-inconvenient below-grade transit capital is load-bearing canon); no climate change; below-grade sealed habitat = no open-air biosphere.
|
||||
- Two-gate default rule ACCEPTED for canon-silent worlds: Gate 0 (habitability from atmosphere/hydrosphere frontmatter → Airless / TerraformedSterile / Alive), Gate 1 (Compatible vs Mirror from economic_base_primary; agriculture→Compatible near-lock, penal→Mirror, research→50/50 lever).
|
||||
- NAMING: Keid''s habitable body renamed Cadwal→(pending final pick; rec Linnaeus — pairs with Solander, its student naturalist; or Beagle). Capital stays ''Vance'' (already the author tribute). Miri''s IP flag (Cadwal = Vance''s exact book title) is the driver.
|
||||
- Still to APPLY: the 7 additive wiki continuity edits (Miri specified exact text), the biosphere_class frontmatter+schema+importer plumbing (workstream 3), and the Cadwal→<name> rename across wiki + the GJ166A atlas proposal.', 'backlog', 'medium', 'miri', NULL, 'D-247', '2026-07-02 20:30:57.222', '2026-07-02 21:23:34.730', NULL, 'd575ad0ccc78d862800236759d1af46a', 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 ('06FJ91R8RV38BJXGB2V55FP3B8', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'Biosphere register (D-247) — chirality bits, wiki continuity, biosphere_class authoring + alien-ecology vocab', 'Operationalize the D-247 native-biosphere premise. Full brief appended.
|
||||
|
||||
---
|
||||
BRIEF (2026-07-02) — governing record: D-247 (governance/decisions/content.md); consumer: D-246 / T-1084 mosaic register.
|
||||
|
||||
WHAT THIS IS
|
||||
D-247 established that habitable Reach worlds were alive before humans (the settlement timeline forbids terraforming from dead rock), and that an AUTHORED per-world chirality bit gates whether native life is edible. This ticket turns that canon into data + reconciled wiki + the vocabulary the mosaic needs. Owner: Miri (worldbuilding/continuity). Stakeholder: Burnelli (food-import economics). Co-maintainer for the vocab: Araminta (visual grammar, per the T-995 ObjectTag precedent).
|
||||
|
||||
THE MODEL (locked in D-247 — do not re-litigate, apply it):
|
||||
- `biosphere_class ∈ {NativeCompatible, NativeMirror, TerraformedSterile, Airless}`. It is an AUTHORED INPUT, never seed-derived — it follows a world''s economic/cultural identity.
|
||||
- Compatible ("levo") = edible; coupling *permits* an authored hazard but does not force it (most are benign gardens).
|
||||
- Mirror ("dextro") = inedible but *guaranteed* inert (can''t infect/poison — safe to study).
|
||||
- "Safe" ≠ "edible" — decoupled. Bio-safe is guaranteed on Mirror; food-safe is Compatible-only.
|
||||
- Achiral seam (always works either way): breathe air, drink water, mine ore, burn/build with native biomass. Only eat/metabolize/medicate/infect/chirally-poison are gated.
|
||||
- ~50/50 Compatible:Mirror is a DESIGN TARGET across canon-silent worlds, not a per-world draw.
|
||||
|
||||
WORKSTREAMS
|
||||
1. CHIRALITY ASSIGNMENT (Miri). Give every inhabited body a biosphere_class by authored identity: principled default from `economic_base_primary` + `planet_class` (food/drink exporters → Compatible; dead+maintained-atmosphere → TerraformedSterile), explicit overrides where canon speaks. Locked exemplars: Arbour (GJ-338B)=Compatible; Solandar (GJ-827)=Mirror; Keid (GJ-166A)=Compatible (preserved *from exploitation* of edible alien life — the stronger conservation motive; Solandar is the Mirror-inert preserve); Oshima (GJ-79)=TerraformedSterile. Hit the ~50/50 target across the silent remainder.
|
||||
2. WIKI CONTINUITY (Miri). Reconcile the entries that already gesture at native biochemistry so each is consistent with its assigned bit + the mechanism: GJ-827, GJ-524, GJ-166A, GJ-684A, GJ-197, GJ-79, GJ-124 (resolve whether Hạ Long''s "protein" is native-Compatible harvest or Earth aquaculture). Additive/explanatory, not contradictory — preserve deliberately-vague lines as intentional.
|
||||
3. biosphere_class AUTHORING PATH (asset-pipeline discipline — schema + source + values together, per .claude/rules/asset-pipeline.md). Add `biosphere_class` to wiki body frontmatter (where `substrate:` already lives), a nullable `bodies.biosphere_class` column in systems-schema.sql + MIGRATION_SQL, and importer read. NOT needed for D-246; lands with this pass.
|
||||
4. ALIEN-ECOLOGY VOCABULARY (Miri + Araminta) — feeds T-1084 directly. The mosaic palette is keyed on surface-class × climate-biome × biosphere-register (D-246 Palette-key). Provide the micro-habitat NAMES per biome × register: native-mirror niches get *alien* clade names (Solandar''s "five kingdoms" flavor); introduced-Earth niches get Earth names (wheatfield, orchard, pasture). Fill the fixed niche spine (canopy / understory / wet-hollow / pioneer / bare) per climate-biome (savanna gallery-copse, tundra thermokarst / lichen-heath, boreal muskeg / deadfall, …). DETERMINISM: the vocab must be fixed before T-1084 generates its first world (a later addition re-rolls affected voxels). Table size is not a perf/storage concern — author generously but to taste (~100k is headroom, not a target).
|
||||
|
||||
BURNELLI (stakeholder, follow-on): a food-supply demand primitive keyed on biosphere_class (Mirror + frontier → import dependency; Compatible → local-food capable). Phase-2 economy consumer; does not block this ticket.
|
||||
|
||||
CONSTRAINTS
|
||||
- Authored input, not derivation (Arbour''s estate food must never be coin-flipped inedible).
|
||||
- Semantic axes stay clade-agnostic (Forest = canopy whether oak or mega-fungus); the register skins niches, it does not change gameplay semantics.
|
||||
- The mosaic geometry is chirality-blind — this work does NOT touch voxel geometry, only edibility/economy/naming/vocab.
|
||||
|
||||
Chirality decisions resolved (2026-07-02, with Jeroen):
|
||||
- Locked exemplars: Arbour=NativeCompatible, Solandar=NativeMirror, Keid=NativeCompatible, Oshima=TerraformedSterile.
|
||||
- Miri assignments accepted: GJ-524=Mirror, Freyburg(GJ-684A)=Compatible, Hạ Long(GJ-124)=Compatible (native-Compatible harvest, per her rationale).
|
||||
- USER OVERRIDES: Puerto Último(GJ-197)=NativeCompatible (breadbasket identity over Miri''s balance-motivated Mirror). Edict/Sirius(GJ-244A)=Airless — keep the world harsh/frozen (deliberately-inconvenient below-grade transit capital is load-bearing canon); no climate change; below-grade sealed habitat = no open-air biosphere.
|
||||
- Two-gate default rule ACCEPTED for canon-silent worlds: Gate 0 (habitability from atmosphere/hydrosphere frontmatter → Airless / TerraformedSterile / Alive), Gate 1 (Compatible vs Mirror from economic_base_primary; agriculture→Compatible near-lock, penal→Mirror, research→50/50 lever).
|
||||
- NAMING: Keid''s habitable body renamed Cadwal→(pending final pick; rec Linnaeus — pairs with Solander, its student naturalist; or Beagle). Capital stays ''Vance'' (already the author tribute). Miri''s IP flag (Cadwal = Vance''s exact book title) is the driver.
|
||||
- Still to APPLY: the 7 additive wiki continuity edits (Miri specified exact text), the biosphere_class frontmatter+schema+importer plumbing (workstream 3), and the Cadwal→<name> rename across wiki + the GJ166A atlas proposal.
|
||||
|
||||
NAMING CONFIRMED: Keid''s habitable body Cadwal → Linnaeus (the great classifier; ties to Solander/Solandar taxonomy + the ''five kingdoms''). Capital stays Vance.', 'backlog', 'medium', 'miri', NULL, 'D-247', '2026-07-02 20:30:57.222', '2026-07-02 21:24:12.734', NULL, '4465986381752d527f9ac2cd9e58311f', 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);
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
# Intra-class micro-habitat mosaic palettes — D-246, T-1084.
|
||||
#
|
||||
# SOURCE-CANONICAL MIRROR. The authoritative runtime source today is the embedded
|
||||
# `atlas::mosaic` const tables; this file mirrors them and is the file to edit when
|
||||
# tuning weights (the same embedded-default + TOML-mirror pattern as
|
||||
# `climate_constants.toml`; runtime loading lands with production tuning). Keep the
|
||||
# two in sync by hand until then.
|
||||
#
|
||||
# Each entry is a micro-habitat = a distinct D-228 axis-triple the mosaic can paint,
|
||||
# plus its relative `weight` and moisture `affinity` (any | wet | dry). Display names
|
||||
# are derived labels (D-228) — scree/talus and outcrop/ledge collapse to one triple.
|
||||
#
|
||||
# This is the TEMPERATE / NATIVE baseline register. Biome variants (savanna, tundra,
|
||||
# boreal) and the introduced-Earth register extend the table via T-1085, before
|
||||
# generation (a later addition re-rolls affected voxels — D-246 determinism rule).
|
||||
#
|
||||
# v1 (T-1084) applies only the Wetland / Forest / Grassland palettes, and only their
|
||||
# vegetation + water — terrain material is left to the family generator so the D-239
|
||||
# §8 lithology law is untouched. The Sand / Rock / Gravel / Lava palettes below are
|
||||
# authored for the §8-aware material follow-up.
|
||||
|
||||
[[wetland]]
|
||||
name = "clearing"
|
||||
terrain = "Wetland"
|
||||
vegetation = "Barren"
|
||||
water = "Dry"
|
||||
weight = 15
|
||||
affinity = "dry"
|
||||
|
||||
[[wetland]]
|
||||
name = "copse"
|
||||
terrain = "Wetland"
|
||||
vegetation = "Forest"
|
||||
water = "Dry"
|
||||
weight = 10
|
||||
affinity = "dry"
|
||||
|
||||
[[wetland]]
|
||||
name = "creek"
|
||||
terrain = "Wetland"
|
||||
vegetation = "Barren"
|
||||
water = "Shallow"
|
||||
weight = 10
|
||||
affinity = "wet"
|
||||
|
||||
[[wetland]]
|
||||
name = "meadow"
|
||||
terrain = "Wetland"
|
||||
vegetation = "Meadow"
|
||||
water = "Dry"
|
||||
weight = 15
|
||||
affinity = "dry"
|
||||
|
||||
[[wetland]]
|
||||
name = "bog"
|
||||
terrain = "Wetland"
|
||||
vegetation = "Scrub"
|
||||
water = "Shallow"
|
||||
weight = 12
|
||||
affinity = "wet"
|
||||
|
||||
[[wetland]]
|
||||
name = "marsh"
|
||||
terrain = "Wetland"
|
||||
vegetation = "Grass"
|
||||
water = "Shallow"
|
||||
weight = 12
|
||||
affinity = "wet"
|
||||
|
||||
[[forest]]
|
||||
name = "dense stand"
|
||||
terrain = "Soil"
|
||||
vegetation = "Forest"
|
||||
water = "Dry"
|
||||
weight = 40
|
||||
affinity = "any"
|
||||
|
||||
[[forest]]
|
||||
name = "glade"
|
||||
terrain = "Soil"
|
||||
vegetation = "Grass"
|
||||
water = "Dry"
|
||||
weight = 15
|
||||
affinity = "dry"
|
||||
|
||||
[[forest]]
|
||||
name = "deadfall"
|
||||
terrain = "Soil"
|
||||
vegetation = "Deadfall"
|
||||
water = "Dry"
|
||||
weight = 10
|
||||
affinity = "any"
|
||||
|
||||
[[forest]]
|
||||
name = "brook"
|
||||
terrain = "Soil"
|
||||
vegetation = "Thicket"
|
||||
water = "Shallow"
|
||||
weight = 10
|
||||
affinity = "wet"
|
||||
|
||||
[[forest]]
|
||||
name = "fern undergrowth"
|
||||
terrain = "Soil"
|
||||
vegetation = "Thicket"
|
||||
water = "Dry"
|
||||
weight = 15
|
||||
affinity = "any"
|
||||
|
||||
[[grassland]]
|
||||
name = "tussock"
|
||||
terrain = "Soil"
|
||||
vegetation = "Grass"
|
||||
water = "Dry"
|
||||
weight = 40
|
||||
affinity = "any"
|
||||
|
||||
[[grassland]]
|
||||
name = "scrub island"
|
||||
terrain = "Soil"
|
||||
vegetation = "Scrub"
|
||||
water = "Dry"
|
||||
weight = 18
|
||||
affinity = "dry"
|
||||
|
||||
[[grassland]]
|
||||
name = "wildflower"
|
||||
terrain = "Soil"
|
||||
vegetation = "Meadow"
|
||||
water = "Dry"
|
||||
weight = 17
|
||||
affinity = "dry"
|
||||
|
||||
[[grassland]]
|
||||
name = "dry wash"
|
||||
terrain = "Gravel"
|
||||
vegetation = "Barren"
|
||||
water = "Dry"
|
||||
weight = 10
|
||||
affinity = "wet"
|
||||
|
||||
# ── Follow-up (§8-aware material pass): Sand / Rock / Gravel / Lava ──────────────
|
||||
|
||||
[[sand]]
|
||||
name = "dune crest"
|
||||
terrain = "Sand"
|
||||
vegetation = "Barren"
|
||||
water = "Dry"
|
||||
weight = 25
|
||||
affinity = "dry"
|
||||
|
||||
[[sand]]
|
||||
name = "slack"
|
||||
terrain = "Sand"
|
||||
vegetation = "Grass"
|
||||
water = "Dry"
|
||||
weight = 15
|
||||
affinity = "wet"
|
||||
|
||||
[[sand]]
|
||||
name = "hardpan"
|
||||
terrain = "Hardpan"
|
||||
vegetation = "Barren"
|
||||
water = "Dry"
|
||||
weight = 20
|
||||
affinity = "dry"
|
||||
|
||||
[[sand]]
|
||||
name = "oasis"
|
||||
terrain = "Sand"
|
||||
vegetation = "Forest"
|
||||
water = "Shallow"
|
||||
weight = 5
|
||||
affinity = "wet"
|
||||
|
||||
[[sand]]
|
||||
name = "scrub clump"
|
||||
terrain = "Sand"
|
||||
vegetation = "Scrub"
|
||||
water = "Dry"
|
||||
weight = 15
|
||||
affinity = "any"
|
||||
|
||||
[[rock]]
|
||||
name = "outcrop"
|
||||
terrain = "Rock"
|
||||
vegetation = "Barren"
|
||||
water = "Dry"
|
||||
weight = 45
|
||||
affinity = "any"
|
||||
|
||||
[[rock]]
|
||||
name = "scree"
|
||||
terrain = "Scree"
|
||||
vegetation = "Barren"
|
||||
water = "Dry"
|
||||
weight = 30
|
||||
affinity = "any"
|
||||
|
||||
[[rock]]
|
||||
name = "lichen"
|
||||
terrain = "Rock"
|
||||
vegetation = "Lichen"
|
||||
water = "Dry"
|
||||
weight = 25
|
||||
affinity = "any"
|
||||
|
||||
[[gravel]]
|
||||
name = "bar"
|
||||
terrain = "Gravel"
|
||||
vegetation = "Barren"
|
||||
water = "Dry"
|
||||
weight = 45
|
||||
affinity = "any"
|
||||
|
||||
[[gravel]]
|
||||
name = "active channel"
|
||||
terrain = "Gravel"
|
||||
vegetation = "Barren"
|
||||
water = "Shallow"
|
||||
weight = 25
|
||||
affinity = "wet"
|
||||
|
||||
[[gravel]]
|
||||
name = "vegetated island"
|
||||
terrain = "Gravel"
|
||||
vegetation = "Scrub"
|
||||
water = "Dry"
|
||||
weight = 30
|
||||
affinity = "dry"
|
||||
|
||||
[[lava]]
|
||||
name = "fresh sheet"
|
||||
terrain = "Lava"
|
||||
vegetation = "Barren"
|
||||
water = "Dry"
|
||||
weight = 50
|
||||
affinity = "any"
|
||||
|
||||
[[lava]]
|
||||
name = "weathered crust"
|
||||
terrain = "Lava"
|
||||
vegetation = "Lichen"
|
||||
water = "Dry"
|
||||
weight = 30
|
||||
affinity = "any"
|
||||
|
||||
[[lava]]
|
||||
name = "pioneer scrub"
|
||||
terrain = "Lava"
|
||||
vegetation = "Scrub"
|
||||
water = "Dry"
|
||||
weight = 20
|
||||
affinity = "dry"
|
||||
@@ -112,6 +112,12 @@ pub struct ContrastMetrics {
|
||||
/// voxel tier carries mid-scale relief. Measured over the 0.25–2 km band a character
|
||||
/// traverses — not the single 64 m sample chunk, which is too narrow to register it.
|
||||
pub voxel_relief_m: i32,
|
||||
/// Minimum distinct micro-habitat axis-triples (terrain × vegetation × water) found
|
||||
/// in any single sampled VEGETATED-land district (T-1084, D-246). The D-245 intra-class
|
||||
/// variety screen: a vegetated patch must show ≥K distinct micro-features, never 100%
|
||||
/// one — near 1 on the pre-mosaic uniform world ("Wetland 100%"), ≥3 once the mosaic
|
||||
/// textures the class. Barren / water patches are excluded (legitimately uniform).
|
||||
pub micro_habitat_distinct: usize,
|
||||
}
|
||||
|
||||
/// Coherence checks — does the world render as a *caused* place? Pass/fail counts
|
||||
@@ -181,8 +187,9 @@ pub fn analyze(
|
||||
.values()
|
||||
.map(|d| format!("{:?}", d.vegetation_class)),
|
||||
),
|
||||
terrain_materials: 0, // filled from the voxel sample below
|
||||
voxel_relief_m: 0, // filled from the relief transect below
|
||||
terrain_materials: 0, // filled from the voxel sample below
|
||||
voxel_relief_m: 0, // filled from the relief transect below
|
||||
micro_habitat_distinct: 0, // filled from the per-district triple sets below
|
||||
};
|
||||
|
||||
// ── Voxel-derived metrics over a deterministic seeded spread of districts ──
|
||||
@@ -196,6 +203,9 @@ pub fn analyze(
|
||||
// Mid-scale relief accumulators (T-1081): sum of per-district transect ranges over
|
||||
// land districts, and the count contributing, → mean within-district relief.
|
||||
let (mut relief_range_sum, mut relief_district_count) = (0i64, 0i64);
|
||||
// Intra-class variety (T-1084): the minimum distinct micro-habitat triples over the
|
||||
// sampled vegetated-land districts — the worst patch, per the D-245 per-patch floor.
|
||||
let mut min_habitat_distinct = usize::MAX;
|
||||
|
||||
let keys: Vec<DistrictPos> = districts.keys().copied().collect();
|
||||
for idx in sample_indices(world_seed, keys.len(), VOXEL_SAMPLE_DISTRICTS) {
|
||||
@@ -210,6 +220,7 @@ pub fn analyze(
|
||||
// Elevation accumulators for the drainage-monotonicity proxy.
|
||||
let (mut wet_elev_sum, mut wet_n) = (0i64, 0i64);
|
||||
let (mut dry_elev_sum, mut dry_n) = (0i64, 0i64);
|
||||
let mut habitat_triples: BTreeSet<String> = BTreeSet::new();
|
||||
|
||||
for sx in (0..CHUNK_M as usize).step_by(VOXEL_SAMPLE_STRIDE) {
|
||||
for sy in (0..CHUNK_M as usize).step_by(VOXEL_SAMPLE_STRIDE) {
|
||||
@@ -217,6 +228,10 @@ pub fn analyze(
|
||||
let ty = chunk.1 * CHUNK_M + sy as i32;
|
||||
let col = derive_voxel_column(world_seed, body_id, prof, &ctx, tx, ty);
|
||||
terrain_set.insert(format!("{:?}", col.terrain));
|
||||
habitat_triples.insert(format!(
|
||||
"{:?}/{:?}/{:?}",
|
||||
col.terrain, col.vegetation, col.water
|
||||
));
|
||||
if col.vegetation != Vegetation::Barren {
|
||||
any_veg = true;
|
||||
}
|
||||
@@ -231,6 +246,12 @@ pub fn analyze(
|
||||
}
|
||||
}
|
||||
|
||||
// Intra-class micro-habitat variety (T-1084): distinct habitat triples in this
|
||||
// patch; the report tracks the worst (minimum) vegetated patch.
|
||||
if any_veg {
|
||||
min_habitat_distinct = min_habitat_distinct.min(habitat_triples.len());
|
||||
}
|
||||
|
||||
// Water coherence (T-1082): water-present districts must render wet voxels.
|
||||
if prof.ocean_fraction_q >= WATER_PRESENCE_Q || ctx.has_active_channel {
|
||||
coh.water_districts += 1;
|
||||
@@ -294,6 +315,11 @@ pub fn analyze(
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let micro_habitat_distinct = if min_habitat_distinct == usize::MAX {
|
||||
0
|
||||
} else {
|
||||
min_habitat_distinct
|
||||
};
|
||||
|
||||
BelievabilityReport {
|
||||
body_id: body_id.to_string(),
|
||||
@@ -303,6 +329,7 @@ pub fn analyze(
|
||||
contrast: ContrastMetrics {
|
||||
terrain_materials: terrain_set.len(),
|
||||
voxel_relief_m,
|
||||
micro_habitat_distinct,
|
||||
..contrast_scalar
|
||||
},
|
||||
coherence: coh,
|
||||
@@ -346,6 +373,14 @@ pub fn evaluate_criteria(r: &BelievabilityReport) -> Vec<Criterion> {
|
||||
pass: c.terrain_materials >= 2,
|
||||
detail: format!("{} distinct materials", c.terrain_materials),
|
||||
},
|
||||
Criterion {
|
||||
name: "intra-class variety",
|
||||
pass: c.micro_habitat_distinct >= 3,
|
||||
detail: format!(
|
||||
"min {} distinct micro-habitats per vegetated patch",
|
||||
c.micro_habitat_distinct
|
||||
),
|
||||
},
|
||||
Criterion {
|
||||
name: "water renders wet",
|
||||
pass: pct(h.water_districts_wet, h.water_districts) >= 50,
|
||||
|
||||
@@ -32,6 +32,11 @@ const OCTAVE_WAVELENGTHS_M: [f64; 4] = [32_768.0, 16_384.0, 8_192.0, 4_096.0];
|
||||
/// the district size so the relief never competes with `elev_q`'s district-scale role.
|
||||
const VOXEL_OCTAVE_WAVELENGTHS_M: [f64; 4] = [1_024.0, 512.0, 256.0, 128.0];
|
||||
|
||||
/// Sub-chunk mosaic octave wavelengths in metres — the ≈8–64 m band, finer than the
|
||||
/// [`VOXEL_OCTAVE_WAVELENGTHS_M`] sub-district band, so a micro-habitat patch reads as
|
||||
/// a distinct blob *inside* a single 64 m chunk (T-1084, D-246). Coarsest first.
|
||||
const MOSAIC_OCTAVE_WAVELENGTHS_M: [f64; 4] = [64.0, 32.0, 16.0, 8.0];
|
||||
|
||||
/// Deterministic lattice value in `[-1, 1)` for an integer noise cell.
|
||||
#[inline]
|
||||
fn lattice(seed: u64, ix: i64, iy: i64) -> f64 {
|
||||
@@ -95,6 +100,35 @@ pub fn voxel_relief(seed: u64, wx: f64, wy: f64, envelope: f64, ruggedness: f64)
|
||||
)
|
||||
}
|
||||
|
||||
/// The sub-chunk micro-habitat **selection field** in `[0, 1]` — a smooth,
|
||||
/// body-global, position-keyed multi-octave value-noise scalar the mosaic pass
|
||||
/// thresholds against a per-class palette's cumulative weights (T-1084, D-246).
|
||||
///
|
||||
/// Unlike [`voxel_relief`] this is not an elevation perturbation: it carries no
|
||||
/// envelope and no ridge folding — it is a plain fBm remapped to `[0, 1]` whose only
|
||||
/// job is to pick *which* micro-habitat a voxel lands in, as spatially-coherent blobs
|
||||
/// (low-frequency → patches, never per-tile salt-and-pepper). The seed must be
|
||||
/// **body-global** (constant across the body); the position `(wx, wy)` carries the
|
||||
/// variation, exactly as for [`voxel_relief`].
|
||||
pub fn voxel_mosaic(seed: u64, wx: f64, wy: f64) -> f64 {
|
||||
let mut sum = 0.0;
|
||||
let mut amp = 1.0;
|
||||
let mut norm = 0.0;
|
||||
for (i, &wl) in MOSAIC_OCTAVE_WAVELENGTHS_M.iter().enumerate() {
|
||||
let n = value_noise(
|
||||
seed.wrapping_add((i as u64).wrapping_mul(0x1000)),
|
||||
wx,
|
||||
wy,
|
||||
wl,
|
||||
);
|
||||
sum += n * amp;
|
||||
norm += amp;
|
||||
amp *= 0.5;
|
||||
}
|
||||
let fbm = sum / norm; // ≈ [-1, 1]
|
||||
((fbm + 1.0) * 0.5).clamp(0.0, 1.0)
|
||||
}
|
||||
|
||||
/// Shared adaptive-fBm core for [`terrain_detail`] and [`voxel_relief`] — the only
|
||||
/// difference between the two tiers is the octave wavelength band. Returns the
|
||||
/// enveloped, ruggedness-modulated perturbation (roughly `[-envelope, +envelope]`).
|
||||
@@ -235,4 +269,48 @@ mod tests {
|
||||
max - min
|
||||
);
|
||||
}
|
||||
|
||||
// ── voxel_mosaic (T-1084): sub-chunk selection field ─────────────────────
|
||||
|
||||
#[test]
|
||||
fn voxel_mosaic_deterministic_and_in_range() {
|
||||
let a = voxel_mosaic(42, 12_345.0, -6_789.0);
|
||||
let b = voxel_mosaic(42, 12_345.0, -6_789.0);
|
||||
assert_eq!(a, b);
|
||||
for i in 0..400 {
|
||||
let v = voxel_mosaic(99, i as f64 * 137.0, i as f64 * -91.0);
|
||||
assert!((0.0..=1.0).contains(&v), "v={v} out of [0,1] at {i}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn voxel_mosaic_varies_at_sub_chunk_scale() {
|
||||
// The mosaic must produce DISTINCT values within a single 64 m chunk — the
|
||||
// blob scale that reads as patches, not the district-scale voxel_relief band.
|
||||
// Steps stay well under 64 m and non-aligned to the octave wavelengths.
|
||||
let seed = 7;
|
||||
let vals: Vec<f64> = (0..16)
|
||||
.map(|i| voxel_mosaic(seed, i as f64 * 3.7, i as f64 * 2.9))
|
||||
.collect();
|
||||
let min = vals.iter().cloned().fold(f64::INFINITY, f64::min);
|
||||
let max = vals.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
|
||||
assert!(
|
||||
max - min > 0.1,
|
||||
"mosaic field is near-flat inside a chunk: range {}",
|
||||
max - min
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn voxel_mosaic_is_smooth_not_salt_and_pepper() {
|
||||
// C¹ value noise → a small position step is a small field change, so patches
|
||||
// are coherent blobs, never the per-tile scatter the mosaic replaces.
|
||||
let base = voxel_mosaic(11, 5_000.0, 5_000.0);
|
||||
let near = voxel_mosaic(11, 5_000.5, 5_000.0);
|
||||
assert!(
|
||||
(base - near).abs() < 0.05,
|
||||
"0.5 m step jumped by {}",
|
||||
(base - near).abs()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ pub mod gen_queue;
|
||||
pub mod heightmap;
|
||||
pub mod layer1;
|
||||
pub mod layer_proxy;
|
||||
pub mod mosaic;
|
||||
pub mod plugin;
|
||||
pub mod region_profile;
|
||||
pub mod road_graph;
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
//! Intra-class micro-habitat mosaic (D-246, T-1084) — the sub-chunk palette that
|
||||
//! modulates the D-228 axes into a spatially-coherent, relief/moisture-conditioned
|
||||
//! mosaic of micro-habitats within a single terrain class.
|
||||
//!
|
||||
//! Mechanism (D-246): [`detail_scatter::voxel_mosaic`](crate::atlas::detail_scatter::voxel_mosaic)
|
||||
//! supplies a smooth, body-global selection field in `[0, 1]`; this module maps a
|
||||
//! voxel's base surface-class to a weighted palette of micro-habitats and picks one
|
||||
//! by thresholding that field against cumulative weights — conditioned on a local
|
||||
//! wetness signal (relief micro-thalweg + district moisture) so wet habitats
|
||||
//! (creek / bog / marsh) land in the lows and dry ones (meadow / glade / clearing) on
|
||||
//! the rises. Selection is **integer basis-points** (D-010): the f64 field and
|
||||
//! wetness are the sanctioned positional inputs, truncated before the integer decision.
|
||||
//!
|
||||
//! Micro-habitats are distinct D-228 **axis-triples**, not names — several display
|
||||
//! names (scree/talus, outcrop/ledge) collapse to one triple, the name being a
|
||||
//! downstream derived label (D-228).
|
||||
//!
|
||||
//! Data residency (D-246): the palette is source-canonical in
|
||||
//! `server/data/mosaic_constants.toml`; the embedded consts here mirror it and are
|
||||
//! the authoritative runtime source today — the same embedded-default + TOML-mirror
|
||||
//! pattern as `climate_constants.toml` (runtime loading lands with production tuning).
|
||||
//! This is the **temperate / native baseline** register; biome variants
|
||||
//! (savanna / tundra / boreal) and the introduced-Earth register extend the table via
|
||||
//! T-1085 before generation.
|
||||
|
||||
use crate::atlas::voxel::{TerrainMaterial, Vegetation, Water};
|
||||
|
||||
/// How the local wetness signal reweights a micro-habitat.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MoistureAffinity {
|
||||
/// Unconditioned — flat weight across wet and dry.
|
||||
Any,
|
||||
/// Favoured in the wet lows (creek / bog / marsh / oasis / active channel).
|
||||
Wet,
|
||||
/// Favoured on the dry rises (meadow / glade / clearing / dune crest / hardpan).
|
||||
Dry,
|
||||
}
|
||||
|
||||
/// One micro-habitat: a distinct D-228 axis-triple the mosaic can paint, plus its
|
||||
/// relative weight and moisture affinity. `name` is documentation / a derived-label
|
||||
/// seed only — it is never stored on the voxel.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct MicroHabitat {
|
||||
pub name: &'static str,
|
||||
pub terrain: TerrainMaterial,
|
||||
pub vegetation: Vegetation,
|
||||
pub water: Water,
|
||||
pub weight: i32,
|
||||
pub affinity: MoistureAffinity,
|
||||
}
|
||||
|
||||
/// The surface-class palette key (D-246) — derived from the base voxel's terrain +
|
||||
/// dominant vegetation, never stored.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SurfaceClass {
|
||||
Wetland,
|
||||
Forest,
|
||||
Grassland,
|
||||
Sand,
|
||||
Rock,
|
||||
Gravel,
|
||||
Lava,
|
||||
}
|
||||
|
||||
impl SurfaceClass {
|
||||
/// Palette key from the family generator's base output (D-246 Palette key):
|
||||
/// terrain material selects directly, except `Soil` splits Forest vs Grassland
|
||||
/// by the district's dominant vegetation. `Hardpan`/`Scree` never arrive as a
|
||||
/// *base* material (they are mosaic outputs) but map defensively.
|
||||
pub fn from_base(terrain: TerrainMaterial, dominant_vegetation: Vegetation) -> Self {
|
||||
match terrain {
|
||||
TerrainMaterial::Wetland => SurfaceClass::Wetland,
|
||||
TerrainMaterial::Sand | TerrainMaterial::Hardpan => SurfaceClass::Sand,
|
||||
TerrainMaterial::Rock | TerrainMaterial::Scree => SurfaceClass::Rock,
|
||||
TerrainMaterial::Gravel => SurfaceClass::Gravel,
|
||||
TerrainMaterial::Lava => SurfaceClass::Lava,
|
||||
TerrainMaterial::Soil => {
|
||||
if matches!(dominant_vegetation, Vegetation::Forest) {
|
||||
SurfaceClass::Forest
|
||||
} else {
|
||||
SurfaceClass::Grassland
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shorthand for the const tables below.
|
||||
use MoistureAffinity::{Any, Dry, Wet};
|
||||
use TerrainMaterial as T;
|
||||
use Vegetation as V;
|
||||
use Water as W;
|
||||
|
||||
const fn h(
|
||||
name: &'static str,
|
||||
terrain: TerrainMaterial,
|
||||
vegetation: Vegetation,
|
||||
water: Water,
|
||||
weight: i32,
|
||||
affinity: MoistureAffinity,
|
||||
) -> MicroHabitat {
|
||||
MicroHabitat {
|
||||
name,
|
||||
terrain,
|
||||
vegetation,
|
||||
water,
|
||||
weight,
|
||||
affinity,
|
||||
}
|
||||
}
|
||||
|
||||
// ── Temperate / native baseline palettes (mirror of mosaic_constants.toml) ──────
|
||||
|
||||
const WETLAND: &[MicroHabitat] = &[
|
||||
h("clearing", T::Wetland, V::Barren, W::Dry, 15, Dry),
|
||||
h("copse", T::Wetland, V::Forest, W::Dry, 10, Dry),
|
||||
h("creek", T::Wetland, V::Barren, W::Shallow, 10, Wet),
|
||||
h("meadow", T::Wetland, V::Meadow, W::Dry, 15, Dry),
|
||||
h("bog", T::Wetland, V::Scrub, W::Shallow, 12, Wet),
|
||||
h("marsh", T::Wetland, V::Grass, W::Shallow, 12, Wet),
|
||||
];
|
||||
|
||||
const FOREST: &[MicroHabitat] = &[
|
||||
h("dense stand", T::Soil, V::Forest, W::Dry, 40, Any),
|
||||
h("glade", T::Soil, V::Grass, W::Dry, 15, Dry),
|
||||
h("deadfall", T::Soil, V::Deadfall, W::Dry, 10, Any),
|
||||
h("brook", T::Soil, V::Thicket, W::Shallow, 10, Wet),
|
||||
h("fern undergrowth", T::Soil, V::Thicket, W::Dry, 15, Any),
|
||||
];
|
||||
|
||||
const GRASSLAND: &[MicroHabitat] = &[
|
||||
h("tussock", T::Soil, V::Grass, W::Dry, 40, Any),
|
||||
h("scrub island", T::Soil, V::Scrub, W::Dry, 18, Dry),
|
||||
h("wildflower", T::Soil, V::Meadow, W::Dry, 17, Dry),
|
||||
h("dry wash", T::Gravel, V::Barren, W::Dry, 10, Wet),
|
||||
];
|
||||
|
||||
const SAND: &[MicroHabitat] = &[
|
||||
h("dune crest", T::Sand, V::Barren, W::Dry, 25, Dry),
|
||||
h("slack", T::Sand, V::Grass, W::Dry, 15, Wet),
|
||||
h("hardpan", T::Hardpan, V::Barren, W::Dry, 20, Dry),
|
||||
h("oasis", T::Sand, V::Forest, W::Shallow, 5, Wet),
|
||||
h("scrub clump", T::Sand, V::Scrub, W::Dry, 15, Any),
|
||||
];
|
||||
|
||||
const ROCK: &[MicroHabitat] = &[
|
||||
h("outcrop", T::Rock, V::Barren, W::Dry, 45, Any),
|
||||
h("scree", T::Scree, V::Barren, W::Dry, 30, Any),
|
||||
h("lichen", T::Rock, V::Lichen, W::Dry, 25, Any),
|
||||
];
|
||||
|
||||
const GRAVEL: &[MicroHabitat] = &[
|
||||
h("bar", T::Gravel, V::Barren, W::Dry, 45, Any),
|
||||
h("active channel", T::Gravel, V::Barren, W::Shallow, 25, Wet),
|
||||
h("vegetated island", T::Gravel, V::Scrub, W::Dry, 30, Dry),
|
||||
];
|
||||
|
||||
const LAVA: &[MicroHabitat] = &[
|
||||
h("fresh sheet", T::Lava, V::Barren, W::Dry, 50, Any),
|
||||
h("weathered crust", T::Lava, V::Lichen, W::Dry, 30, Any),
|
||||
h("pioneer scrub", T::Lava, V::Scrub, W::Dry, 20, Dry),
|
||||
];
|
||||
|
||||
/// The embedded palette for a surface-class (temperate / native baseline).
|
||||
pub fn palette(class: SurfaceClass) -> &'static [MicroHabitat] {
|
||||
match class {
|
||||
SurfaceClass::Wetland => WETLAND,
|
||||
SurfaceClass::Forest => FOREST,
|
||||
SurfaceClass::Grassland => GRASSLAND,
|
||||
SurfaceClass::Sand => SAND,
|
||||
SurfaceClass::Rock => ROCK,
|
||||
SurfaceClass::Gravel => GRAVEL,
|
||||
SurfaceClass::Lava => LAVA,
|
||||
}
|
||||
}
|
||||
|
||||
/// Moisture-conditioned effective weight of one entry, in the same integer units as
|
||||
/// `weight`. `wet_bp` is the local wetness in basis-points (0..10000). Wet/Dry
|
||||
/// affinities scale within `[0.2×, 1.0×]` (a 0.2 floor so a habitat can still appear
|
||||
/// off-affinity); `Any` is flat.
|
||||
fn effective_weight(hab: &MicroHabitat, wet_bp: i32) -> i64 {
|
||||
let factor_bp: i32 = match hab.affinity {
|
||||
MoistureAffinity::Any => 10_000,
|
||||
MoistureAffinity::Wet => 2_000 + 8 * wet_bp / 10, // 2000..10000
|
||||
MoistureAffinity::Dry => 2_000 + 8 * (10_000 - wet_bp) / 10,
|
||||
};
|
||||
(hab.weight as i64 * factor_bp as i64) / 10_000
|
||||
}
|
||||
|
||||
/// Pick a micro-habitat from `palette` using the mosaic selection field and a local
|
||||
/// wetness signal. Integer basis-points (D-010): `field` and `wetness` are the
|
||||
/// sanctioned f64 positional inputs, truncated to bp before the integer decision.
|
||||
///
|
||||
/// - `field`: the [`voxel_mosaic`](crate::atlas::detail_scatter::voxel_mosaic) scalar in `[0, 1]`.
|
||||
/// - `wetness`: local wet/dry signal in `[0, 1]` (relief micro-thalweg + moisture).
|
||||
pub fn select(palette: &[MicroHabitat], field: f64, wetness: f64) -> &MicroHabitat {
|
||||
debug_assert!(!palette.is_empty(), "mosaic palette must be non-empty");
|
||||
let wet_bp = (wetness.clamp(0.0, 1.0) * 10_000.0) as i32; // truncate (D-010)
|
||||
let total: i64 = palette
|
||||
.iter()
|
||||
.map(|hab| effective_weight(hab, wet_bp))
|
||||
.sum::<i64>()
|
||||
.max(1);
|
||||
let field_bp = (field.clamp(0.0, 1.0) * 10_000.0) as i64; // truncate (D-010)
|
||||
let mut threshold = field_bp * total / 10_000;
|
||||
for hab in palette {
|
||||
let w = effective_weight(hab, wet_bp);
|
||||
if threshold < w {
|
||||
return hab;
|
||||
}
|
||||
threshold -= w;
|
||||
}
|
||||
// field == 1.0 edge → last bucket.
|
||||
palette.last().expect("palette non-empty")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn from_base_maps_soil_by_vegetation() {
|
||||
assert_eq!(
|
||||
SurfaceClass::from_base(T::Soil, V::Forest),
|
||||
SurfaceClass::Forest
|
||||
);
|
||||
assert_eq!(
|
||||
SurfaceClass::from_base(T::Soil, V::Grass),
|
||||
SurfaceClass::Grassland
|
||||
);
|
||||
assert_eq!(
|
||||
SurfaceClass::from_base(T::Wetland, V::Barren),
|
||||
SurfaceClass::Wetland
|
||||
);
|
||||
assert_eq!(
|
||||
SurfaceClass::from_base(T::Lava, V::Barren),
|
||||
SurfaceClass::Lava
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_palette_nonempty_and_positively_weighted() {
|
||||
for class in [
|
||||
SurfaceClass::Wetland,
|
||||
SurfaceClass::Forest,
|
||||
SurfaceClass::Grassland,
|
||||
SurfaceClass::Sand,
|
||||
SurfaceClass::Rock,
|
||||
SurfaceClass::Gravel,
|
||||
SurfaceClass::Lava,
|
||||
] {
|
||||
let p = palette(class);
|
||||
assert!(!p.is_empty(), "{class:?} palette empty");
|
||||
assert!(
|
||||
p.iter().all(|hab| hab.weight > 0),
|
||||
"{class:?} has a non-positive weight"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_is_deterministic_and_spans_the_palette() {
|
||||
let p = palette(SurfaceClass::Wetland);
|
||||
assert_eq!(select(p, 0.37, 0.5).name, select(p, 0.37, 0.5).name);
|
||||
let names: std::collections::BTreeSet<_> = (0..100)
|
||||
.map(|i| select(p, i as f64 / 100.0, 0.5).name)
|
||||
.collect();
|
||||
assert!(
|
||||
names.len() >= 3,
|
||||
"selection collapsed to {} habitats",
|
||||
names.len()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wetness_favours_wet_habitats_over_dry_signal() {
|
||||
let p = palette(SurfaceClass::Wetland);
|
||||
let wet_count = |wetness: f64| {
|
||||
(0..100)
|
||||
.filter(|i| {
|
||||
matches!(
|
||||
select(p, *i as f64 / 100.0, wetness).affinity,
|
||||
MoistureAffinity::Wet
|
||||
)
|
||||
})
|
||||
.count()
|
||||
};
|
||||
assert!(
|
||||
wet_count(0.95) > wet_count(0.05),
|
||||
"wet conditioning did not favour wet habitats: {} vs {}",
|
||||
wet_count(0.95),
|
||||
wet_count(0.05)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,12 @@ pub enum TerrainMaterial {
|
||||
Wetland = 4,
|
||||
/// Lava — sheets/shield slopes + tubes, immature drainage (D-239 §8).
|
||||
Lava = 5,
|
||||
/// Hardpan — compacted flat crust (D-246 mosaic). Hard, fast footing; does not
|
||||
/// slump to an angle of repose like sand.
|
||||
Hardpan = 6,
|
||||
/// Scree — loose angular rock debris (D-246 mosaic). Unstable footing; the
|
||||
/// derived name is "talus" at a cliff foot, "scree" on an open slope.
|
||||
Scree = 7,
|
||||
}
|
||||
|
||||
/// Built surface over natural ground (D-228).
|
||||
@@ -128,6 +134,15 @@ pub enum Vegetation {
|
||||
Crop = 5,
|
||||
/// Cleared — formerly vegetated, stripped (player/sim action).
|
||||
Cleared = 6,
|
||||
/// Meadow — forb-rich flowering herbaceous (D-246 mosaic). Higher cover than
|
||||
/// Grass; the wetland *meadow* and grassland *wildflower*.
|
||||
Meadow = 7,
|
||||
/// Deadfall — dead woody debris (D-246 mosaic). Obstruction + fire fuel, no
|
||||
/// canopy concealment.
|
||||
Deadfall = 8,
|
||||
/// Lichen — crustose/pioneer biological crust on rock/lava (D-246 mosaic).
|
||||
/// Distinct from Barren; the rock *lichen* and lava *weathered crust*.
|
||||
Lichen = 9,
|
||||
}
|
||||
|
||||
impl Vegetation {
|
||||
@@ -464,6 +479,8 @@ pub fn derive_voxel_column(
|
||||
// WaterBody stays at sea level. The flat families are the ones that read flat and
|
||||
// need invented relief; their only feature is a 3–15 m channel, narrow enough that
|
||||
// the smooth relief shifts it bodily with its banks.
|
||||
// Captured for the mosaic wetness signal below (stays 0 where no relief is added).
|
||||
let mut relief_signal = 0.0f64;
|
||||
let takes_mid_scale_relief = matches!(
|
||||
family,
|
||||
MorphologyFamily::AlluvialPlain
|
||||
@@ -492,6 +509,58 @@ pub fn derive_voxel_column(
|
||||
);
|
||||
let relief_m = (relief * VOXEL_RELIEF_SPAN_M as f64) as i32; // truncate (D-010)
|
||||
column.elevation_m = (column.elevation_m + relief_m).max(0);
|
||||
relief_signal = relief; // ~[-0.5, +0.5]; negative = mid-scale valley (wetter)
|
||||
}
|
||||
|
||||
// ── 3c. Intra-class micro-habitat mosaic (T-1084, D-246) ──────────────
|
||||
// Within the vegetated / wet land classes (Wetland, Forest, Grassland), modulate
|
||||
// VEGETATION + WATER into a spatially-coherent mosaic of micro-habitats: a
|
||||
// body-global selection field (SeedDomain::VoxelMosaic, ≈8–64 m band) picks a
|
||||
// habitat from the surface-class palette, conditioned on a local wetness signal
|
||||
// (mid-scale relief thalweg + district moisture) so wet habitats settle in the
|
||||
// lows. This is the motivating fix for the probe's uniform "Wetland 100%".
|
||||
//
|
||||
// v1 scope: terrain MATERIAL is left exactly as the family generator set it, so
|
||||
// the D-239 §8 lithology law holds untouched. The material-variant micro-habitats
|
||||
// (hardpan / scree / dry-wash gravel) and the extreme classes (Rock / Sand /
|
||||
// Gravel / Lava) need shape-aware §8 reconciliation and are a follow-up. A
|
||||
// climatically-barren district stays barren (D-245 §5 climate-appropriateness).
|
||||
// Within these classes this supersedes the per-voxel `scatter_vegetation` scatter.
|
||||
if !matches!(family, MorphologyFamily::WaterBody) {
|
||||
use crate::atlas::mosaic::{self, SurfaceClass};
|
||||
let dominant_veg = Vegetation::from_vegetation_class(district_eff.vegetation_class);
|
||||
let class = SurfaceClass::from_base(column.terrain, dominant_veg);
|
||||
let apply = match class {
|
||||
SurfaceClass::Wetland | SurfaceClass::Forest => true,
|
||||
// Barren grassland (cold / arid) stays barren — no imposed grass.
|
||||
SurfaceClass::Grassland => !matches!(dominant_veg, Vegetation::Barren),
|
||||
// Rock / Sand / Gravel / Lava are material-driven — a §8-aware follow-up.
|
||||
_ => false,
|
||||
};
|
||||
if apply {
|
||||
let mosaic_seed = SeedChain::for_body(world_seed, body_id)
|
||||
.derive(SeedDomain::VoxelMosaic, 0)
|
||||
.seed();
|
||||
let field = crate::atlas::detail_scatter::voxel_mosaic(
|
||||
mosaic_seed,
|
||||
voxel_x as f64,
|
||||
voxel_y as f64,
|
||||
);
|
||||
// Wetness in [0,1]: district moisture, raised in the mid-scale lows and
|
||||
// lowered on the rises (relief_signal negative in valleys).
|
||||
let moisture = (district_eff.moisture_q as f64 / 100.0).clamp(0.0, 1.0);
|
||||
let wetness = (moisture - relief_signal).clamp(0.0, 1.0);
|
||||
let hab = mosaic::select(mosaic::palette(class), field, wetness);
|
||||
column.vegetation = hab.vegetation;
|
||||
// Add standing water only where the family left it dry AND the chunk's
|
||||
// hydrology gate allows it (T-1040: water placement must agree with
|
||||
// has_active_channel — water never appears in a channel-free chunk). Wet
|
||||
// habitats (bog / marsh / creek / brook) thus cluster near channels; away
|
||||
// from them they render in their dry-ground form.
|
||||
if column.water == Water::Dry && chunk.has_active_channel {
|
||||
column.water = hab.water;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── 4. Seasonal cover overlay (D-239 §3, T-1030) ──────────────────────
|
||||
@@ -2433,16 +2502,22 @@ mod tests {
|
||||
fn alluvial_plain_vegetation_from_district_class() {
|
||||
let district = alluvial_district(); // vegetation_class = Forest
|
||||
let chunk = alluvial_chunk(&district);
|
||||
// Multiple samples to account for sub-chunk scatter; all should be
|
||||
// in the Forest/Scrub/Grass range (D-239 §8: no-skip law — no Barren in Forest zone).
|
||||
// Multiple samples to account for the intra-class mosaic (T-1084, D-246),
|
||||
// which textures a Forest district into dense stand / glade / deadfall / brook /
|
||||
// fern. The binding invariant is the D-239 §8 no-skip law: NEVER Barren in a
|
||||
// Forest zone — the Forest palette upholds it (no Barren entry).
|
||||
for tx in [100, 101, 102, 103, 104] {
|
||||
let col = derive_voxel_column(42, "GJ1c", &district, &chunk, tx, 100);
|
||||
assert!(
|
||||
matches!(
|
||||
col.vegetation,
|
||||
Vegetation::Forest | Vegetation::Scrub | Vegetation::Grass
|
||||
Vegetation::Forest
|
||||
| Vegetation::Scrub
|
||||
| Vegetation::Grass
|
||||
| Vegetation::Thicket
|
||||
| Vegetation::Deadfall
|
||||
),
|
||||
"Forest district must produce Forest/Scrub/Grass vegetation, got {:?}",
|
||||
"Forest district must stay non-Barren (§8 no-skip), got {:?}",
|
||||
col.vegetation
|
||||
);
|
||||
}
|
||||
|
||||
@@ -115,6 +115,11 @@ pub enum SeedDomain {
|
||||
/// keyed by a single constant id (one field per body). Distinct domain so the
|
||||
/// relief lattice can never correlate with the per-voxel terrain or cover streams.
|
||||
VoxelRelief = 11,
|
||||
/// Sub-chunk micro-habitat mosaic selection field (T-1084, D-246). A body-global,
|
||||
/// position-keyed value-noise field at ≈8–64 m wavelengths (one field per body,
|
||||
/// keyed by a single constant id). Distinct domain so the mosaic-selection lattice
|
||||
/// can never correlate with the relief, cover, or per-voxel terrain streams.
|
||||
VoxelMosaic = 12,
|
||||
}
|
||||
|
||||
/// A position in the deterministic seed tree (D-224).
|
||||
@@ -275,6 +280,7 @@ mod tests {
|
||||
assert_eq!(SeedDomain::Voxel as u64, 9);
|
||||
assert_eq!(SeedDomain::Cover as u64, 10);
|
||||
assert_eq!(SeedDomain::VoxelRelief as u64, 11);
|
||||
assert_eq!(SeedDomain::VoxelMosaic as u64, 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
"morphology_zones": 9,
|
||||
"vegetation_classes": 3,
|
||||
"terrain_materials": 4,
|
||||
"voxel_relief_m": 20
|
||||
"voxel_relief_m": 20,
|
||||
"micro_habitat_distinct": 2
|
||||
},
|
||||
"coherence": {
|
||||
"water_districts": 49,
|
||||
@@ -68,7 +69,8 @@
|
||||
"morphology_zones": 9,
|
||||
"vegetation_classes": 1,
|
||||
"terrain_materials": 4,
|
||||
"voxel_relief_m": 19
|
||||
"voxel_relief_m": 19,
|
||||
"micro_habitat_distinct": 0
|
||||
},
|
||||
"coherence": {
|
||||
"water_districts": 25,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"tile_x": 1375,
|
||||
"tile_y": 800,
|
||||
"terrain": 0,
|
||||
"vegetation": 4,
|
||||
"vegetation": 8,
|
||||
"water": 0,
|
||||
"elevation_m": 14,
|
||||
"cover": 0
|
||||
|
||||
Reference in New Issue
Block a user