Files
settled-reach/.pql/changelog/tickets/2026-07.sql
T

5300 lines
1.8 MiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 ('06FGSK19FGZAC3H25VRAV4JRFW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'Intra-class micro-habitat mosaic — seed-scattered sub-features within every terrain type', 'A third believability lever, distinct from T-1080 (macro field gradient) and T-1081 (relief): within a SINGLE terrain class the sub-chunk seed should paint a spatially-coherent mosaic of micro-habitats, generalized to ALL terrain types. Motivating case: the probe''s ''Wetland 100%'' should resolve into clearings, copses, creeks, meadows, bogs, marshes — and likewise for every other class. NOT D-210/T-948: D-210 SubBiomeVariant is a MACRO biome tag per attractor (Tundra vs TemperateForest vs Wetland) — ''which biome''. THIS is finer: the intra-class MICRO-mosaic (''texture within a class''), at sub-chunk (<64 m) scale. MECHANIC: district/biome picks the PALETTE; the sub-chunk seed (SeedDomain::Voxel) paints the MOSAIC, reusing the derive-don''t-store tier (detail_scatter.rs, domain_warp, D-227). Three properties: (1) spatially COHERENT — patches (copse/clearing/bog/meadow) are blobs via low-frequency noise; creeks are lines following micro-drainage; never per-tile salt-and-pepper. (2) causally CONDITIONED on micro-relief/micro-drainage/moisture/slope/aspect (bogs in wettest lows, meadows on drier rises, copses where drainage improves, creeks along micro-thalwegs) — legible, not random. (3) MODULATES the existing D-228 axes, not a new axis. PER-CLASS VOCAB (one mechanism, per-class data): Wetland: clearing/copse/creek/meadow/bog/marsh; Forest: glade/dense stand/deadfall/brook/fern undergrowth; Grassland: tussock/scrub island/wildflower/dry wash; Sand: dune crest/slack/hardpan/oasis/scrub clump; Rock: outcrop/scree/ledge/lichen/talus; Gravel: bar/active channel/vegetated island; Lava: fresh sheet/weathered crust/tube collapse/pioneer scrub. Needs a Q/D-record before implementation. Relates to T-948 (D-210 macro), T-1081 (relief, a conditioning input), T-1083 (protocol must measure intra-class mosaic diversity).
---
Refinement (2026-07-02, /whats-next Si pass + user resolution): design gate resolved. First work item: write the Q/D-record (claim via `pql decisions claim D architecture`), pinning the five decisions below; then implement.
DECISIONS (user-resolved):
1. MECHANISM: new `voxel_mosaic()` octave band in detail_scatter.rs (~[64,32,16,8] m) + `SeedDomain::VoxelMosaic = 12` (append-only; update the pinned-discriminant test in server/src/seed.rs) → organic blob patches. Creeks + wet/dry conditioning follow the existing voxel_relief field''s micro-thalwegs (creeks along lowest-relief paths; bogs in lows, meadows on rises). ~4 extra value_noise calls/voxel — same cost class as voxel_relief.
2. VOCAB→AXES: expand D-228 enum VARIANTS (values on existing axes — not new axes) to cover the FULL shipped vocabulary NOW, all 7 classes. User rationale: palette composition affects the deterministic realization, so adding variants later would shift generated worlds — front-load the vocabulary. The complete axis-combination table for all 7×56 vocab items is the load-bearing content of the D-record, with a D-228 amendment for every item needing a new variant (deadfall / hardpan / lichen / fern-undergrowth have no honest existing mapping).
3. PALETTE RESIDENCY: `server/data/mosaic_constants.toml`, runtime-loaded like climate_constants.toml (T-1080 precedent). Note: the TOML is part of the deterministic input surface — weight changes change realizations; expected during Q-123 calibration, freeze after.
4. DoD METRIC IN SCOPE: add a within-patch intra-class variety field to ContrastMetrics (server/src/atlas/believability.rs) + a provisional criterion K≥3 distinct micro-features per sampled patch; Q-123 calibrates the final K. Matches the T-1081 precedent (mechanic + metric land together).
5. FAMILY GATING (grounded default, accepted): flat families (AlluvialPlain, LavaField, BraidedDelta, DuneStrand, MeanderReach) take the full mosaic; dramatic families (CliffCoast, FjordWall, IncisedGorge) get a per-family-gated non-structural modulation pass (vegetation/material only — must not warp carved geometry), mirroring voxel_relief gating. WaterBody: none. <5 ms chunk budget verification is part of implementation (T-1081 harness precedent).
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).', 'backlog', 'high', NULL, 'server', 'D-227', '2026-06-28 05:55:34.396', '2026-07-02 07:42:06.753', NULL, '0cd112f1fc4f62f5b7a464dd51ae5205', 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 ('06FGSK19FGZAC3H25VRAV4JRFW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'Intra-class micro-habitat mosaic — seed-scattered sub-features within every terrain type', 'A third believability lever, distinct from T-1080 (macro field gradient) and T-1081 (relief): within a SINGLE terrain class the sub-chunk seed should paint a spatially-coherent mosaic of micro-habitats, generalized to ALL terrain types. Motivating case: the probe''s ''Wetland 100%'' should resolve into clearings, copses, creeks, meadows, bogs, marshes — and likewise for every other class. NOT D-210/T-948: D-210 SubBiomeVariant is a MACRO biome tag per attractor (Tundra vs TemperateForest vs Wetland) — ''which biome''. THIS is finer: the intra-class MICRO-mosaic (''texture within a class''), at sub-chunk (<64 m) scale. MECHANIC: district/biome picks the PALETTE; the sub-chunk seed (SeedDomain::Voxel) paints the MOSAIC, reusing the derive-don''t-store tier (detail_scatter.rs, domain_warp, D-227). Three properties: (1) spatially COHERENT — patches (copse/clearing/bog/meadow) are blobs via low-frequency noise; creeks are lines following micro-drainage; never per-tile salt-and-pepper. (2) causally CONDITIONED on micro-relief/micro-drainage/moisture/slope/aspect (bogs in wettest lows, meadows on drier rises, copses where drainage improves, creeks along micro-thalwegs) — legible, not random. (3) MODULATES the existing D-228 axes, not a new axis. PER-CLASS VOCAB (one mechanism, per-class data): Wetland: clearing/copse/creek/meadow/bog/marsh; Forest: glade/dense stand/deadfall/brook/fern undergrowth; Grassland: tussock/scrub island/wildflower/dry wash; Sand: dune crest/slack/hardpan/oasis/scrub clump; Rock: outcrop/scree/ledge/lichen/talus; Gravel: bar/active channel/vegetated island; Lava: fresh sheet/weathered crust/tube collapse/pioneer scrub. Needs a Q/D-record before implementation. Relates to T-948 (D-210 macro), T-1081 (relief, a conditioning input), T-1083 (protocol must measure intra-class mosaic diversity).
---
Refinement (2026-07-02, /whats-next Si pass + user resolution): design gate resolved. First work item: write the Q/D-record (claim via `pql decisions claim D architecture`), pinning the five decisions below; then implement.
DECISIONS (user-resolved):
1. MECHANISM: new `voxel_mosaic()` octave band in detail_scatter.rs (~[64,32,16,8] m) + `SeedDomain::VoxelMosaic = 12` (append-only; update the pinned-discriminant test in server/src/seed.rs) → organic blob patches. Creeks + wet/dry conditioning follow the existing voxel_relief field''s micro-thalwegs (creeks along lowest-relief paths; bogs in lows, meadows on rises). ~4 extra value_noise calls/voxel — same cost class as voxel_relief.
2. VOCAB→AXES: expand D-228 enum VARIANTS (values on existing axes — not new axes) to cover the FULL shipped vocabulary NOW, all 7 classes. User rationale: palette composition affects the deterministic realization, so adding variants later would shift generated worlds — front-load the vocabulary. The complete axis-combination table for all 7×56 vocab items is the load-bearing content of the D-record, with a D-228 amendment for every item needing a new variant (deadfall / hardpan / lichen / fern-undergrowth have no honest existing mapping).
3. PALETTE RESIDENCY: `server/data/mosaic_constants.toml`, runtime-loaded like climate_constants.toml (T-1080 precedent). Note: the TOML is part of the deterministic input surface — weight changes change realizations; expected during Q-123 calibration, freeze after.
4. DoD METRIC IN SCOPE: add a within-patch intra-class variety field to ContrastMetrics (server/src/atlas/believability.rs) + a provisional criterion K≥3 distinct micro-features per sampled patch; Q-123 calibrates the final K. Matches the T-1081 precedent (mechanic + metric land together).
5. FAMILY GATING (grounded default, accepted): flat families (AlluvialPlain, LavaField, BraidedDelta, DuneStrand, MeanderReach) take the full mosaic; dramatic families (CliffCoast, FjordWall, IncisedGorge) get a per-family-gated non-structural modulation pass (vegetation/material only — must not warp carved geometry), mirroring voxel_relief gating. WaterBody: none. <5 ms chunk budget verification is part of implementation (T-1081 harness precedent).
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).', 'in_progress', 'high', NULL, 'server', 'D-227', '2026-06-28 05:55:34.396', '2026-07-02 14:25:53.590', NULL, 'cdd766b50cd259b35aee899be9433cb8', 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 ('06FGSK19FGZAC3H25VRAV4JRFW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'Intra-class micro-habitat mosaic — seed-scattered sub-features within every terrain type', 'A third believability lever, distinct from T-1080 (macro field gradient) and T-1081 (relief): within a SINGLE terrain class the sub-chunk seed should paint a spatially-coherent mosaic of micro-habitats, generalized to ALL terrain types. Motivating case: the probe''s ''Wetland 100%'' should resolve into clearings, copses, creeks, meadows, bogs, marshes — and likewise for every other class. NOT D-210/T-948: D-210 SubBiomeVariant is a MACRO biome tag per attractor (Tundra vs TemperateForest vs Wetland) — ''which biome''. THIS is finer: the intra-class MICRO-mosaic (''texture within a class''), at sub-chunk (<64 m) scale. MECHANIC: district/biome picks the PALETTE; the sub-chunk seed (SeedDomain::Voxel) paints the MOSAIC, reusing the derive-don''t-store tier (detail_scatter.rs, domain_warp, D-227). Three properties: (1) spatially COHERENT — patches (copse/clearing/bog/meadow) are blobs via low-frequency noise; creeks are lines following micro-drainage; never per-tile salt-and-pepper. (2) causally CONDITIONED on micro-relief/micro-drainage/moisture/slope/aspect (bogs in wettest lows, meadows on drier rises, copses where drainage improves, creeks along micro-thalwegs) — legible, not random. (3) MODULATES the existing D-228 axes, not a new axis. PER-CLASS VOCAB (one mechanism, per-class data): Wetland: clearing/copse/creek/meadow/bog/marsh; Forest: glade/dense stand/deadfall/brook/fern undergrowth; Grassland: tussock/scrub island/wildflower/dry wash; Sand: dune crest/slack/hardpan/oasis/scrub clump; Rock: outcrop/scree/ledge/lichen/talus; Gravel: bar/active channel/vegetated island; Lava: fresh sheet/weathered crust/tube collapse/pioneer scrub. Needs a Q/D-record before implementation. Relates to T-948 (D-210 macro), T-1081 (relief, a conditioning input), T-1083 (protocol must measure intra-class mosaic diversity).
---
Refinement (2026-07-02, /whats-next Si pass + user resolution): design gate resolved. First work item: write the Q/D-record (claim via `pql decisions claim D architecture`), pinning the five decisions below; then implement.
DECISIONS (user-resolved):
1. MECHANISM: new `voxel_mosaic()` octave band in detail_scatter.rs (~[64,32,16,8] m) + `SeedDomain::VoxelMosaic = 12` (append-only; update the pinned-discriminant test in server/src/seed.rs) → organic blob patches. Creeks + wet/dry conditioning follow the existing voxel_relief field''s micro-thalwegs (creeks along lowest-relief paths; bogs in lows, meadows on rises). ~4 extra value_noise calls/voxel — same cost class as voxel_relief.
2. VOCAB→AXES: expand D-228 enum VARIANTS (values on existing axes — not new axes) to cover the FULL shipped vocabulary NOW, all 7 classes. User rationale: palette composition affects the deterministic realization, so adding variants later would shift generated worlds — front-load the vocabulary. The complete axis-combination table for all 7×56 vocab items is the load-bearing content of the D-record, with a D-228 amendment for every item needing a new variant (deadfall / hardpan / lichen / fern-undergrowth have no honest existing mapping).
3. PALETTE RESIDENCY: `server/data/mosaic_constants.toml`, runtime-loaded like climate_constants.toml (T-1080 precedent). Note: the TOML is part of the deterministic input surface — weight changes change realizations; expected during Q-123 calibration, freeze after.
4. DoD METRIC IN SCOPE: add a within-patch intra-class variety field to ContrastMetrics (server/src/atlas/believability.rs) + a provisional criterion K≥3 distinct micro-features per sampled patch; Q-123 calibrates the final K. Matches the T-1081 precedent (mechanic + metric land together).
5. FAMILY GATING (grounded default, accepted): flat families (AlluvialPlain, LavaField, BraidedDelta, DuneStrand, MeanderReach) take the full mosaic; dramatic families (CliffCoast, FjordWall, IncisedGorge) get a per-family-gated non-structural modulation pass (vegetation/material only — must not warp carved geometry), mirroring voxel_relief gating. WaterBody: none. <5 ms chunk budget verification is part of implementation (T-1081 harness precedent).
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);
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.', 'in_progress', 'medium', 'miri', NULL, 'D-247', '2026-07-02 20:30:57.222', '2026-07-03 20:18:52.236', NULL, '3a8443d96126741609571662cf38b4c1', 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.', 'in_progress', 'medium', 'miri', NULL, 'D-247', '2026-07-02 20:30:57.222', '2026-07-03 20:20:01.466', NULL, 'f7bd0e6fd21f924f65afcec9a7ed9624', 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.
PROGRESS 2026-07-03: Workstreams 1-2 DONE + committed (388168eef, branch micro-habitat-mosaic).
- Cadwal→Linnaeus rename applied (body frontmatter name + biosphere_class:NativeCompatible, GJ-166A infobox, atlas proposal, D-247 clarified). Capital stays Vance.
- 5 wiki continuity edits applied (GJ-524 Mirror, GJ-166A/Keid Compatible, GJ-684A Freyburg Compatible, GJ-124 Hạ Long Compatible, GJ-197 Puerto Último Compatible). Solandar/Oshima needed none.
REMAINING:
- Workstream 3 (biosphere_class plumbing): add nullable bodies.biosphere_class column to systems-schema.sql; teach tooling/db/wiki_sync.py import_from_wiki to read biosphere_class frontmatter + apply the two-gate DEFAULT for canon-silent bodies (Gate0 atmosphere/hydrosphere, Gate1 economic_role/economic_base); add explicit frontmatter for the 3 override bodies still needing it (Solandar=NativeMirror, GJ-524=NativeMirror [uninhabited-but-alive → default would mis-Airless it], Edict/GJ244Ad=Airless [standard-atm+ice edge case]); ensure generate_wiki round-trips biosphere_class; then the BAKE (run import_from_wiki + generate_wiki → systems.db + regenerated infoboxes, incl. the Linnaeus name propagation) + commit systems.db. Add biosphere_class to BodyParams read set (D-240) as the D-247 code hook. NOTE: the bake is a systems.db binary-artifact commit — consequential, do deliberately.
- Workstream 4 (alien-ecology vocab → palette): integrate Miri''s biome × register vocabulary (cane/vane/wrack/husk/drum-root native-mirror lexicon + Earth-register names, per her Deliverable 3) into atlas/mosaic.rs + mosaic_constants.toml — extends the temperate/native baseline to the full surface-class × climate-biome × register palette. This also completes T-1084''s biome-awareness. Determinism: lock before generation.
Miri''s full Deliverable 3 vocab (the source for workstream 4) is in her T-1085 agent report.', 'in_progress', 'medium', 'miri', NULL, 'D-247', '2026-07-02 20:30:57.222', '2026-07-03 20:40:15.305', NULL, '83246ebc2aa0550f44afd68721efb9d4', 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.
PROGRESS 2026-07-03: Workstreams 1-2 DONE + committed (388168eef, branch micro-habitat-mosaic).
- Cadwal→Linnaeus rename applied (body frontmatter name + biosphere_class:NativeCompatible, GJ-166A infobox, atlas proposal, D-247 clarified). Capital stays Vance.
- 5 wiki continuity edits applied (GJ-524 Mirror, GJ-166A/Keid Compatible, GJ-684A Freyburg Compatible, GJ-124 Hạ Long Compatible, GJ-197 Puerto Último Compatible). Solandar/Oshima needed none.
REMAINING:
- Workstream 3 (biosphere_class plumbing): add nullable bodies.biosphere_class column to systems-schema.sql; teach tooling/db/wiki_sync.py import_from_wiki to read biosphere_class frontmatter + apply the two-gate DEFAULT for canon-silent bodies (Gate0 atmosphere/hydrosphere, Gate1 economic_role/economic_base); add explicit frontmatter for the 3 override bodies still needing it (Solandar=NativeMirror, GJ-524=NativeMirror [uninhabited-but-alive → default would mis-Airless it], Edict/GJ244Ad=Airless [standard-atm+ice edge case]); ensure generate_wiki round-trips biosphere_class; then the BAKE (run import_from_wiki + generate_wiki → systems.db + regenerated infoboxes, incl. the Linnaeus name propagation) + commit systems.db. Add biosphere_class to BodyParams read set (D-240) as the D-247 code hook. NOTE: the bake is a systems.db binary-artifact commit — consequential, do deliberately.
- Workstream 4 (alien-ecology vocab → palette): integrate Miri''s biome × register vocabulary (cane/vane/wrack/husk/drum-root native-mirror lexicon + Earth-register names, per her Deliverable 3) into atlas/mosaic.rs + mosaic_constants.toml — extends the temperate/native baseline to the full surface-class × climate-biome × register palette. This also completes T-1084''s biome-awareness. Determinism: lock before generation.
Miri''s full Deliverable 3 vocab (the source for workstream 4) is in her T-1085 agent report.
Workstream 4 DONE (partial) + committed (5fb34675e): grassland biome-awareness (ClimateBiome from temperature_c+moisture_q → savanna/temperate/boreal/tundra palettes; savanna Forest-copses, tundra lichen/sparse) + Miri''s full biome×register vocab preserved as docs/design/alien-ecology-vocabulary.md. Cold-barren districts now render structured-sparse (D-245 climate-appropriateness): vegetated_districts 8→20, 0→39 on cold bodies. Full suite green, believability golden regenerated. CONTINUATION (noted): Forest biomes (tropical/boreal), Wetland/extreme-class biomes, the introduced-Earth register (needs biosphere_class + the managed layer), and the §8 material follow-up. REMAINING on T-1085: workstream 3 (biosphere_class schema+importer+default-rule+bake) + this WS4 continuation.', 'in_progress', 'medium', 'miri', NULL, 'D-247', '2026-07-02 20:30:57.222', '2026-07-04 18:31:00.300', NULL, '3ff69149b0067dade2dbd718125be6b1', 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.
PROGRESS 2026-07-03: Workstreams 1-2 DONE + committed (388168eef, branch micro-habitat-mosaic).
- Cadwal→Linnaeus rename applied (body frontmatter name + biosphere_class:NativeCompatible, GJ-166A infobox, atlas proposal, D-247 clarified). Capital stays Vance.
- 5 wiki continuity edits applied (GJ-524 Mirror, GJ-166A/Keid Compatible, GJ-684A Freyburg Compatible, GJ-124 Hạ Long Compatible, GJ-197 Puerto Último Compatible). Solandar/Oshima needed none.
REMAINING:
- Workstream 3 (biosphere_class plumbing): add nullable bodies.biosphere_class column to systems-schema.sql; teach tooling/db/wiki_sync.py import_from_wiki to read biosphere_class frontmatter + apply the two-gate DEFAULT for canon-silent bodies (Gate0 atmosphere/hydrosphere, Gate1 economic_role/economic_base); add explicit frontmatter for the 3 override bodies still needing it (Solandar=NativeMirror, GJ-524=NativeMirror [uninhabited-but-alive → default would mis-Airless it], Edict/GJ244Ad=Airless [standard-atm+ice edge case]); ensure generate_wiki round-trips biosphere_class; then the BAKE (run import_from_wiki + generate_wiki → systems.db + regenerated infoboxes, incl. the Linnaeus name propagation) + commit systems.db. Add biosphere_class to BodyParams read set (D-240) as the D-247 code hook. NOTE: the bake is a systems.db binary-artifact commit — consequential, do deliberately.
- Workstream 4 (alien-ecology vocab → palette): integrate Miri''s biome × register vocabulary (cane/vane/wrack/husk/drum-root native-mirror lexicon + Earth-register names, per her Deliverable 3) into atlas/mosaic.rs + mosaic_constants.toml — extends the temperate/native baseline to the full surface-class × climate-biome × register palette. This also completes T-1084''s biome-awareness. Determinism: lock before generation.
Miri''s full Deliverable 3 vocab (the source for workstream 4) is in her T-1085 agent report.
Workstream 4 DONE (partial) + committed (5fb34675e): grassland biome-awareness (ClimateBiome from temperature_c+moisture_q → savanna/temperate/boreal/tundra palettes; savanna Forest-copses, tundra lichen/sparse) + Miri''s full biome×register vocab preserved as docs/design/alien-ecology-vocabulary.md. Cold-barren districts now render structured-sparse (D-245 climate-appropriateness): vegetated_districts 8→20, 0→39 on cold bodies. Full suite green, believability golden regenerated. CONTINUATION (noted): Forest biomes (tropical/boreal), Wetland/extreme-class biomes, the introduced-Earth register (needs biosphere_class + the managed layer), and the §8 material follow-up. REMAINING on T-1085: workstream 3 (biosphere_class schema+importer+default-rule+bake) + this WS4 continuation.
Workstream 3 DONE + committed (cc7c5ada0): biosphere_class plumbing via the stamped regen-db path. Nullable bodies.biosphere_class column (schema + migration); economy_import/bodies.py populate_biosphere_class (frontmatter override + D-247 two-gate default, recompute-all); wired as import step 17; frontmatter overrides for Solandar/GJ-524/Hạ Long (the 3 whose default deviated). systems.db regenerated + stamped (make regen-db). VERIFIED: all 9 canon exemplars correct; distribution 111 Compatible : 108 Mirror among inhabited alive (~50/50), Airless for uninhabited, TerraformedSterile for thin-atm. BodyParams Rust hook deferred (no consumer yet — mosaic is chirality-blind; Burnelli economy is the future consumer).
T-1085 STATUS: WS1 (chirality assignment) DONE, WS2 (wiki continuity) DONE, WS3 (biosphere plumbing) DONE, WS4 (vocab→palette) DONE-partial (grassland biome-awareness + vocab doc; Forest/register/§8-material continuation noted). REMAINING: (a) rename completion — propagate Linnaeus to bodies.proper_name in the DB (still Cadwal; proper_name is atlas-CLI-owned, wipe+reimport would drop non-proposal cols → needs a targeted approach); (b) WS4 continuation; (c) Burnelli food-economy consumer (separate stakeholder follow-on).', 'in_progress', 'medium', 'miri', NULL, 'D-247', '2026-07-02 20:30:57.222', '2026-07-05 09:57:14.888', NULL, '266b2ac2007b0bb0ac6ebb5d1a0522be', 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 ('06FGSK19FGZAC3H25VRAV4JRFW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'Intra-class micro-habitat mosaic — seed-scattered sub-features within every terrain type', 'A third believability lever, distinct from T-1080 (macro field gradient) and T-1081 (relief): within a SINGLE terrain class the sub-chunk seed should paint a spatially-coherent mosaic of micro-habitats, generalized to ALL terrain types. Motivating case: the probe''s ''Wetland 100%'' should resolve into clearings, copses, creeks, meadows, bogs, marshes — and likewise for every other class. NOT D-210/T-948: D-210 SubBiomeVariant is a MACRO biome tag per attractor (Tundra vs TemperateForest vs Wetland) — ''which biome''. THIS is finer: the intra-class MICRO-mosaic (''texture within a class''), at sub-chunk (<64 m) scale. MECHANIC: district/biome picks the PALETTE; the sub-chunk seed (SeedDomain::Voxel) paints the MOSAIC, reusing the derive-don''t-store tier (detail_scatter.rs, domain_warp, D-227). Three properties: (1) spatially COHERENT — patches (copse/clearing/bog/meadow) are blobs via low-frequency noise; creeks are lines following micro-drainage; never per-tile salt-and-pepper. (2) causally CONDITIONED on micro-relief/micro-drainage/moisture/slope/aspect (bogs in wettest lows, meadows on drier rises, copses where drainage improves, creeks along micro-thalwegs) — legible, not random. (3) MODULATES the existing D-228 axes, not a new axis. PER-CLASS VOCAB (one mechanism, per-class data): Wetland: clearing/copse/creek/meadow/bog/marsh; Forest: glade/dense stand/deadfall/brook/fern undergrowth; Grassland: tussock/scrub island/wildflower/dry wash; Sand: dune crest/slack/hardpan/oasis/scrub clump; Rock: outcrop/scree/ledge/lichen/talus; Gravel: bar/active channel/vegetated island; Lava: fresh sheet/weathered crust/tube collapse/pioneer scrub. Needs a Q/D-record before implementation. Relates to T-948 (D-210 macro), T-1081 (relief, a conditioning input), T-1083 (protocol must measure intra-class mosaic diversity).
---
Refinement (2026-07-02, /whats-next Si pass + user resolution): design gate resolved. First work item: write the Q/D-record (claim via `pql decisions claim D architecture`), pinning the five decisions below; then implement.
DECISIONS (user-resolved):
1. MECHANISM: new `voxel_mosaic()` octave band in detail_scatter.rs (~[64,32,16,8] m) + `SeedDomain::VoxelMosaic = 12` (append-only; update the pinned-discriminant test in server/src/seed.rs) → organic blob patches. Creeks + wet/dry conditioning follow the existing voxel_relief field''s micro-thalwegs (creeks along lowest-relief paths; bogs in lows, meadows on rises). ~4 extra value_noise calls/voxel — same cost class as voxel_relief.
2. VOCAB→AXES: expand D-228 enum VARIANTS (values on existing axes — not new axes) to cover the FULL shipped vocabulary NOW, all 7 classes. User rationale: palette composition affects the deterministic realization, so adding variants later would shift generated worlds — front-load the vocabulary. The complete axis-combination table for all 7×56 vocab items is the load-bearing content of the D-record, with a D-228 amendment for every item needing a new variant (deadfall / hardpan / lichen / fern-undergrowth have no honest existing mapping).
3. PALETTE RESIDENCY: `server/data/mosaic_constants.toml`, runtime-loaded like climate_constants.toml (T-1080 precedent). Note: the TOML is part of the deterministic input surface — weight changes change realizations; expected during Q-123 calibration, freeze after.
4. DoD METRIC IN SCOPE: add a within-patch intra-class variety field to ContrastMetrics (server/src/atlas/believability.rs) + a provisional criterion K≥3 distinct micro-features per sampled patch; Q-123 calibrates the final K. Matches the T-1081 precedent (mechanic + metric land together).
5. FAMILY GATING (grounded default, accepted): flat families (AlluvialPlain, LavaField, BraidedDelta, DuneStrand, MeanderReach) take the full mosaic; dramatic families (CliffCoast, FjordWall, IncisedGorge) get a per-family-gated non-structural modulation pass (vegetation/material only — must not warp carved geometry), mirroring voxel_relief gating. WaterBody: none. <5 ms chunk budget verification is part of implementation (T-1081 harness precedent).
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.', 'review', 'high', NULL, 'server', 'D-227', '2026-06-28 05:55:34.396', '2026-07-05 11:04:20.115', NULL, '6c6979d10d1da1a90d8bbd0629e58ce1', 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.
PROGRESS 2026-07-03: Workstreams 1-2 DONE + committed (388168eef, branch micro-habitat-mosaic).
- Cadwal→Linnaeus rename applied (body frontmatter name + biosphere_class:NativeCompatible, GJ-166A infobox, atlas proposal, D-247 clarified). Capital stays Vance.
- 5 wiki continuity edits applied (GJ-524 Mirror, GJ-166A/Keid Compatible, GJ-684A Freyburg Compatible, GJ-124 Hạ Long Compatible, GJ-197 Puerto Último Compatible). Solandar/Oshima needed none.
REMAINING:
- Workstream 3 (biosphere_class plumbing): add nullable bodies.biosphere_class column to systems-schema.sql; teach tooling/db/wiki_sync.py import_from_wiki to read biosphere_class frontmatter + apply the two-gate DEFAULT for canon-silent bodies (Gate0 atmosphere/hydrosphere, Gate1 economic_role/economic_base); add explicit frontmatter for the 3 override bodies still needing it (Solandar=NativeMirror, GJ-524=NativeMirror [uninhabited-but-alive → default would mis-Airless it], Edict/GJ244Ad=Airless [standard-atm+ice edge case]); ensure generate_wiki round-trips biosphere_class; then the BAKE (run import_from_wiki + generate_wiki → systems.db + regenerated infoboxes, incl. the Linnaeus name propagation) + commit systems.db. Add biosphere_class to BodyParams read set (D-240) as the D-247 code hook. NOTE: the bake is a systems.db binary-artifact commit — consequential, do deliberately.
- Workstream 4 (alien-ecology vocab → palette): integrate Miri''s biome × register vocabulary (cane/vane/wrack/husk/drum-root native-mirror lexicon + Earth-register names, per her Deliverable 3) into atlas/mosaic.rs + mosaic_constants.toml — extends the temperate/native baseline to the full surface-class × climate-biome × register palette. This also completes T-1084''s biome-awareness. Determinism: lock before generation.
Miri''s full Deliverable 3 vocab (the source for workstream 4) is in her T-1085 agent report.
Workstream 4 DONE (partial) + committed (5fb34675e): grassland biome-awareness (ClimateBiome from temperature_c+moisture_q → savanna/temperate/boreal/tundra palettes; savanna Forest-copses, tundra lichen/sparse) + Miri''s full biome×register vocab preserved as docs/design/alien-ecology-vocabulary.md. Cold-barren districts now render structured-sparse (D-245 climate-appropriateness): vegetated_districts 8→20, 0→39 on cold bodies. Full suite green, believability golden regenerated. CONTINUATION (noted): Forest biomes (tropical/boreal), Wetland/extreme-class biomes, the introduced-Earth register (needs biosphere_class + the managed layer), and the §8 material follow-up. REMAINING on T-1085: workstream 3 (biosphere_class schema+importer+default-rule+bake) + this WS4 continuation.
Workstream 3 DONE + committed (cc7c5ada0): biosphere_class plumbing via the stamped regen-db path. Nullable bodies.biosphere_class column (schema + migration); economy_import/bodies.py populate_biosphere_class (frontmatter override + D-247 two-gate default, recompute-all); wired as import step 17; frontmatter overrides for Solandar/GJ-524/Hạ Long (the 3 whose default deviated). systems.db regenerated + stamped (make regen-db). VERIFIED: all 9 canon exemplars correct; distribution 111 Compatible : 108 Mirror among inhabited alive (~50/50), Airless for uninhabited, TerraformedSterile for thin-atm. BodyParams Rust hook deferred (no consumer yet — mosaic is chirality-blind; Burnelli economy is the future consumer).
T-1085 STATUS: WS1 (chirality assignment) DONE, WS2 (wiki continuity) DONE, WS3 (biosphere plumbing) DONE, WS4 (vocab→palette) DONE-partial (grassland biome-awareness + vocab doc; Forest/register/§8-material continuation noted). REMAINING: (a) rename completion — propagate Linnaeus to bodies.proper_name in the DB (still Cadwal; proper_name is atlas-CLI-owned, wipe+reimport would drop non-proposal cols → needs a targeted approach); (b) WS4 continuation; (c) Burnelli food-economy consumer (separate stakeholder follow-on).', 'review', 'medium', 'miri', NULL, 'D-247', '2026-07-02 20:30:57.222', '2026-07-05 11:04:20.119', NULL, '8adc7b9bdd2f69889aae0237c1670291', 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 ('06FK6RTBN796N8WZ0GW8BREG0R', 'task', '06FGSJNP8YM537DJCC82ZD6544', 'Propagate Linnaeus rename to bodies.proper_name in systems.db', 'Follow-up from T-1085 (D-247 biosphere pass), confirmed by the PR #172 re-review FYI. GJ166Ac''s biosphere_class is correctly NativeCompatible, but bodies.proper_name in systems.db still reads ''Cadwal'' — the wiki + atlas proposal were renamed to Linnaeus, but proper_name is atlas-CLI-owned and a wipe+reimport would drop non-proposal columns (e.g. founding_age_years). Needs a targeted approach: an atlas-CLI update path or a migration that sets proper_name without clobbering the enriched columns. Capital stays ''Vance''.', 'backlog', 'low', NULL, 'server', NULL, '2026-07-05 17:46:13.289', '2026-07-05 17:46:13.289', NULL, 'f5d4541a945a07b3325e072416ea8680', 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 ('06FK6RVP1HS8RHN29XQPEXT5QM', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'Mosaic biome palettes continuation — Forest/Wetland + introduced-Earth register + §8 material (D-246)', 'Continuation of T-1084/T-1085 WS4 (PR #172 shipped grassland biome-awareness + the alien-ecology vocab doc). Remaining: (1) Forest biome palettes (tropical/boreal) + Wetland/extreme-class biome variants — the D-246 vocab table grows past the temperate baseline rows. (2) The introduced-Earth managed register — needs the biosphere_class consumer + the D-166 managed/settlement layer (the D-228 wild->managed override). (3) The D-246 v1 material-variant follow-up (PR #172 H2): wire the material-driven families (Sand/Rock/Gravel/Lava, currently _ => false) so TerrainMaterial::{Hardpan, Scree} become reachable, with shape-aware §8 reconciliation. Source vocab: docs/design/alien-ecology-vocabulary.md. Determinism: lock any new palette entries before generation (a later addition re-rolls affected voxels). Threshold calibration deferred to Q-123.', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-05 17:46:24.140', '2026-07-05 17:46:24.140', NULL, '3eed7ddc24e2ed5f65c7558f02a3dcd1', 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.
PROGRESS 2026-07-03: Workstreams 1-2 DONE + committed (388168eef, branch micro-habitat-mosaic).
- Cadwal→Linnaeus rename applied (body frontmatter name + biosphere_class:NativeCompatible, GJ-166A infobox, atlas proposal, D-247 clarified). Capital stays Vance.
- 5 wiki continuity edits applied (GJ-524 Mirror, GJ-166A/Keid Compatible, GJ-684A Freyburg Compatible, GJ-124 Hạ Long Compatible, GJ-197 Puerto Último Compatible). Solandar/Oshima needed none.
REMAINING:
- Workstream 3 (biosphere_class plumbing): add nullable bodies.biosphere_class column to systems-schema.sql; teach tooling/db/wiki_sync.py import_from_wiki to read biosphere_class frontmatter + apply the two-gate DEFAULT for canon-silent bodies (Gate0 atmosphere/hydrosphere, Gate1 economic_role/economic_base); add explicit frontmatter for the 3 override bodies still needing it (Solandar=NativeMirror, GJ-524=NativeMirror [uninhabited-but-alive → default would mis-Airless it], Edict/GJ244Ad=Airless [standard-atm+ice edge case]); ensure generate_wiki round-trips biosphere_class; then the BAKE (run import_from_wiki + generate_wiki → systems.db + regenerated infoboxes, incl. the Linnaeus name propagation) + commit systems.db. Add biosphere_class to BodyParams read set (D-240) as the D-247 code hook. NOTE: the bake is a systems.db binary-artifact commit — consequential, do deliberately.
- Workstream 4 (alien-ecology vocab → palette): integrate Miri''s biome × register vocabulary (cane/vane/wrack/husk/drum-root native-mirror lexicon + Earth-register names, per her Deliverable 3) into atlas/mosaic.rs + mosaic_constants.toml — extends the temperate/native baseline to the full surface-class × climate-biome × register palette. This also completes T-1084''s biome-awareness. Determinism: lock before generation.
Miri''s full Deliverable 3 vocab (the source for workstream 4) is in her T-1085 agent report.
Workstream 4 DONE (partial) + committed (5fb34675e): grassland biome-awareness (ClimateBiome from temperature_c+moisture_q → savanna/temperate/boreal/tundra palettes; savanna Forest-copses, tundra lichen/sparse) + Miri''s full biome×register vocab preserved as docs/design/alien-ecology-vocabulary.md. Cold-barren districts now render structured-sparse (D-245 climate-appropriateness): vegetated_districts 8→20, 0→39 on cold bodies. Full suite green, believability golden regenerated. CONTINUATION (noted): Forest biomes (tropical/boreal), Wetland/extreme-class biomes, the introduced-Earth register (needs biosphere_class + the managed layer), and the §8 material follow-up. REMAINING on T-1085: workstream 3 (biosphere_class schema+importer+default-rule+bake) + this WS4 continuation.
Workstream 3 DONE + committed (cc7c5ada0): biosphere_class plumbing via the stamped regen-db path. Nullable bodies.biosphere_class column (schema + migration); economy_import/bodies.py populate_biosphere_class (frontmatter override + D-247 two-gate default, recompute-all); wired as import step 17; frontmatter overrides for Solandar/GJ-524/Hạ Long (the 3 whose default deviated). systems.db regenerated + stamped (make regen-db). VERIFIED: all 9 canon exemplars correct; distribution 111 Compatible : 108 Mirror among inhabited alive (~50/50), Airless for uninhabited, TerraformedSterile for thin-atm. BodyParams Rust hook deferred (no consumer yet — mosaic is chirality-blind; Burnelli economy is the future consumer).
T-1085 STATUS: WS1 (chirality assignment) DONE, WS2 (wiki continuity) DONE, WS3 (biosphere plumbing) DONE, WS4 (vocab→palette) DONE-partial (grassland biome-awareness + vocab doc; Forest/register/§8-material continuation noted). REMAINING: (a) rename completion — propagate Linnaeus to bodies.proper_name in the DB (still Cadwal; proper_name is atlas-CLI-owned, wipe+reimport would drop non-proposal cols → needs a targeted approach); (b) WS4 continuation; (c) Burnelli food-economy consumer (separate stakeholder follow-on).
CLOSED 2026-07-05 (PR #172 merged to main @ 895bb83ba). Core deliverable shipped: WS1 chirality assignment, WS2 wiki continuity, WS3 biosphere_class plumbing (stamped regen-db), WS4 grassland biome-awareness + vocab doc. Both blocking review findings (C1 over-vegetation, H1 biosphere gate) fixed + N1 (Wetland gate) folded in; both reviewers approved. Remaining work split into follow-ups: T-1086 (propagate Linnaeus to bodies.proper_name) and T-1087 (Forest/Wetland biome palettes + introduced-Earth register + §8 material). Burnelli food-economy consumer stays a Phase-2 stakeholder follow-on (noted, not filed — cross-phase).', 'review', 'medium', 'miri', NULL, 'D-247', '2026-07-02 20:30:57.222', '2026-07-05 17:46:33.511', NULL, '7ff082f949e3b57a35709dc94c97d655', 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 ('06FGSK19FGZAC3H25VRAV4JRFW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'Intra-class micro-habitat mosaic — seed-scattered sub-features within every terrain type', 'A third believability lever, distinct from T-1080 (macro field gradient) and T-1081 (relief): within a SINGLE terrain class the sub-chunk seed should paint a spatially-coherent mosaic of micro-habitats, generalized to ALL terrain types. Motivating case: the probe''s ''Wetland 100%'' should resolve into clearings, copses, creeks, meadows, bogs, marshes — and likewise for every other class. NOT D-210/T-948: D-210 SubBiomeVariant is a MACRO biome tag per attractor (Tundra vs TemperateForest vs Wetland) — ''which biome''. THIS is finer: the intra-class MICRO-mosaic (''texture within a class''), at sub-chunk (<64 m) scale. MECHANIC: district/biome picks the PALETTE; the sub-chunk seed (SeedDomain::Voxel) paints the MOSAIC, reusing the derive-don''t-store tier (detail_scatter.rs, domain_warp, D-227). Three properties: (1) spatially COHERENT — patches (copse/clearing/bog/meadow) are blobs via low-frequency noise; creeks are lines following micro-drainage; never per-tile salt-and-pepper. (2) causally CONDITIONED on micro-relief/micro-drainage/moisture/slope/aspect (bogs in wettest lows, meadows on drier rises, copses where drainage improves, creeks along micro-thalwegs) — legible, not random. (3) MODULATES the existing D-228 axes, not a new axis. PER-CLASS VOCAB (one mechanism, per-class data): Wetland: clearing/copse/creek/meadow/bog/marsh; Forest: glade/dense stand/deadfall/brook/fern undergrowth; Grassland: tussock/scrub island/wildflower/dry wash; Sand: dune crest/slack/hardpan/oasis/scrub clump; Rock: outcrop/scree/ledge/lichen/talus; Gravel: bar/active channel/vegetated island; Lava: fresh sheet/weathered crust/tube collapse/pioneer scrub. Needs a Q/D-record before implementation. Relates to T-948 (D-210 macro), T-1081 (relief, a conditioning input), T-1083 (protocol must measure intra-class mosaic diversity).
---
Refinement (2026-07-02, /whats-next Si pass + user resolution): design gate resolved. First work item: write the Q/D-record (claim via `pql decisions claim D architecture`), pinning the five decisions below; then implement.
DECISIONS (user-resolved):
1. MECHANISM: new `voxel_mosaic()` octave band in detail_scatter.rs (~[64,32,16,8] m) + `SeedDomain::VoxelMosaic = 12` (append-only; update the pinned-discriminant test in server/src/seed.rs) → organic blob patches. Creeks + wet/dry conditioning follow the existing voxel_relief field''s micro-thalwegs (creeks along lowest-relief paths; bogs in lows, meadows on rises). ~4 extra value_noise calls/voxel — same cost class as voxel_relief.
2. VOCAB→AXES: expand D-228 enum VARIANTS (values on existing axes — not new axes) to cover the FULL shipped vocabulary NOW, all 7 classes. User rationale: palette composition affects the deterministic realization, so adding variants later would shift generated worlds — front-load the vocabulary. The complete axis-combination table for all 7×56 vocab items is the load-bearing content of the D-record, with a D-228 amendment for every item needing a new variant (deadfall / hardpan / lichen / fern-undergrowth have no honest existing mapping).
3. PALETTE RESIDENCY: `server/data/mosaic_constants.toml`, runtime-loaded like climate_constants.toml (T-1080 precedent). Note: the TOML is part of the deterministic input surface — weight changes change realizations; expected during Q-123 calibration, freeze after.
4. DoD METRIC IN SCOPE: add a within-patch intra-class variety field to ContrastMetrics (server/src/atlas/believability.rs) + a provisional criterion K≥3 distinct micro-features per sampled patch; Q-123 calibrates the final K. Matches the T-1081 precedent (mechanic + metric land together).
5. FAMILY GATING (grounded default, accepted): flat families (AlluvialPlain, LavaField, BraidedDelta, DuneStrand, MeanderReach) take the full mosaic; dramatic families (CliffCoast, FjordWall, IncisedGorge) get a per-family-gated non-structural modulation pass (vegetation/material only — must not warp carved geometry), mirroring voxel_relief gating. WaterBody: none. <5 ms chunk budget verification is part of implementation (T-1081 harness precedent).
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.', 'done', 'high', NULL, 'server', 'D-227', '2026-06-28 05:55:34.396', '2026-07-05 17:46:36.689', NULL, 'b5fff3907c71295a337361bce8f8720b', 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.
PROGRESS 2026-07-03: Workstreams 1-2 DONE + committed (388168eef, branch micro-habitat-mosaic).
- Cadwal→Linnaeus rename applied (body frontmatter name + biosphere_class:NativeCompatible, GJ-166A infobox, atlas proposal, D-247 clarified). Capital stays Vance.
- 5 wiki continuity edits applied (GJ-524 Mirror, GJ-166A/Keid Compatible, GJ-684A Freyburg Compatible, GJ-124 Hạ Long Compatible, GJ-197 Puerto Último Compatible). Solandar/Oshima needed none.
REMAINING:
- Workstream 3 (biosphere_class plumbing): add nullable bodies.biosphere_class column to systems-schema.sql; teach tooling/db/wiki_sync.py import_from_wiki to read biosphere_class frontmatter + apply the two-gate DEFAULT for canon-silent bodies (Gate0 atmosphere/hydrosphere, Gate1 economic_role/economic_base); add explicit frontmatter for the 3 override bodies still needing it (Solandar=NativeMirror, GJ-524=NativeMirror [uninhabited-but-alive → default would mis-Airless it], Edict/GJ244Ad=Airless [standard-atm+ice edge case]); ensure generate_wiki round-trips biosphere_class; then the BAKE (run import_from_wiki + generate_wiki → systems.db + regenerated infoboxes, incl. the Linnaeus name propagation) + commit systems.db. Add biosphere_class to BodyParams read set (D-240) as the D-247 code hook. NOTE: the bake is a systems.db binary-artifact commit — consequential, do deliberately.
- Workstream 4 (alien-ecology vocab → palette): integrate Miri''s biome × register vocabulary (cane/vane/wrack/husk/drum-root native-mirror lexicon + Earth-register names, per her Deliverable 3) into atlas/mosaic.rs + mosaic_constants.toml — extends the temperate/native baseline to the full surface-class × climate-biome × register palette. This also completes T-1084''s biome-awareness. Determinism: lock before generation.
Miri''s full Deliverable 3 vocab (the source for workstream 4) is in her T-1085 agent report.
Workstream 4 DONE (partial) + committed (5fb34675e): grassland biome-awareness (ClimateBiome from temperature_c+moisture_q → savanna/temperate/boreal/tundra palettes; savanna Forest-copses, tundra lichen/sparse) + Miri''s full biome×register vocab preserved as docs/design/alien-ecology-vocabulary.md. Cold-barren districts now render structured-sparse (D-245 climate-appropriateness): vegetated_districts 8→20, 0→39 on cold bodies. Full suite green, believability golden regenerated. CONTINUATION (noted): Forest biomes (tropical/boreal), Wetland/extreme-class biomes, the introduced-Earth register (needs biosphere_class + the managed layer), and the §8 material follow-up. REMAINING on T-1085: workstream 3 (biosphere_class schema+importer+default-rule+bake) + this WS4 continuation.
Workstream 3 DONE + committed (cc7c5ada0): biosphere_class plumbing via the stamped regen-db path. Nullable bodies.biosphere_class column (schema + migration); economy_import/bodies.py populate_biosphere_class (frontmatter override + D-247 two-gate default, recompute-all); wired as import step 17; frontmatter overrides for Solandar/GJ-524/Hạ Long (the 3 whose default deviated). systems.db regenerated + stamped (make regen-db). VERIFIED: all 9 canon exemplars correct; distribution 111 Compatible : 108 Mirror among inhabited alive (~50/50), Airless for uninhabited, TerraformedSterile for thin-atm. BodyParams Rust hook deferred (no consumer yet — mosaic is chirality-blind; Burnelli economy is the future consumer).
T-1085 STATUS: WS1 (chirality assignment) DONE, WS2 (wiki continuity) DONE, WS3 (biosphere plumbing) DONE, WS4 (vocab→palette) DONE-partial (grassland biome-awareness + vocab doc; Forest/register/§8-material continuation noted). REMAINING: (a) rename completion — propagate Linnaeus to bodies.proper_name in the DB (still Cadwal; proper_name is atlas-CLI-owned, wipe+reimport would drop non-proposal cols → needs a targeted approach); (b) WS4 continuation; (c) Burnelli food-economy consumer (separate stakeholder follow-on).
CLOSED 2026-07-05 (PR #172 merged to main @ 895bb83ba). Core deliverable shipped: WS1 chirality assignment, WS2 wiki continuity, WS3 biosphere_class plumbing (stamped regen-db), WS4 grassland biome-awareness + vocab doc. Both blocking review findings (C1 over-vegetation, H1 biosphere gate) fixed + N1 (Wetland gate) folded in; both reviewers approved. Remaining work split into follow-ups: T-1086 (propagate Linnaeus to bodies.proper_name) and T-1087 (Forest/Wetland biome palettes + introduced-Earth register + §8 material). Burnelli food-economy consumer stays a Phase-2 stakeholder follow-on (noted, not filed — cross-phase).', 'done', 'medium', 'miri', NULL, 'D-247', '2026-07-02 20:30:57.222', '2026-07-05 17:46:36.698', NULL, '8b1b13167ab0c9f9ef270ce13532778b', 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 ('06FKCER95EK266H8RWPW8XT1F0', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', '3D character locomotion sidequest — composited character walks the Gauntlet on the live server', 'Fable-5-window sidequest (sanctioned cascade exception, 2026-07-05): the presentation layer that turns discrete server tile steps into a believable walking 3D character. Scope: 3D sandbox dev scene (SR_LIVE=1, greybox tiles derived from server snapshots, no hand-authored map), CharacterVisual instantiated in-world, locomotion rig (tile-step + facing-octant interpolation, stance-aware speeds), UAL animation state machine (idle/walk/sprint/careful/crouch), D-148 camera rig (30deg low-angle follow, 45deg rotation). Server-authoritative from day one — no client-local movement model. Builds on D-148/D-149/D-244/D-066 and the T-417 stance system. If the Fable 5 window closes mid-flight, work parks here.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:01:06.475', '2026-07-06 07:01:06.475', NULL, 'b5f4cd0311d6327be4b8cc8226c1c8de', 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 ('06FKCER95EK266H8RWPW8XT1F0', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', '3D character locomotion sidequest — composited character walks the Gauntlet on the live server', 'Fable-5-window sidequest (sanctioned cascade exception, 2026-07-05): the presentation layer that turns discrete server tile steps into a believable walking 3D character. Scope: 3D sandbox dev scene (SR_LIVE=1, greybox tiles derived from server snapshots, no hand-authored map), CharacterVisual instantiated in-world, locomotion rig (tile-step + facing-octant interpolation, stance-aware speeds), UAL animation state machine (idle/walk/sprint/careful/crouch), D-148 camera rig (30deg low-angle follow, 45deg rotation). Server-authoritative from day one — no client-local movement model. Builds on D-148/D-149/D-244/D-066 and the T-417 stance system. If the Fable 5 window closes mid-flight, work parks here.
Visual reference (user, 2026-07-06): Xenonauts 2 nails the target tiles/occlusion/character perspective — readable tile grid, low-angle tactical camera with angle options (matches D-148 30deg low-angle + 45deg rotation steps and the spikes/3dpipeline angle presets), and crucially its wall/roof occlusion treatment: camera-facing walls cut down/ghosted so interiors stay readable, floors peel per z-level. One deliberate difference: our camera locks to the player character (D-015/T-116) — no free map panning. Implication for the locomotion sandbox: greybox walls should prototype camera-facing cutaway occlusion early, since wall readability interacts with the locked low-angle camera.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:01:06.475', '2026-07-06 07:10:07.799', NULL, '97cd308b84225e379e7399416932ee0f', 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 ('06FKCER95EK266H8RWPW8XT1F0', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', '3D character locomotion sidequest — composited character walks the Gauntlet on the live server', 'Fable-5-window sidequest (sanctioned cascade exception, 2026-07-05): the presentation layer that turns discrete server tile steps into a believable walking 3D character. Scope: 3D sandbox dev scene (SR_LIVE=1, greybox tiles derived from server snapshots, no hand-authored map), CharacterVisual instantiated in-world, locomotion rig (tile-step + facing-octant interpolation, stance-aware speeds), UAL animation state machine (idle/walk/sprint/careful/crouch), D-148 camera rig (30deg low-angle follow, 45deg rotation). Server-authoritative from day one — no client-local movement model. Builds on D-148/D-149/D-244/D-066 and the T-417 stance system. If the Fable 5 window closes mid-flight, work parks here.
Visual reference (user, 2026-07-06): Xenonauts 2 nails the target tiles/occlusion/character perspective — readable tile grid, low-angle tactical camera with angle options (matches D-148 30deg low-angle + 45deg rotation steps and the spikes/3dpipeline angle presets), and crucially its wall/roof occlusion treatment: camera-facing walls cut down/ghosted so interiors stay readable, floors peel per z-level. One deliberate difference: our camera locks to the player character (D-015/T-116) — no free map panning. Implication for the locomotion sandbox: greybox walls should prototype camera-facing cutaway occlusion early, since wall readability interacts with the locked low-angle camera.
Scoping call (user + lead agreement, 2026-07-06): wall cutaway is CLIENT-SIDE presentation, decoupled from fog-of-perception. Rationale: two occlusion systems with different subjects — perception/fog governs what the CHARACTER knows (server-enforced info boundary; client only receives observer-filtered geometry), cutaway governs what the PLAYER''S CAMERA sees (pure render mode on wall data the client already holds). They compose: cutaway cuts render height; fog overlay still gates what is visible behind. Walls remain first-class client data objects (future destruction/explosion interactions), cutaway is a render mode on them like tint/LOD. Nuance parked for Phase 5 proper: destroyed-while-unobserved walls should render in remembered state (fog-memory applied to geometry). If this holds through implementation, mint a D-record (perception domain) when the sidequest lands.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:01:06.475', '2026-07-06 07:14:14.484', NULL, 'ebbbd5ff1ed11d0ebf37303782e6e1d5', 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 ('06FKCPBF41RWK9PW9M22Q7961C', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Clothing catalogue engine + basic set — recolorable, logo-capable everyday garments', 'Fable-5-window sidequest workstream 2 (user directive 2026-07-06, extends T-1088 scope family): build the clothing ENGINE and a basic set now, park variety for later. Target bar: RimWorld''s original apparel breadth — varied enough to not feel limiting. Goal experience: being able to pick and SHOP for clothes — colorize + slap logos on casualwear. Basic set: t-shirt (logo-capable), hoodie (logo-capable), button-down shirt, shorts, jeans, formal pants, jacket, colorable uniform, black suit (moved up so officials are includable). Skipped by explicit call: capes, dresses/skirts (parked — difficulty tier). Engine deliverables: (1) multi-region tint masks — requires Quaternius Source tier files (USD5 Patreon month, one-time download, CC0 retained — user purchase action); (2) logo decal region on garment chest UV — flat 2D artwork per D-244, diegetic hook into economy generated brands (wiki/economics/corporations/generated_brands.toml) for brand-logo shirts; (3) manifest + CharacterVisualDescriptor category coverage (slots/tints already generic); (4) batch-fit each authored garment to all 11 body types via the validated Sprint-28 Blender Surface Deform pipeline (tooling/blender_*.py). Authoring routes per garment TBD by feasibility pass: derive from owned Fantasy pack meshes (62 parts, e.g. tunic->tshirt sleeve cut, pants->shorts hem cut) vs hand-author low-poly on the 65-bone rig (Garment Tool). Related: T-1088 locomotion sandbox doubles as the garment-under-animation QA rig (cycle outfits through Walk/Sprint/Crouch_Fwd).', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-06 07:34:18.656', NULL, 'f95f8855e41f59a6c23ab7adb4dd7b31', 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 ('06FKCPBF41RWK9PW9M22Q7961C', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Clothing catalogue engine + basic set — recolorable, logo-capable everyday garments', 'Fable-5-window sidequest workstream 2 (user directive 2026-07-06, extends T-1088 scope family): build the clothing ENGINE and a basic set now, park variety for later. Target bar: RimWorld''s original apparel breadth — varied enough to not feel limiting. Goal experience: being able to pick and SHOP for clothes — colorize + slap logos on casualwear. Basic set: t-shirt (logo-capable), hoodie (logo-capable), button-down shirt, shorts, jeans, formal pants, jacket, colorable uniform, black suit (moved up so officials are includable). Skipped by explicit call: capes, dresses/skirts (parked — difficulty tier). Engine deliverables: (1) multi-region tint masks — requires Quaternius Source tier files (USD5 Patreon month, one-time download, CC0 retained — user purchase action); (2) logo decal region on garment chest UV — flat 2D artwork per D-244, diegetic hook into economy generated brands (wiki/economics/corporations/generated_brands.toml) for brand-logo shirts; (3) manifest + CharacterVisualDescriptor category coverage (slots/tints already generic); (4) batch-fit each authored garment to all 11 body types via the validated Sprint-28 Blender Surface Deform pipeline (tooling/blender_*.py). Authoring routes per garment TBD by feasibility pass: derive from owned Fantasy pack meshes (62 parts, e.g. tunic->tshirt sleeve cut, pants->shorts hem cut) vs hand-author low-poly on the 65-bone rig (Garment Tool). Related: T-1088 locomotion sandbox doubles as the garment-under-animation QA rig (cycle outfits through Walk/Sprint/Crouch_Fwd).
Correction (2026-07-06): Source tier is PARTIALLY owned already — docs/assets/downloads/ (gitignored vendor archives, present in both checkouts) holds Universal Base Characters[Source] (630MB: full .blend files for all 6 base bodies incl. FullBody + OnlyHead variants, hairstyle .blends origin-at-0 + rigged-to-head-bone, texture masters, Godot engine project) plus UBC[Standard], UAL 1+2[Standard], Modular Character Outfits Fantasy[Standard]. NOT owned: Fantasy Outfits[Source] — the garment recolor masks + named vertex groups. Consequence: authoring NEW garments is fully unblocked (author against source body .blends, create our own tint masks/vertex groups — no purchase needed); only pack-DERIVED garments'' clean masks would want one more USD5 Patreon month, decision deferred until the feasibility route table says how much derivation we actually use.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-06 07:39:21.760', NULL, '5db4379bf42ee42226f1fb7e212e6348', 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 ('06FKCPBF41RWK9PW9M22Q7961C', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Clothing catalogue engine + basic set — recolorable, logo-capable everyday garments', 'Fable-5-window sidequest workstream 2 (user directive 2026-07-06, extends T-1088 scope family): build the clothing ENGINE and a basic set now, park variety for later. Target bar: RimWorld''s original apparel breadth — varied enough to not feel limiting. Goal experience: being able to pick and SHOP for clothes — colorize + slap logos on casualwear. Basic set: t-shirt (logo-capable), hoodie (logo-capable), button-down shirt, shorts, jeans, formal pants, jacket, colorable uniform, black suit (moved up so officials are includable). Skipped by explicit call: capes, dresses/skirts (parked — difficulty tier). Engine deliverables: (1) multi-region tint masks — requires Quaternius Source tier files (USD5 Patreon month, one-time download, CC0 retained — user purchase action); (2) logo decal region on garment chest UV — flat 2D artwork per D-244, diegetic hook into economy generated brands (wiki/economics/corporations/generated_brands.toml) for brand-logo shirts; (3) manifest + CharacterVisualDescriptor category coverage (slots/tints already generic); (4) batch-fit each authored garment to all 11 body types via the validated Sprint-28 Blender Surface Deform pipeline (tooling/blender_*.py). Authoring routes per garment TBD by feasibility pass: derive from owned Fantasy pack meshes (62 parts, e.g. tunic->tshirt sleeve cut, pants->shorts hem cut) vs hand-author low-poly on the 65-bone rig (Garment Tool). Related: T-1088 locomotion sandbox doubles as the garment-under-animation QA rig (cycle outfits through Walk/Sprint/Crouch_Fwd).
Correction (2026-07-06): Source tier is PARTIALLY owned already — docs/assets/downloads/ (gitignored vendor archives, present in both checkouts) holds Universal Base Characters[Source] (630MB: full .blend files for all 6 base bodies incl. FullBody + OnlyHead variants, hairstyle .blends origin-at-0 + rigged-to-head-bone, texture masters, Godot engine project) plus UBC[Standard], UAL 1+2[Standard], Modular Character Outfits Fantasy[Standard]. NOT owned: Fantasy Outfits[Source] — the garment recolor masks + named vertex groups. Consequence: authoring NEW garments is fully unblocked (author against source body .blends, create our own tint masks/vertex groups — no purchase needed); only pack-DERIVED garments'' clean masks would want one more USD5 Patreon month, decision deferred until the feasibility route table says how much derivation we actually use.
Art direction (user, 2026-07-06): garments read CYBERPUNK/MODERN — near-future everyday civilian + corporate wear — NOT fantasy/medieval. The owned Fantasy pack is at most a topology donor (low-poly base shapes like pants/shirts are genre-neutral; genre lives in silhouette details, textures, trims, and accessories) — no medieval trim/textures may survive into shipped garments. Weight the authoring routes accordingly: texture identity matters as much as mesh. Canon hook: the wiki already ships a garment brand — thrds (wiki/star-systems/GJ-475/index.md, Braemar fiber cooperative, corridor fashion markets) — so branded clothing connects to Phase-1 lore, not only generated_brands.toml. This emphasis also feeds the Quaternius-pivot evaluation: commercial alternatives skew sci-fi/urban while Quaternius clothing is fantasy-only.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-06 07:42:06.637', NULL, '6400115dc840a8e65f55b1932a1449c1', 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 ('06FKCPBF41RWK9PW9M22Q7961C', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Clothing catalogue engine + basic set — recolorable, logo-capable everyday garments', 'Fable-5-window sidequest workstream 2 (user directive 2026-07-06, extends T-1088 scope family): build the clothing ENGINE and a basic set now, park variety for later. Target bar: RimWorld''s original apparel breadth — varied enough to not feel limiting. Goal experience: being able to pick and SHOP for clothes — colorize + slap logos on casualwear. Basic set: t-shirt (logo-capable), hoodie (logo-capable), button-down shirt, shorts, jeans, formal pants, jacket, colorable uniform, black suit (moved up so officials are includable). Skipped by explicit call: capes, dresses/skirts (parked — difficulty tier). Engine deliverables: (1) multi-region tint masks — requires Quaternius Source tier files (USD5 Patreon month, one-time download, CC0 retained — user purchase action); (2) logo decal region on garment chest UV — flat 2D artwork per D-244, diegetic hook into economy generated brands (wiki/economics/corporations/generated_brands.toml) for brand-logo shirts; (3) manifest + CharacterVisualDescriptor category coverage (slots/tints already generic); (4) batch-fit each authored garment to all 11 body types via the validated Sprint-28 Blender Surface Deform pipeline (tooling/blender_*.py). Authoring routes per garment TBD by feasibility pass: derive from owned Fantasy pack meshes (62 parts, e.g. tunic->tshirt sleeve cut, pants->shorts hem cut) vs hand-author low-poly on the 65-bone rig (Garment Tool). Related: T-1088 locomotion sandbox doubles as the garment-under-animation QA rig (cycle outfits through Walk/Sprint/Crouch_Fwd).
Correction (2026-07-06): Source tier is PARTIALLY owned already — docs/assets/downloads/ (gitignored vendor archives, present in both checkouts) holds Universal Base Characters[Source] (630MB: full .blend files for all 6 base bodies incl. FullBody + OnlyHead variants, hairstyle .blends origin-at-0 + rigged-to-head-bone, texture masters, Godot engine project) plus UBC[Standard], UAL 1+2[Standard], Modular Character Outfits Fantasy[Standard]. NOT owned: Fantasy Outfits[Source] — the garment recolor masks + named vertex groups. Consequence: authoring NEW garments is fully unblocked (author against source body .blends, create our own tint masks/vertex groups — no purchase needed); only pack-DERIVED garments'' clean masks would want one more USD5 Patreon month, decision deferred until the feasibility route table says how much derivation we actually use.
Art direction (user, 2026-07-06): garments read CYBERPUNK/MODERN — near-future everyday civilian + corporate wear — NOT fantasy/medieval. The owned Fantasy pack is at most a topology donor (low-poly base shapes like pants/shirts are genre-neutral; genre lives in silhouette details, textures, trims, and accessories) — no medieval trim/textures may survive into shipped garments. Weight the authoring routes accordingly: texture identity matters as much as mesh. Canon hook: the wiki already ships a garment brand — thrds (wiki/star-systems/GJ-475/index.md, Braemar fiber cooperative, corridor fashion markets) — so branded clothing connects to Phase-1 lore, not only generated_brands.toml. This emphasis also feeds the Quaternius-pivot evaluation: commercial alternatives skew sci-fi/urban while Quaternius clothing is fantasy-only.
Nuance (user, 2026-07-06): the cyberpunk/modern direction is the DEFAULT, not a prohibition — fashion in the Reach is cyclical, so fantasy/medieval-flavored garments may appear here and there as deliberate fashion statements (subculture pieces, luxury/revival trims, statement outerwear). Supersedes the previous note''s hard ''no medieval trim may survive'' line. Practical consequence: a small number of Fantasy-pack pieces may ship near-as-is where they read as intentional fashion; the basic set itself stays modern.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-06 07:42:24.025', NULL, '990ed335ad3d3f59255ba4f8f8ef1c07', 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 ('06FKCPBF41RWK9PW9M22Q7961C', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Clothing catalogue engine + basic set — recolorable, logo-capable everyday garments', 'Fable-5-window sidequest workstream 2 (user directive 2026-07-06, extends T-1088 scope family): build the clothing ENGINE and a basic set now, park variety for later. Target bar: RimWorld''s original apparel breadth — varied enough to not feel limiting. Goal experience: being able to pick and SHOP for clothes — colorize + slap logos on casualwear. Basic set: t-shirt (logo-capable), hoodie (logo-capable), button-down shirt, shorts, jeans, formal pants, jacket, colorable uniform, black suit (moved up so officials are includable). Skipped by explicit call: capes, dresses/skirts (parked — difficulty tier). Engine deliverables: (1) multi-region tint masks — requires Quaternius Source tier files (USD5 Patreon month, one-time download, CC0 retained — user purchase action); (2) logo decal region on garment chest UV — flat 2D artwork per D-244, diegetic hook into economy generated brands (wiki/economics/corporations/generated_brands.toml) for brand-logo shirts; (3) manifest + CharacterVisualDescriptor category coverage (slots/tints already generic); (4) batch-fit each authored garment to all 11 body types via the validated Sprint-28 Blender Surface Deform pipeline (tooling/blender_*.py). Authoring routes per garment TBD by feasibility pass: derive from owned Fantasy pack meshes (62 parts, e.g. tunic->tshirt sleeve cut, pants->shorts hem cut) vs hand-author low-poly on the 65-bone rig (Garment Tool). Related: T-1088 locomotion sandbox doubles as the garment-under-animation QA rig (cycle outfits through Walk/Sprint/Crouch_Fwd).
Correction (2026-07-06): Source tier is PARTIALLY owned already — docs/assets/downloads/ (gitignored vendor archives, present in both checkouts) holds Universal Base Characters[Source] (630MB: full .blend files for all 6 base bodies incl. FullBody + OnlyHead variants, hairstyle .blends origin-at-0 + rigged-to-head-bone, texture masters, Godot engine project) plus UBC[Standard], UAL 1+2[Standard], Modular Character Outfits Fantasy[Standard]. NOT owned: Fantasy Outfits[Source] — the garment recolor masks + named vertex groups. Consequence: authoring NEW garments is fully unblocked (author against source body .blends, create our own tint masks/vertex groups — no purchase needed); only pack-DERIVED garments'' clean masks would want one more USD5 Patreon month, decision deferred until the feasibility route table says how much derivation we actually use.
Art direction (user, 2026-07-06): garments read CYBERPUNK/MODERN — near-future everyday civilian + corporate wear — NOT fantasy/medieval. The owned Fantasy pack is at most a topology donor (low-poly base shapes like pants/shirts are genre-neutral; genre lives in silhouette details, textures, trims, and accessories) — no medieval trim/textures may survive into shipped garments. Weight the authoring routes accordingly: texture identity matters as much as mesh. Canon hook: the wiki already ships a garment brand — thrds (wiki/star-systems/GJ-475/index.md, Braemar fiber cooperative, corridor fashion markets) — so branded clothing connects to Phase-1 lore, not only generated_brands.toml. This emphasis also feeds the Quaternius-pivot evaluation: commercial alternatives skew sci-fi/urban while Quaternius clothing is fantasy-only.
Nuance (user, 2026-07-06): the cyberpunk/modern direction is the DEFAULT, not a prohibition — fashion in the Reach is cyclical, so fantasy/medieval-flavored garments may appear here and there as deliberate fashion statements (subculture pieces, luxury/revival trims, statement outerwear). Supersedes the previous note''s hard ''no medieval trim may survive'' line. Practical consequence: a small number of Fantasy-pack pieces may ship near-as-is where they read as intentional fashion; the basic set itself stays modern.
Art direction CONSOLIDATED (user, 2026-07-06 — supersedes the two notes above): the Reach has no single clothing style — ALL styles coexist, with modern/cyberpunk probably the most prevalent. Style is a prevalence DISTRIBUTION, not a genre rule, and may eventually vary by culture/settlement (fits the simulation DNA; fashion-as-cultural-data is a later phase concern, not this ticket). Consequences now: (1) the engine is style-agnostic by construction (slots/tints/logos carry no genre); (2) the basic set targets the prevalent center — modern casual + corporate — because it dresses the most NPCs and the shopping experience; (3) garments get a lightweight style tag in the manifest (e.g. style: modern|formal|fantasy|utility) as the cheap seam so per-culture distributions can be authored later without retrofitting; (4) Fantasy-pack pieces are legitimate catalogue members under their own tag, prevalence-weighted low.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-06 07:43:12.193', NULL, '4fb0958003b1bd349d7e58e07e53690e', 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 ('06FKCPBF41RWK9PW9M22Q7961C', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Clothing catalogue engine + basic set — recolorable, logo-capable everyday garments', 'Fable-5-window sidequest workstream 2 (user directive 2026-07-06, extends T-1088 scope family): build the clothing ENGINE and a basic set now, park variety for later. Target bar: RimWorld''s original apparel breadth — varied enough to not feel limiting. Goal experience: being able to pick and SHOP for clothes — colorize + slap logos on casualwear. Basic set: t-shirt (logo-capable), hoodie (logo-capable), button-down shirt, shorts, jeans, formal pants, jacket, colorable uniform, black suit (moved up so officials are includable). Skipped by explicit call: capes, dresses/skirts (parked — difficulty tier). Engine deliverables: (1) multi-region tint masks — requires Quaternius Source tier files (USD5 Patreon month, one-time download, CC0 retained — user purchase action); (2) logo decal region on garment chest UV — flat 2D artwork per D-244, diegetic hook into economy generated brands (wiki/economics/corporations/generated_brands.toml) for brand-logo shirts; (3) manifest + CharacterVisualDescriptor category coverage (slots/tints already generic); (4) batch-fit each authored garment to all 11 body types via the validated Sprint-28 Blender Surface Deform pipeline (tooling/blender_*.py). Authoring routes per garment TBD by feasibility pass: derive from owned Fantasy pack meshes (62 parts, e.g. tunic->tshirt sleeve cut, pants->shorts hem cut) vs hand-author low-poly on the 65-bone rig (Garment Tool). Related: T-1088 locomotion sandbox doubles as the garment-under-animation QA rig (cycle outfits through Walk/Sprint/Crouch_Fwd).
Correction (2026-07-06): Source tier is PARTIALLY owned already — docs/assets/downloads/ (gitignored vendor archives, present in both checkouts) holds Universal Base Characters[Source] (630MB: full .blend files for all 6 base bodies incl. FullBody + OnlyHead variants, hairstyle .blends origin-at-0 + rigged-to-head-bone, texture masters, Godot engine project) plus UBC[Standard], UAL 1+2[Standard], Modular Character Outfits Fantasy[Standard]. NOT owned: Fantasy Outfits[Source] — the garment recolor masks + named vertex groups. Consequence: authoring NEW garments is fully unblocked (author against source body .blends, create our own tint masks/vertex groups — no purchase needed); only pack-DERIVED garments'' clean masks would want one more USD5 Patreon month, decision deferred until the feasibility route table says how much derivation we actually use.
Art direction (user, 2026-07-06): garments read CYBERPUNK/MODERN — near-future everyday civilian + corporate wear — NOT fantasy/medieval. The owned Fantasy pack is at most a topology donor (low-poly base shapes like pants/shirts are genre-neutral; genre lives in silhouette details, textures, trims, and accessories) — no medieval trim/textures may survive into shipped garments. Weight the authoring routes accordingly: texture identity matters as much as mesh. Canon hook: the wiki already ships a garment brand — thrds (wiki/star-systems/GJ-475/index.md, Braemar fiber cooperative, corridor fashion markets) — so branded clothing connects to Phase-1 lore, not only generated_brands.toml. This emphasis also feeds the Quaternius-pivot evaluation: commercial alternatives skew sci-fi/urban while Quaternius clothing is fantasy-only.
Nuance (user, 2026-07-06): the cyberpunk/modern direction is the DEFAULT, not a prohibition — fashion in the Reach is cyclical, so fantasy/medieval-flavored garments may appear here and there as deliberate fashion statements (subculture pieces, luxury/revival trims, statement outerwear). Supersedes the previous note''s hard ''no medieval trim may survive'' line. Practical consequence: a small number of Fantasy-pack pieces may ship near-as-is where they read as intentional fashion; the basic set itself stays modern.
Art direction CONSOLIDATED (user, 2026-07-06 — supersedes the two notes above): the Reach has no single clothing style — ALL styles coexist, with modern/cyberpunk probably the most prevalent. Style is a prevalence DISTRIBUTION, not a genre rule, and may eventually vary by culture/settlement (fits the simulation DNA; fashion-as-cultural-data is a later phase concern, not this ticket). Consequences now: (1) the engine is style-agnostic by construction (slots/tints/logos carry no genre); (2) the basic set targets the prevalent center — modern casual + corporate — because it dresses the most NPCs and the shopping experience; (3) garments get a lightweight style tag in the manifest (e.g. style: modern|formal|fantasy|utility) as the cheap seam so per-culture distributions can be authored later without retrofitting; (4) Fantasy-pack pieces are legitimate catalogue members under their own tag, prevalence-weighted low.
Scope pin (user, 2026-07-06, final word on style): for simplicity, THIS ticket ships ONE style only — cyberpunk/modern. The pluralistic distribution model above remains the long-term truth (keep the manifest style tag as the seam), but the v1 basic set is single-style; other styles are catalogue growth, not scope.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-06 07:43:39.155', NULL, '35d33cc74fde16fe96e0e4000d26056b', 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 ('06FKCPBF41RWK9PW9M22Q7961C', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Clothing catalogue engine + basic set — recolorable, logo-capable everyday garments', 'Fable-5-window sidequest workstream 2 (user directive 2026-07-06, extends T-1088 scope family): build the clothing ENGINE and a basic set now, park variety for later. Target bar: RimWorld''s original apparel breadth — varied enough to not feel limiting. Goal experience: being able to pick and SHOP for clothes — colorize + slap logos on casualwear. Basic set: t-shirt (logo-capable), hoodie (logo-capable), button-down shirt, shorts, jeans, formal pants, jacket, colorable uniform, black suit (moved up so officials are includable). Skipped by explicit call: capes, dresses/skirts (parked — difficulty tier). Engine deliverables: (1) multi-region tint masks — requires Quaternius Source tier files (USD5 Patreon month, one-time download, CC0 retained — user purchase action); (2) logo decal region on garment chest UV — flat 2D artwork per D-244, diegetic hook into economy generated brands (wiki/economics/corporations/generated_brands.toml) for brand-logo shirts; (3) manifest + CharacterVisualDescriptor category coverage (slots/tints already generic); (4) batch-fit each authored garment to all 11 body types via the validated Sprint-28 Blender Surface Deform pipeline (tooling/blender_*.py). Authoring routes per garment TBD by feasibility pass: derive from owned Fantasy pack meshes (62 parts, e.g. tunic->tshirt sleeve cut, pants->shorts hem cut) vs hand-author low-poly on the 65-bone rig (Garment Tool). Related: T-1088 locomotion sandbox doubles as the garment-under-animation QA rig (cycle outfits through Walk/Sprint/Crouch_Fwd).
Correction (2026-07-06): Source tier is PARTIALLY owned already — docs/assets/downloads/ (gitignored vendor archives, present in both checkouts) holds Universal Base Characters[Source] (630MB: full .blend files for all 6 base bodies incl. FullBody + OnlyHead variants, hairstyle .blends origin-at-0 + rigged-to-head-bone, texture masters, Godot engine project) plus UBC[Standard], UAL 1+2[Standard], Modular Character Outfits Fantasy[Standard]. NOT owned: Fantasy Outfits[Source] — the garment recolor masks + named vertex groups. Consequence: authoring NEW garments is fully unblocked (author against source body .blends, create our own tint masks/vertex groups — no purchase needed); only pack-DERIVED garments'' clean masks would want one more USD5 Patreon month, decision deferred until the feasibility route table says how much derivation we actually use.
Art direction (user, 2026-07-06): garments read CYBERPUNK/MODERN — near-future everyday civilian + corporate wear — NOT fantasy/medieval. The owned Fantasy pack is at most a topology donor (low-poly base shapes like pants/shirts are genre-neutral; genre lives in silhouette details, textures, trims, and accessories) — no medieval trim/textures may survive into shipped garments. Weight the authoring routes accordingly: texture identity matters as much as mesh. Canon hook: the wiki already ships a garment brand — thrds (wiki/star-systems/GJ-475/index.md, Braemar fiber cooperative, corridor fashion markets) — so branded clothing connects to Phase-1 lore, not only generated_brands.toml. This emphasis also feeds the Quaternius-pivot evaluation: commercial alternatives skew sci-fi/urban while Quaternius clothing is fantasy-only.
Nuance (user, 2026-07-06): the cyberpunk/modern direction is the DEFAULT, not a prohibition — fashion in the Reach is cyclical, so fantasy/medieval-flavored garments may appear here and there as deliberate fashion statements (subculture pieces, luxury/revival trims, statement outerwear). Supersedes the previous note''s hard ''no medieval trim may survive'' line. Practical consequence: a small number of Fantasy-pack pieces may ship near-as-is where they read as intentional fashion; the basic set itself stays modern.
Art direction CONSOLIDATED (user, 2026-07-06 — supersedes the two notes above): the Reach has no single clothing style — ALL styles coexist, with modern/cyberpunk probably the most prevalent. Style is a prevalence DISTRIBUTION, not a genre rule, and may eventually vary by culture/settlement (fits the simulation DNA; fashion-as-cultural-data is a later phase concern, not this ticket). Consequences now: (1) the engine is style-agnostic by construction (slots/tints/logos carry no genre); (2) the basic set targets the prevalent center — modern casual + corporate — because it dresses the most NPCs and the shopping experience; (3) garments get a lightweight style tag in the manifest (e.g. style: modern|formal|fantasy|utility) as the cheap seam so per-culture distributions can be authored later without retrofitting; (4) Fantasy-pack pieces are legitimate catalogue members under their own tag, prevalence-weighted low.
Scope pin (user, 2026-07-06, final word on style): for simplicity, THIS ticket ships ONE style only — cyberpunk/modern. The pluralistic distribution model above remains the long-term truth (keep the manifest style tag as the seam), but the v1 basic set is single-style; other styles are catalogue growth, not scope.
Route challenge (user, 2026-07-06): not sold on staying Quaternius-primary — ''Quaternius is limited and will move the wall at too slow a pace; I''d rather pick something that has what we need now.'' Fresh store verification (2026-07-06): Sidekick packs are one-time USD199.99, perpetual, engine-agnostic license (Godot legal, unsupported); NO raw FBX — extraction from unitypackage (community-proven: unitypackage_godot, third-party Sidekick Creator Godot plugin incl. body blend shapes); ''Sold out'' labels are a broken-theme artifact per Shopify JSON (needs one browser cart check); SyntyPass rejected (assets stop being developable after cancel). Relevant packs: Starter (FREE, 57+91 parts), Modern Civilians (165 parts = v1 basic set territory), Sci-Fi Civilians (103 parts, cyberpunk accents), Modern Police (uniforms/officials). DECISION NOW GATED ON THE FREE SPIKE: user downloads free Starter Pack -> Bridge-C spike (extract garment -> Robust Weight Transfer onto our rig -> 11-body batch -> animation QA in T-1088 sandbox). Pass -> buy Modern Civilians, Synty = primary garment supply (+UAL1/UAL2 Source ~USD30 for clips — no Synty substitute exists for Godot animations; Fantasy Outfits Source drops to low priority under the single-style pin). Fail -> hand-author route stands. Stay-vs-pivot governance record HELD until spike evidence lands.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-06 08:14:11.037', NULL, 'ecbabb557fe69f476b66e9b4ec50f93e', 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 ('06FKCPBF41RWK9PW9M22Q7961C', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Clothing catalogue engine + basic set — recolorable, logo-capable everyday garments', 'Fable-5-window sidequest workstream 2 (user directive 2026-07-06, extends T-1088 scope family): build the clothing ENGINE and a basic set now, park variety for later. Target bar: RimWorld''s original apparel breadth — varied enough to not feel limiting. Goal experience: being able to pick and SHOP for clothes — colorize + slap logos on casualwear. Basic set: t-shirt (logo-capable), hoodie (logo-capable), button-down shirt, shorts, jeans, formal pants, jacket, colorable uniform, black suit (moved up so officials are includable). Skipped by explicit call: capes, dresses/skirts (parked — difficulty tier). Engine deliverables: (1) multi-region tint masks — requires Quaternius Source tier files (USD5 Patreon month, one-time download, CC0 retained — user purchase action); (2) logo decal region on garment chest UV — flat 2D artwork per D-244, diegetic hook into economy generated brands (wiki/economics/corporations/generated_brands.toml) for brand-logo shirts; (3) manifest + CharacterVisualDescriptor category coverage (slots/tints already generic); (4) batch-fit each authored garment to all 11 body types via the validated Sprint-28 Blender Surface Deform pipeline (tooling/blender_*.py). Authoring routes per garment TBD by feasibility pass: derive from owned Fantasy pack meshes (62 parts, e.g. tunic->tshirt sleeve cut, pants->shorts hem cut) vs hand-author low-poly on the 65-bone rig (Garment Tool). Related: T-1088 locomotion sandbox doubles as the garment-under-animation QA rig (cycle outfits through Walk/Sprint/Crouch_Fwd).
Correction (2026-07-06): Source tier is PARTIALLY owned already — docs/assets/downloads/ (gitignored vendor archives, present in both checkouts) holds Universal Base Characters[Source] (630MB: full .blend files for all 6 base bodies incl. FullBody + OnlyHead variants, hairstyle .blends origin-at-0 + rigged-to-head-bone, texture masters, Godot engine project) plus UBC[Standard], UAL 1+2[Standard], Modular Character Outfits Fantasy[Standard]. NOT owned: Fantasy Outfits[Source] — the garment recolor masks + named vertex groups. Consequence: authoring NEW garments is fully unblocked (author against source body .blends, create our own tint masks/vertex groups — no purchase needed); only pack-DERIVED garments'' clean masks would want one more USD5 Patreon month, decision deferred until the feasibility route table says how much derivation we actually use.
Art direction (user, 2026-07-06): garments read CYBERPUNK/MODERN — near-future everyday civilian + corporate wear — NOT fantasy/medieval. The owned Fantasy pack is at most a topology donor (low-poly base shapes like pants/shirts are genre-neutral; genre lives in silhouette details, textures, trims, and accessories) — no medieval trim/textures may survive into shipped garments. Weight the authoring routes accordingly: texture identity matters as much as mesh. Canon hook: the wiki already ships a garment brand — thrds (wiki/star-systems/GJ-475/index.md, Braemar fiber cooperative, corridor fashion markets) — so branded clothing connects to Phase-1 lore, not only generated_brands.toml. This emphasis also feeds the Quaternius-pivot evaluation: commercial alternatives skew sci-fi/urban while Quaternius clothing is fantasy-only.
Nuance (user, 2026-07-06): the cyberpunk/modern direction is the DEFAULT, not a prohibition — fashion in the Reach is cyclical, so fantasy/medieval-flavored garments may appear here and there as deliberate fashion statements (subculture pieces, luxury/revival trims, statement outerwear). Supersedes the previous note''s hard ''no medieval trim may survive'' line. Practical consequence: a small number of Fantasy-pack pieces may ship near-as-is where they read as intentional fashion; the basic set itself stays modern.
Art direction CONSOLIDATED (user, 2026-07-06 — supersedes the two notes above): the Reach has no single clothing style — ALL styles coexist, with modern/cyberpunk probably the most prevalent. Style is a prevalence DISTRIBUTION, not a genre rule, and may eventually vary by culture/settlement (fits the simulation DNA; fashion-as-cultural-data is a later phase concern, not this ticket). Consequences now: (1) the engine is style-agnostic by construction (slots/tints/logos carry no genre); (2) the basic set targets the prevalent center — modern casual + corporate — because it dresses the most NPCs and the shopping experience; (3) garments get a lightweight style tag in the manifest (e.g. style: modern|formal|fantasy|utility) as the cheap seam so per-culture distributions can be authored later without retrofitting; (4) Fantasy-pack pieces are legitimate catalogue members under their own tag, prevalence-weighted low.
Scope pin (user, 2026-07-06, final word on style): for simplicity, THIS ticket ships ONE style only — cyberpunk/modern. The pluralistic distribution model above remains the long-term truth (keep the manifest style tag as the seam), but the v1 basic set is single-style; other styles are catalogue growth, not scope.
Route challenge (user, 2026-07-06): not sold on staying Quaternius-primary — ''Quaternius is limited and will move the wall at too slow a pace; I''d rather pick something that has what we need now.'' Fresh store verification (2026-07-06): Sidekick packs are one-time USD199.99, perpetual, engine-agnostic license (Godot legal, unsupported); NO raw FBX — extraction from unitypackage (community-proven: unitypackage_godot, third-party Sidekick Creator Godot plugin incl. body blend shapes); ''Sold out'' labels are a broken-theme artifact per Shopify JSON (needs one browser cart check); SyntyPass rejected (assets stop being developable after cancel). Relevant packs: Starter (FREE, 57+91 parts), Modern Civilians (165 parts = v1 basic set territory), Sci-Fi Civilians (103 parts, cyberpunk accents), Modern Police (uniforms/officials). DECISION NOW GATED ON THE FREE SPIKE: user downloads free Starter Pack -> Bridge-C spike (extract garment -> Robust Weight Transfer onto our rig -> 11-body batch -> animation QA in T-1088 sandbox). Pass -> buy Modern Civilians, Synty = primary garment supply (+UAL1/UAL2 Source ~USD30 for clips — no Synty substitute exists for Godot animations; Fantasy Outfits Source drops to low priority under the single-style pin). Fail -> hand-author route stands. Stay-vs-pivot governance record HELD until spike evidence lands.
Scope guard (user concern, 2026-07-06): Synty intake must NOT displace the create-stuff-yourself pipeline — the user''s goal includes in-house garment creation capability, not just swapping vendor catalogs. Resolution: three-route portfolio sharing one engine — (a) Synty intake = variety bootstrap only; (b) derivation scripting (cuts/merges/retexture, agent-automatable) = create-yourself lite, turns any donor topology into original designs; (c) TRUE AUTHORING on owned UBC Source body .blends = the pipeline proper. GUARANTEE: the colorable uniform and black suit (no donor exists in any pack) are built via route (c) as the authoring pipeline''s end-to-end proof — non-negotiable deliverable of this ticket, so the pipeline exists even if Synty covers everything else. Spike status: extraction done inline by lead (158 FBX, sci-fi civilian theme confirmed); DECISIVE finding — Sidekick rig is UE-Mannequin-named, 52/65 bone-name overlap with our rig, sample garment binds to 12 vgroups of which 11 match ours exactly (only head->Head case rename) => transplant likely rename-and-rebind, far cheaper than the weight-transfer the pivot eval assumed; body blend shapes travel in the FBX. Transplant phase parked pending API recovery / token-window reset.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-06 08:59:05.485', NULL, 'cb85c826871146df718f5cc94ee89c7d', 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 ('06FKE0EW2617K9VE4QHSYSR9HW', 'bug', '06FB0TNSRXX29H5X0EB0F0GBW4', '5 of 11 body types misrender bare in CharacterVisual (thin_f/m, heavy_f/m, child)', 'Found by the T-1089 Synty intake spike (2026-07-06), body-only controls with NO garment attached: thin_f/m, heavy_f/m, child misrender in CharacterVisual (exploded/spider arms, detached head/hair; child 303mm max warp, 87 collapsed edges; f-bodies bust-through). These are the newer high-poly bodies (no seg_hips, different segmentation) — exactly the 5 with no production peasant-clothing variants. Upstream of ANY clothing route; blocks T-1089''s 11-of-11 acceptance criterion and resolves part of Q-060. Evidence: spikes/synty-intake/out/bodies_fit_log.json + out/qa_bodies/ (44 screenshots) + out/bodies_recon.json.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 10:38:16.593', '2026-07-06 10:38:16.593', NULL, '3c7e5d110be1dde94aa27379cf3664e4', 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 ('06FKE0J4B8GQ17NFFV8N7D45BW', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Silence MCH-*/ik_* skin-bind warnings from production head/hair asset (Rigify control bones)', 'Godot logs ~600 lines of ''Skin bind #N ... Skeleton3D has no bone'' per CharacterVisual load — traced by the T-1089 spike to a production head/hair asset carrying Rigify control-bone names (MCH-*/ik_*), not to any garment. Strip the stale bind targets from the asset (Blender re-export) or filter at import. Pure log hygiene / asset cleanliness.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-06 10:38:43.290', '2026-07-06 10:38:43.290', NULL, 'd01e49191a4e9bcd5ca83866c2d65468', 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 ('06FKCPBF41RWK9PW9M22Q7961C', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Clothing catalogue engine + basic set — recolorable, logo-capable everyday garments', 'Fable-5-window sidequest workstream 2 (user directive 2026-07-06, extends T-1088 scope family): build the clothing ENGINE and a basic set now, park variety for later. Target bar: RimWorld''s original apparel breadth — varied enough to not feel limiting. Goal experience: being able to pick and SHOP for clothes — colorize + slap logos on casualwear. Basic set: t-shirt (logo-capable), hoodie (logo-capable), button-down shirt, shorts, jeans, formal pants, jacket, colorable uniform, black suit (moved up so officials are includable). Skipped by explicit call: capes, dresses/skirts (parked — difficulty tier). Engine deliverables: (1) multi-region tint masks — requires Quaternius Source tier files (USD5 Patreon month, one-time download, CC0 retained — user purchase action); (2) logo decal region on garment chest UV — flat 2D artwork per D-244, diegetic hook into economy generated brands (wiki/economics/corporations/generated_brands.toml) for brand-logo shirts; (3) manifest + CharacterVisualDescriptor category coverage (slots/tints already generic); (4) batch-fit each authored garment to all 11 body types via the validated Sprint-28 Blender Surface Deform pipeline (tooling/blender_*.py). Authoring routes per garment TBD by feasibility pass: derive from owned Fantasy pack meshes (62 parts, e.g. tunic->tshirt sleeve cut, pants->shorts hem cut) vs hand-author low-poly on the 65-bone rig (Garment Tool). Related: T-1088 locomotion sandbox doubles as the garment-under-animation QA rig (cycle outfits through Walk/Sprint/Crouch_Fwd).
Correction (2026-07-06): Source tier is PARTIALLY owned already — docs/assets/downloads/ (gitignored vendor archives, present in both checkouts) holds Universal Base Characters[Source] (630MB: full .blend files for all 6 base bodies incl. FullBody + OnlyHead variants, hairstyle .blends origin-at-0 + rigged-to-head-bone, texture masters, Godot engine project) plus UBC[Standard], UAL 1+2[Standard], Modular Character Outfits Fantasy[Standard]. NOT owned: Fantasy Outfits[Source] — the garment recolor masks + named vertex groups. Consequence: authoring NEW garments is fully unblocked (author against source body .blends, create our own tint masks/vertex groups — no purchase needed); only pack-DERIVED garments'' clean masks would want one more USD5 Patreon month, decision deferred until the feasibility route table says how much derivation we actually use.
Art direction (user, 2026-07-06): garments read CYBERPUNK/MODERN — near-future everyday civilian + corporate wear — NOT fantasy/medieval. The owned Fantasy pack is at most a topology donor (low-poly base shapes like pants/shirts are genre-neutral; genre lives in silhouette details, textures, trims, and accessories) — no medieval trim/textures may survive into shipped garments. Weight the authoring routes accordingly: texture identity matters as much as mesh. Canon hook: the wiki already ships a garment brand — thrds (wiki/star-systems/GJ-475/index.md, Braemar fiber cooperative, corridor fashion markets) — so branded clothing connects to Phase-1 lore, not only generated_brands.toml. This emphasis also feeds the Quaternius-pivot evaluation: commercial alternatives skew sci-fi/urban while Quaternius clothing is fantasy-only.
Nuance (user, 2026-07-06): the cyberpunk/modern direction is the DEFAULT, not a prohibition — fashion in the Reach is cyclical, so fantasy/medieval-flavored garments may appear here and there as deliberate fashion statements (subculture pieces, luxury/revival trims, statement outerwear). Supersedes the previous note''s hard ''no medieval trim may survive'' line. Practical consequence: a small number of Fantasy-pack pieces may ship near-as-is where they read as intentional fashion; the basic set itself stays modern.
Art direction CONSOLIDATED (user, 2026-07-06 — supersedes the two notes above): the Reach has no single clothing style — ALL styles coexist, with modern/cyberpunk probably the most prevalent. Style is a prevalence DISTRIBUTION, not a genre rule, and may eventually vary by culture/settlement (fits the simulation DNA; fashion-as-cultural-data is a later phase concern, not this ticket). Consequences now: (1) the engine is style-agnostic by construction (slots/tints/logos carry no genre); (2) the basic set targets the prevalent center — modern casual + corporate — because it dresses the most NPCs and the shopping experience; (3) garments get a lightweight style tag in the manifest (e.g. style: modern|formal|fantasy|utility) as the cheap seam so per-culture distributions can be authored later without retrofitting; (4) Fantasy-pack pieces are legitimate catalogue members under their own tag, prevalence-weighted low.
Scope pin (user, 2026-07-06, final word on style): for simplicity, THIS ticket ships ONE style only — cyberpunk/modern. The pluralistic distribution model above remains the long-term truth (keep the manifest style tag as the seam), but the v1 basic set is single-style; other styles are catalogue growth, not scope.
Route challenge (user, 2026-07-06): not sold on staying Quaternius-primary — ''Quaternius is limited and will move the wall at too slow a pace; I''d rather pick something that has what we need now.'' Fresh store verification (2026-07-06): Sidekick packs are one-time USD199.99, perpetual, engine-agnostic license (Godot legal, unsupported); NO raw FBX — extraction from unitypackage (community-proven: unitypackage_godot, third-party Sidekick Creator Godot plugin incl. body blend shapes); ''Sold out'' labels are a broken-theme artifact per Shopify JSON (needs one browser cart check); SyntyPass rejected (assets stop being developable after cancel). Relevant packs: Starter (FREE, 57+91 parts), Modern Civilians (165 parts = v1 basic set territory), Sci-Fi Civilians (103 parts, cyberpunk accents), Modern Police (uniforms/officials). DECISION NOW GATED ON THE FREE SPIKE: user downloads free Starter Pack -> Bridge-C spike (extract garment -> Robust Weight Transfer onto our rig -> 11-body batch -> animation QA in T-1088 sandbox). Pass -> buy Modern Civilians, Synty = primary garment supply (+UAL1/UAL2 Source ~USD30 for clips — no Synty substitute exists for Godot animations; Fantasy Outfits Source drops to low priority under the single-style pin). Fail -> hand-author route stands. Stay-vs-pivot governance record HELD until spike evidence lands.
Scope guard (user concern, 2026-07-06): Synty intake must NOT displace the create-stuff-yourself pipeline — the user''s goal includes in-house garment creation capability, not just swapping vendor catalogs. Resolution: three-route portfolio sharing one engine — (a) Synty intake = variety bootstrap only; (b) derivation scripting (cuts/merges/retexture, agent-automatable) = create-yourself lite, turns any donor topology into original designs; (c) TRUE AUTHORING on owned UBC Source body .blends = the pipeline proper. GUARANTEE: the colorable uniform and black suit (no donor exists in any pack) are built via route (c) as the authoring pipeline''s end-to-end proof — non-negotiable deliverable of this ticket, so the pipeline exists even if Synty covers everything else. Spike status: extraction done inline by lead (158 FBX, sci-fi civilian theme confirmed); DECISIVE finding — Sidekick rig is UE-Mannequin-named, 52/65 bone-name overlap with our rig, sample garment binds to 12 vgroups of which 11 match ours exactly (only head->Head case rename) => transplant likely rename-and-rebind, far cheaper than the weight-transfer the pivot eval assumed; body blend shapes travel in the FBX. Transplant phase parked pending API recovery / token-window reset.
ROUTE DECISION (user, 2026-07-06, final): Synty intake REJECTED despite the spike''s technical PASS — (1) USD199.99/pack is too expensive; (2) Sidekick modularity is baked body-segment swap (clothes fused to body chunks), incompatible with our layered clothing-over-body system (11 bodies, skin tones, slot swapping); (3) user dislikes the Synty style. The spike still paid: transplant/batch scripts are source-agnostic and become the G1-family production pipeline; T-1090 (5 bodies broken bare) discovered; the Synty question is closed with evidence, not parked. OPERATIVE ROUTE: (b) derive from owned Fantasy parts + (c) hand-author — with a new preferred authoring technique: OFFSET-SHELL FROM OUR OWN BODY MESHES (extract body segment surface, offset outward into garment shell, cut neckline/hem/sleeves; weights inherited by construction, style fully ours, USD0). Suit + uniform remain the full hand-author proof. Purchases still open (separate concerns): UAL1/UAL2 Source ~USD30 for animation clips; Fantasy Outfits Source USD20 low-priority convenience.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-06 11:09:40.351', NULL, 'aec99c98e0291eb224f12840fdeb7c7d', 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 ('06FKCER95EK266H8RWPW8XT1F0', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', '3D character locomotion sidequest — composited character walks the Gauntlet on the live server', 'Fable-5-window sidequest (sanctioned cascade exception, 2026-07-05): the presentation layer that turns discrete server tile steps into a believable walking 3D character. Scope: 3D sandbox dev scene (SR_LIVE=1, greybox tiles derived from server snapshots, no hand-authored map), CharacterVisual instantiated in-world, locomotion rig (tile-step + facing-octant interpolation, stance-aware speeds), UAL animation state machine (idle/walk/sprint/careful/crouch), D-148 camera rig (30deg low-angle follow, 45deg rotation). Server-authoritative from day one — no client-local movement model. Builds on D-148/D-149/D-244/D-066 and the T-417 stance system. If the Fable 5 window closes mid-flight, work parks here.
Visual reference (user, 2026-07-06): Xenonauts 2 nails the target tiles/occlusion/character perspective — readable tile grid, low-angle tactical camera with angle options (matches D-148 30deg low-angle + 45deg rotation steps and the spikes/3dpipeline angle presets), and crucially its wall/roof occlusion treatment: camera-facing walls cut down/ghosted so interiors stay readable, floors peel per z-level. One deliberate difference: our camera locks to the player character (D-015/T-116) — no free map panning. Implication for the locomotion sandbox: greybox walls should prototype camera-facing cutaway occlusion early, since wall readability interacts with the locked low-angle camera.
Scoping call (user + lead agreement, 2026-07-06): wall cutaway is CLIENT-SIDE presentation, decoupled from fog-of-perception. Rationale: two occlusion systems with different subjects — perception/fog governs what the CHARACTER knows (server-enforced info boundary; client only receives observer-filtered geometry), cutaway governs what the PLAYER''S CAMERA sees (pure render mode on wall data the client already holds). They compose: cutaway cuts render height; fog overlay still gates what is visible behind. Walls remain first-class client data objects (future destruction/explosion interactions), cutaway is a render mode on them like tint/LOD. Nuance parked for Phase 5 proper: destroyed-while-unobserved walls should render in remembered state (fog-memory applied to geometry). If this holds through implementation, mint a D-record (perception domain) when the sidequest lands.
Animation supply resolved (2026-07-06): user purchased UAL1[Source+Pro] (120 clips, identical lists) + UAL2[Source] (134 clips), unzipped in docs/assets/downloads/. Gains: Turn90_L/R + Turn180_L/R (turn-in-place), Sprint_Enter/Exit + Crouch_Enter/Exit (stance transitions), full 8-direction Walk/Jog/Crouch sets incl. diagonals, Counter_Idle + talking/phone/sitting idles (life-sim verbs), plus _RM root-motion variants (unused — in-place is correct for server-authoritative interpolation). Still absent everywhere: walk start/stop + stairs — the only remaining custom clips. Integration is NOT a drop-in swap: upstream renamed clips (Walk_Loop -> Walk_Fwd_Loop etc.), so wire via the design''s recorded plan — explicit library names + lib/Clip addressing + gait-table update. NOTE for facing design: the 8-dir walk sets unlock Q-084''s parked walk-vs-aim split (direction-matched clips relative to facing) — revisit during the S9 tuning session.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:01:06.475', '2026-07-06 11:17:25.652', NULL, 'f5d3ba9fab6f37a621d71047b25cc5a9', 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 ('06FKCPBF41RWK9PW9M22Q7961C', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Clothing catalogue engine + basic set — recolorable, logo-capable everyday garments', 'Fable-5-window sidequest workstream 2 (user directive 2026-07-06, extends T-1088 scope family): build the clothing ENGINE and a basic set now, park variety for later. Target bar: RimWorld''s original apparel breadth — varied enough to not feel limiting. Goal experience: being able to pick and SHOP for clothes — colorize + slap logos on casualwear. Basic set: t-shirt (logo-capable), hoodie (logo-capable), button-down shirt, shorts, jeans, formal pants, jacket, colorable uniform, black suit (moved up so officials are includable). Skipped by explicit call: capes, dresses/skirts (parked — difficulty tier). Engine deliverables: (1) multi-region tint masks — requires Quaternius Source tier files (USD5 Patreon month, one-time download, CC0 retained — user purchase action); (2) logo decal region on garment chest UV — flat 2D artwork per D-244, diegetic hook into economy generated brands (wiki/economics/corporations/generated_brands.toml) for brand-logo shirts; (3) manifest + CharacterVisualDescriptor category coverage (slots/tints already generic); (4) batch-fit each authored garment to all 11 body types via the validated Sprint-28 Blender Surface Deform pipeline (tooling/blender_*.py). Authoring routes per garment TBD by feasibility pass: derive from owned Fantasy pack meshes (62 parts, e.g. tunic->tshirt sleeve cut, pants->shorts hem cut) vs hand-author low-poly on the 65-bone rig (Garment Tool). Related: T-1088 locomotion sandbox doubles as the garment-under-animation QA rig (cycle outfits through Walk/Sprint/Crouch_Fwd).
Correction (2026-07-06): Source tier is PARTIALLY owned already — docs/assets/downloads/ (gitignored vendor archives, present in both checkouts) holds Universal Base Characters[Source] (630MB: full .blend files for all 6 base bodies incl. FullBody + OnlyHead variants, hairstyle .blends origin-at-0 + rigged-to-head-bone, texture masters, Godot engine project) plus UBC[Standard], UAL 1+2[Standard], Modular Character Outfits Fantasy[Standard]. NOT owned: Fantasy Outfits[Source] — the garment recolor masks + named vertex groups. Consequence: authoring NEW garments is fully unblocked (author against source body .blends, create our own tint masks/vertex groups — no purchase needed); only pack-DERIVED garments'' clean masks would want one more USD5 Patreon month, decision deferred until the feasibility route table says how much derivation we actually use.
Art direction (user, 2026-07-06): garments read CYBERPUNK/MODERN — near-future everyday civilian + corporate wear — NOT fantasy/medieval. The owned Fantasy pack is at most a topology donor (low-poly base shapes like pants/shirts are genre-neutral; genre lives in silhouette details, textures, trims, and accessories) — no medieval trim/textures may survive into shipped garments. Weight the authoring routes accordingly: texture identity matters as much as mesh. Canon hook: the wiki already ships a garment brand — thrds (wiki/star-systems/GJ-475/index.md, Braemar fiber cooperative, corridor fashion markets) — so branded clothing connects to Phase-1 lore, not only generated_brands.toml. This emphasis also feeds the Quaternius-pivot evaluation: commercial alternatives skew sci-fi/urban while Quaternius clothing is fantasy-only.
Nuance (user, 2026-07-06): the cyberpunk/modern direction is the DEFAULT, not a prohibition — fashion in the Reach is cyclical, so fantasy/medieval-flavored garments may appear here and there as deliberate fashion statements (subculture pieces, luxury/revival trims, statement outerwear). Supersedes the previous note''s hard ''no medieval trim may survive'' line. Practical consequence: a small number of Fantasy-pack pieces may ship near-as-is where they read as intentional fashion; the basic set itself stays modern.
Art direction CONSOLIDATED (user, 2026-07-06 — supersedes the two notes above): the Reach has no single clothing style — ALL styles coexist, with modern/cyberpunk probably the most prevalent. Style is a prevalence DISTRIBUTION, not a genre rule, and may eventually vary by culture/settlement (fits the simulation DNA; fashion-as-cultural-data is a later phase concern, not this ticket). Consequences now: (1) the engine is style-agnostic by construction (slots/tints/logos carry no genre); (2) the basic set targets the prevalent center — modern casual + corporate — because it dresses the most NPCs and the shopping experience; (3) garments get a lightweight style tag in the manifest (e.g. style: modern|formal|fantasy|utility) as the cheap seam so per-culture distributions can be authored later without retrofitting; (4) Fantasy-pack pieces are legitimate catalogue members under their own tag, prevalence-weighted low.
Scope pin (user, 2026-07-06, final word on style): for simplicity, THIS ticket ships ONE style only — cyberpunk/modern. The pluralistic distribution model above remains the long-term truth (keep the manifest style tag as the seam), but the v1 basic set is single-style; other styles are catalogue growth, not scope.
Route challenge (user, 2026-07-06): not sold on staying Quaternius-primary — ''Quaternius is limited and will move the wall at too slow a pace; I''d rather pick something that has what we need now.'' Fresh store verification (2026-07-06): Sidekick packs are one-time USD199.99, perpetual, engine-agnostic license (Godot legal, unsupported); NO raw FBX — extraction from unitypackage (community-proven: unitypackage_godot, third-party Sidekick Creator Godot plugin incl. body blend shapes); ''Sold out'' labels are a broken-theme artifact per Shopify JSON (needs one browser cart check); SyntyPass rejected (assets stop being developable after cancel). Relevant packs: Starter (FREE, 57+91 parts), Modern Civilians (165 parts = v1 basic set territory), Sci-Fi Civilians (103 parts, cyberpunk accents), Modern Police (uniforms/officials). DECISION NOW GATED ON THE FREE SPIKE: user downloads free Starter Pack -> Bridge-C spike (extract garment -> Robust Weight Transfer onto our rig -> 11-body batch -> animation QA in T-1088 sandbox). Pass -> buy Modern Civilians, Synty = primary garment supply (+UAL1/UAL2 Source ~USD30 for clips — no Synty substitute exists for Godot animations; Fantasy Outfits Source drops to low priority under the single-style pin). Fail -> hand-author route stands. Stay-vs-pivot governance record HELD until spike evidence lands.
Scope guard (user concern, 2026-07-06): Synty intake must NOT displace the create-stuff-yourself pipeline — the user''s goal includes in-house garment creation capability, not just swapping vendor catalogs. Resolution: three-route portfolio sharing one engine — (a) Synty intake = variety bootstrap only; (b) derivation scripting (cuts/merges/retexture, agent-automatable) = create-yourself lite, turns any donor topology into original designs; (c) TRUE AUTHORING on owned UBC Source body .blends = the pipeline proper. GUARANTEE: the colorable uniform and black suit (no donor exists in any pack) are built via route (c) as the authoring pipeline''s end-to-end proof — non-negotiable deliverable of this ticket, so the pipeline exists even if Synty covers everything else. Spike status: extraction done inline by lead (158 FBX, sci-fi civilian theme confirmed); DECISIVE finding — Sidekick rig is UE-Mannequin-named, 52/65 bone-name overlap with our rig, sample garment binds to 12 vgroups of which 11 match ours exactly (only head->Head case rename) => transplant likely rename-and-rebind, far cheaper than the weight-transfer the pivot eval assumed; body blend shapes travel in the FBX. Transplant phase parked pending API recovery / token-window reset.
ROUTE DECISION (user, 2026-07-06, final): Synty intake REJECTED despite the spike''s technical PASS — (1) USD199.99/pack is too expensive; (2) Sidekick modularity is baked body-segment swap (clothes fused to body chunks), incompatible with our layered clothing-over-body system (11 bodies, skin tones, slot swapping); (3) user dislikes the Synty style. The spike still paid: transplant/batch scripts are source-agnostic and become the G1-family production pipeline; T-1090 (5 bodies broken bare) discovered; the Synty question is closed with evidence, not parked. OPERATIVE ROUTE: (b) derive from owned Fantasy parts + (c) hand-author — with a new preferred authoring technique: OFFSET-SHELL FROM OUR OWN BODY MESHES (extract body segment surface, offset outward into garment shell, cut neckline/hem/sleeves; weights inherited by construction, style fully ours, USD0). Suit + uniform remain the full hand-author proof. Purchases still open (separate concerns): UAL1/UAL2 Source ~USD30 for animation clips; Fantasy Outfits Source USD20 low-priority convenience.
Chromakey garment-QA harness DELIVERED (2026-07-06, commits eaca6c8c4/74fa16260/b09a50efb): two-pass depth-proximity clip detection — pass A keys covered body segments magenta, pass B re-renders garment-only with a depth-only 3cm epsilon bias toward camera; analyzer splits key pixels into exposed_skin (informational — sleeveless arms, collars) vs clip_through (gating — skin within 3cm in front of cloth). Run: tooling/garment-qa/run-garment-qa <config>. Verified on the peasant set (avg_m/f x Walk/Sprint/Crouch_Fwd x 3 frames x 4 yaws): 0/18 front-view false positives; genuine findings = sleeveless armhole seam (avg_f, sides), deep-crouch waist gap (worst 150px, back), collar nape. First real fit observations already on record for the peasant set. Sensitivity: clip_epsilon_m + --min-pixels, to calibrate against the first modern garments. This is the acceptance gate for every garment the wardrobe pipeline produces; pair with the T-1088 sandbox for in-world walk QA.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-06 12:59:45.213', NULL, 'ffdb04fb1abf542f041161529a4a2f89', 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 ('06FKCER95EK266H8RWPW8XT1F0', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', '3D character locomotion sidequest — composited character walks the Gauntlet on the live server', 'Fable-5-window sidequest (sanctioned cascade exception, 2026-07-05): the presentation layer that turns discrete server tile steps into a believable walking 3D character. Scope: 3D sandbox dev scene (SR_LIVE=1, greybox tiles derived from server snapshots, no hand-authored map), CharacterVisual instantiated in-world, locomotion rig (tile-step + facing-octant interpolation, stance-aware speeds), UAL animation state machine (idle/walk/sprint/careful/crouch), D-148 camera rig (30deg low-angle follow, 45deg rotation). Server-authoritative from day one — no client-local movement model. Builds on D-148/D-149/D-244/D-066 and the T-417 stance system. If the Fable 5 window closes mid-flight, work parks here.
Visual reference (user, 2026-07-06): Xenonauts 2 nails the target tiles/occlusion/character perspective — readable tile grid, low-angle tactical camera with angle options (matches D-148 30deg low-angle + 45deg rotation steps and the spikes/3dpipeline angle presets), and crucially its wall/roof occlusion treatment: camera-facing walls cut down/ghosted so interiors stay readable, floors peel per z-level. One deliberate difference: our camera locks to the player character (D-015/T-116) — no free map panning. Implication for the locomotion sandbox: greybox walls should prototype camera-facing cutaway occlusion early, since wall readability interacts with the locked low-angle camera.
Scoping call (user + lead agreement, 2026-07-06): wall cutaway is CLIENT-SIDE presentation, decoupled from fog-of-perception. Rationale: two occlusion systems with different subjects — perception/fog governs what the CHARACTER knows (server-enforced info boundary; client only receives observer-filtered geometry), cutaway governs what the PLAYER''S CAMERA sees (pure render mode on wall data the client already holds). They compose: cutaway cuts render height; fog overlay still gates what is visible behind. Walls remain first-class client data objects (future destruction/explosion interactions), cutaway is a render mode on them like tint/LOD. Nuance parked for Phase 5 proper: destroyed-while-unobserved walls should render in remembered state (fog-memory applied to geometry). If this holds through implementation, mint a D-record (perception domain) when the sidequest lands.
Animation supply resolved (2026-07-06): user purchased UAL1[Source+Pro] (120 clips, identical lists) + UAL2[Source] (134 clips), unzipped in docs/assets/downloads/. Gains: Turn90_L/R + Turn180_L/R (turn-in-place), Sprint_Enter/Exit + Crouch_Enter/Exit (stance transitions), full 8-direction Walk/Jog/Crouch sets incl. diagonals, Counter_Idle + talking/phone/sitting idles (life-sim verbs), plus _RM root-motion variants (unused — in-place is correct for server-authoritative interpolation). Still absent everywhere: walk start/stop + stairs — the only remaining custom clips. Integration is NOT a drop-in swap: upstream renamed clips (Walk_Loop -> Walk_Fwd_Loop etc.), so wire via the design''s recorded plan — explicit library names + lib/Clip addressing + gait-table update. NOTE for facing design: the 8-dir walk sets unlock Q-084''s parked walk-vs-aim split (direction-matched clips relative to facing) — revisit during the S9 tuning session.
Live-session feedback (user, 2026-07-06): (1) Corridor cutaway verdict — two minds: ''extremely trippy, could be the game''s own look'' vs ''player cannot see what is in the room unless close''. Resolution: cutaway MODES, toggled live in the sandbox (corridor / bottom-walls-low / both) — bottom-wall rule is data-driven (wall drops to stub when a KNOWN floor tile sits on its camera-far side; camera-facing directions derived from WorldRoot basis at runtime, not hand-derived). Default picked by feel during the session; the trippy corridor stays available as a signature-look option. (2) NEW: mouse-over move-here marker on the hovered tile + optimal path line — client-side A* over the never-evict KNOWN tile store (info-boundary-honest: the character plans only through known tiles; fog is unpathable), 8-dir with diagonal step cost 1 (time-optimal per the no-sqrt2 wire), terrain-cost provider seam (uniform today, Phase-4 terrain later), click executes by streaming ordinary Move* step commands through the existing throttle (zero protocol change, server validates every step), cancelled by WASD override / path invalidation / arrival. Settles more of Q-084 (click-to-move half).', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:01:06.475', '2026-07-06 17:12:09.007', NULL, '4286ff0868e7ab0aaafbaf2d183a6975', 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 ('06FKCER95EK266H8RWPW8XT1F0', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', '3D character locomotion sidequest — composited character walks the Gauntlet on the live server', 'Fable-5-window sidequest (sanctioned cascade exception, 2026-07-05): the presentation layer that turns discrete server tile steps into a believable walking 3D character. Scope: 3D sandbox dev scene (SR_LIVE=1, greybox tiles derived from server snapshots, no hand-authored map), CharacterVisual instantiated in-world, locomotion rig (tile-step + facing-octant interpolation, stance-aware speeds), UAL animation state machine (idle/walk/sprint/careful/crouch), D-148 camera rig (30deg low-angle follow, 45deg rotation). Server-authoritative from day one — no client-local movement model. Builds on D-148/D-149/D-244/D-066 and the T-417 stance system. If the Fable 5 window closes mid-flight, work parks here.
Visual reference (user, 2026-07-06): Xenonauts 2 nails the target tiles/occlusion/character perspective — readable tile grid, low-angle tactical camera with angle options (matches D-148 30deg low-angle + 45deg rotation steps and the spikes/3dpipeline angle presets), and crucially its wall/roof occlusion treatment: camera-facing walls cut down/ghosted so interiors stay readable, floors peel per z-level. One deliberate difference: our camera locks to the player character (D-015/T-116) — no free map panning. Implication for the locomotion sandbox: greybox walls should prototype camera-facing cutaway occlusion early, since wall readability interacts with the locked low-angle camera.
Scoping call (user + lead agreement, 2026-07-06): wall cutaway is CLIENT-SIDE presentation, decoupled from fog-of-perception. Rationale: two occlusion systems with different subjects — perception/fog governs what the CHARACTER knows (server-enforced info boundary; client only receives observer-filtered geometry), cutaway governs what the PLAYER''S CAMERA sees (pure render mode on wall data the client already holds). They compose: cutaway cuts render height; fog overlay still gates what is visible behind. Walls remain first-class client data objects (future destruction/explosion interactions), cutaway is a render mode on them like tint/LOD. Nuance parked for Phase 5 proper: destroyed-while-unobserved walls should render in remembered state (fog-memory applied to geometry). If this holds through implementation, mint a D-record (perception domain) when the sidequest lands.
Animation supply resolved (2026-07-06): user purchased UAL1[Source+Pro] (120 clips, identical lists) + UAL2[Source] (134 clips), unzipped in docs/assets/downloads/. Gains: Turn90_L/R + Turn180_L/R (turn-in-place), Sprint_Enter/Exit + Crouch_Enter/Exit (stance transitions), full 8-direction Walk/Jog/Crouch sets incl. diagonals, Counter_Idle + talking/phone/sitting idles (life-sim verbs), plus _RM root-motion variants (unused — in-place is correct for server-authoritative interpolation). Still absent everywhere: walk start/stop + stairs — the only remaining custom clips. Integration is NOT a drop-in swap: upstream renamed clips (Walk_Loop -> Walk_Fwd_Loop etc.), so wire via the design''s recorded plan — explicit library names + lib/Clip addressing + gait-table update. NOTE for facing design: the 8-dir walk sets unlock Q-084''s parked walk-vs-aim split (direction-matched clips relative to facing) — revisit during the S9 tuning session.
Live-session feedback (user, 2026-07-06): (1) Corridor cutaway verdict — two minds: ''extremely trippy, could be the game''s own look'' vs ''player cannot see what is in the room unless close''. Resolution: cutaway MODES, toggled live in the sandbox (corridor / bottom-walls-low / both) — bottom-wall rule is data-driven (wall drops to stub when a KNOWN floor tile sits on its camera-far side; camera-facing directions derived from WorldRoot basis at runtime, not hand-derived). Default picked by feel during the session; the trippy corridor stays available as a signature-look option. (2) NEW: mouse-over move-here marker on the hovered tile + optimal path line — client-side A* over the never-evict KNOWN tile store (info-boundary-honest: the character plans only through known tiles; fog is unpathable), 8-dir with diagonal step cost 1 (time-optimal per the no-sqrt2 wire), terrain-cost provider seam (uniform today, Phase-4 terrain later), click executes by streaming ordinary Move* step commands through the existing throttle (zero protocol change, server validates every step), cancelled by WASD override / path invalidation / arrival. Settles more of Q-084 (click-to-move half).
RMB gesture vocabulary (user, live session 2026-07-06, added to the path-preview feature): single right-click = walk there (current stance); DOUBLE right-click = sprint there (ladder ToggleStanceUp burst to Sprint via the ordinary command queue, pre-sprint stance RESTORED on arrival; a double upgrades the already-started follow in place); LONG-PRESS right-click (~0.4s, commit on release) = ''take cover at location'', prototyped as go-there-then-Crouch-on-arrival (no restore — crouching was the point). Real cover mechanics (wall adjacency, directional protection) are future combat design; the gesture is the input-vocabulary prototype. WASD always cancels any follow without stance restore (player override is deliberate).', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:01:06.475', '2026-07-06 17:37:13.480', NULL, '6a60bee830c86a4c53c735ec17087499', 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 ('06FKH8SJRMXTP6XQNBXHRD4CQ0', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'test_fog_shader perf assertions flake under machine load — make budget load-tolerant', 'test_visibility_texture_update_performance asserts wall-clock < 0.5ms and failed at 0.704ms during a pre-push while a live game session + capture harness shared the machine (2026-07-06); 46/46 green re-run in isolation. Fix options: median-of-N timing, a generous multiplier, or gating perf assertions behind an env var so functional coverage always runs but timing only gates on a quiet machine.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-06 18:14:07.301', '2026-07-06 18:14:07.301', NULL, '1a1f4e07550e0c8834184ffec0816848', 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 ('06FKCER95EK266H8RWPW8XT1F0', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', '3D character locomotion sidequest — composited character walks the Gauntlet on the live server', 'Fable-5-window sidequest (sanctioned cascade exception, 2026-07-05): the presentation layer that turns discrete server tile steps into a believable walking 3D character. Scope: 3D sandbox dev scene (SR_LIVE=1, greybox tiles derived from server snapshots, no hand-authored map), CharacterVisual instantiated in-world, locomotion rig (tile-step + facing-octant interpolation, stance-aware speeds), UAL animation state machine (idle/walk/sprint/careful/crouch), D-148 camera rig (30deg low-angle follow, 45deg rotation). Server-authoritative from day one — no client-local movement model. Builds on D-148/D-149/D-244/D-066 and the T-417 stance system. If the Fable 5 window closes mid-flight, work parks here.
Visual reference (user, 2026-07-06): Xenonauts 2 nails the target tiles/occlusion/character perspective — readable tile grid, low-angle tactical camera with angle options (matches D-148 30deg low-angle + 45deg rotation steps and the spikes/3dpipeline angle presets), and crucially its wall/roof occlusion treatment: camera-facing walls cut down/ghosted so interiors stay readable, floors peel per z-level. One deliberate difference: our camera locks to the player character (D-015/T-116) — no free map panning. Implication for the locomotion sandbox: greybox walls should prototype camera-facing cutaway occlusion early, since wall readability interacts with the locked low-angle camera.
Scoping call (user + lead agreement, 2026-07-06): wall cutaway is CLIENT-SIDE presentation, decoupled from fog-of-perception. Rationale: two occlusion systems with different subjects — perception/fog governs what the CHARACTER knows (server-enforced info boundary; client only receives observer-filtered geometry), cutaway governs what the PLAYER''S CAMERA sees (pure render mode on wall data the client already holds). They compose: cutaway cuts render height; fog overlay still gates what is visible behind. Walls remain first-class client data objects (future destruction/explosion interactions), cutaway is a render mode on them like tint/LOD. Nuance parked for Phase 5 proper: destroyed-while-unobserved walls should render in remembered state (fog-memory applied to geometry). If this holds through implementation, mint a D-record (perception domain) when the sidequest lands.
Animation supply resolved (2026-07-06): user purchased UAL1[Source+Pro] (120 clips, identical lists) + UAL2[Source] (134 clips), unzipped in docs/assets/downloads/. Gains: Turn90_L/R + Turn180_L/R (turn-in-place), Sprint_Enter/Exit + Crouch_Enter/Exit (stance transitions), full 8-direction Walk/Jog/Crouch sets incl. diagonals, Counter_Idle + talking/phone/sitting idles (life-sim verbs), plus _RM root-motion variants (unused — in-place is correct for server-authoritative interpolation). Still absent everywhere: walk start/stop + stairs — the only remaining custom clips. Integration is NOT a drop-in swap: upstream renamed clips (Walk_Loop -> Walk_Fwd_Loop etc.), so wire via the design''s recorded plan — explicit library names + lib/Clip addressing + gait-table update. NOTE for facing design: the 8-dir walk sets unlock Q-084''s parked walk-vs-aim split (direction-matched clips relative to facing) — revisit during the S9 tuning session.
Live-session feedback (user, 2026-07-06): (1) Corridor cutaway verdict — two minds: ''extremely trippy, could be the game''s own look'' vs ''player cannot see what is in the room unless close''. Resolution: cutaway MODES, toggled live in the sandbox (corridor / bottom-walls-low / both) — bottom-wall rule is data-driven (wall drops to stub when a KNOWN floor tile sits on its camera-far side; camera-facing directions derived from WorldRoot basis at runtime, not hand-derived). Default picked by feel during the session; the trippy corridor stays available as a signature-look option. (2) NEW: mouse-over move-here marker on the hovered tile + optimal path line — client-side A* over the never-evict KNOWN tile store (info-boundary-honest: the character plans only through known tiles; fog is unpathable), 8-dir with diagonal step cost 1 (time-optimal per the no-sqrt2 wire), terrain-cost provider seam (uniform today, Phase-4 terrain later), click executes by streaming ordinary Move* step commands through the existing throttle (zero protocol change, server validates every step), cancelled by WASD override / path invalidation / arrival. Settles more of Q-084 (click-to-move half).
RMB gesture vocabulary (user, live session 2026-07-06, added to the path-preview feature): single right-click = walk there (current stance); DOUBLE right-click = sprint there (ladder ToggleStanceUp burst to Sprint via the ordinary command queue, pre-sprint stance RESTORED on arrival; a double upgrades the already-started follow in place); LONG-PRESS right-click (~0.4s, commit on release) = ''take cover at location'', prototyped as go-there-then-Crouch-on-arrival (no restore — crouching was the point). Real cover mechanics (wall adjacency, directional protection) are future combat design; the gesture is the input-vocabulary prototype. WASD always cancels any follow without stance restore (player override is deliberate).
Follow-facing refinement (user, live session 2026-07-06): during an RMB path-follow the BODY commits to the leg/movement direction (mouse no longer swings body yaw); the mouse instead drives a LAYERED LOOK-AT — Head bone (~70deg limit) plus torso twist via a spine bone (~30deg) for looking far lateral/behind; beyond their sum the character physically cannot look further without turning (correct). Pure client presentation on the D-249 model; SetFacing continues to ride the wire during follows, so the SERVER vision cone still follows the mouse (diegetic: the character looks where the player points while walking a committed path). Implementation: Godot LookAtModifier3D pair under the CharacterVisual skeleton, influence-faded in/out on follow start/end; rig gains a commit_body_to_motion flag (yaw from leg velocity while set).', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:01:06.475', '2026-07-06 18:29:55.012', NULL, 'c45dd9ede3dbadcc20b4ce12b90a94f5', 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 ('06FKHFRCP5R0VBA9BKR97RRBJ0', 'task', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Server aim-lock: explicit SetFacing should survive accepted moves (vision-cone flap during path-follows)', 'Found live in the T-1088 sandbox: during a client path-follow the vision cone flaps to path-forward on every accepted step because movement.rs facing_from_delta overwrites Facing on each move, and D-054''s change-gated SetFacing never re-asserts an unchanged octant. Client mitigation shipped (InputMapper.reassert_facing ~2 ticks after each emitted step) shrinks the flap to ~100ms per step but cannot eliminate it — movement wins within a tick. Proper fix is server-side facing decoupling (e.g. an explicit-aim flag/timestamp on Facing that accepted moves do not overwrite while fresh), which is exactly Q-084''s parked walk-vs-aim split now with play evidence. Needs a D-record before wire semantics change; touch nothing until the sidequest''s presentation-layer constraint lifts.', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-06 18:44:32.561', '2026-07-06 18:44:32.561', NULL, 'c83a2bdbf3c41b678f04db6e5077b78a', 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 ('06FKHFRCP5R0VBA9BKR97RRBJ0', 'task', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Server aim-lock: explicit SetFacing should survive accepted moves (vision-cone flap during path-follows)', 'Found live in the T-1088 sandbox: during a client path-follow the vision cone flaps to path-forward on every accepted step because movement.rs facing_from_delta overwrites Facing on each move, and D-054''s change-gated SetFacing never re-asserts an unchanged octant. Client mitigation shipped (InputMapper.reassert_facing ~2 ticks after each emitted step) shrinks the flap to ~100ms per step but cannot eliminate it — movement wins within a tick. Proper fix is server-side facing decoupling (e.g. an explicit-aim flag/timestamp on Facing that accepted moves do not overwrite while fresh), which is exactly Q-084''s parked walk-vs-aim split now with play evidence. Needs a D-record before wire semantics change; touch nothing until the sidequest''s presentation-layer constraint lifts.', 'in_progress', 'medium', NULL, 'server', NULL, '2026-07-06 18:44:32.561', '2026-07-06 18:55:29.037', NULL, '01cc8d20f141986d0d4fdfe4d6cb1f22', 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 ('06FKHFRCP5R0VBA9BKR97RRBJ0', 'task', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Server aim-lock: explicit SetFacing should survive accepted moves (vision-cone flap during path-follows)', 'Found live in the T-1088 sandbox: during a client path-follow the vision cone flaps to path-forward on every accepted step because movement.rs facing_from_delta overwrites Facing on each move, and D-054''s change-gated SetFacing never re-asserts an unchanged octant. Client mitigation shipped (InputMapper.reassert_facing ~2 ticks after each emitted step) shrinks the flap to ~100ms per step but cannot eliminate it — movement wins within a tick. Proper fix is server-side facing decoupling (e.g. an explicit-aim flag/timestamp on Facing that accepted moves do not overwrite while fresh), which is exactly Q-084''s parked walk-vs-aim split now with play evidence. Needs a D-record before wire semantics change; touch nothing until the sidequest''s presentation-layer constraint lifts.', 'done', 'medium', NULL, 'server', NULL, '2026-07-06 18:44:32.561', '2026-07-06 19:18:30.657', NULL, 'a71401dfc41971383da8d314361e15bb', 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 ('06FKE0EW2617K9VE4QHSYSR9HW', 'bug', '06FB0TNSRXX29H5X0EB0F0GBW4', '5 of 11 body types misrender bare in CharacterVisual (thin_f/m, heavy_f/m, child)', 'Found by the T-1089 Synty intake spike (2026-07-06), body-only controls with NO garment attached: thin_f/m, heavy_f/m, child misrender in CharacterVisual (exploded/spider arms, detached head/hair; child 303mm max warp, 87 collapsed edges; f-bodies bust-through). These are the newer high-poly bodies (no seg_hips, different segmentation) — exactly the 5 with no production peasant-clothing variants. Upstream of ANY clothing route; blocks T-1089''s 11-of-11 acceptance criterion and resolves part of Q-060. Evidence: spikes/synty-intake/out/bodies_fit_log.json + out/qa_bodies/ (44 screenshots) + out/bodies_recon.json.', 'in_progress', 'high', NULL, 'client', NULL, '2026-07-06 10:38:16.593', '2026-07-06 19:25:35.248', NULL, 'ec4a20d81cb872c936cd825d4ebca616', 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 ('06FKE0EW2617K9VE4QHSYSR9HW', 'bug', '06FB0TNSRXX29H5X0EB0F0GBW4', '5 of 11 body types misrender bare in CharacterVisual (thin_f/m, heavy_f/m, child)', 'Found by the T-1089 Synty intake spike (2026-07-06), body-only controls with NO garment attached: thin_f/m, heavy_f/m, child misrender in CharacterVisual (exploded/spider arms, detached head/hair; child 303mm max warp, 87 collapsed edges; f-bodies bust-through). These are the newer high-poly bodies (no seg_hips, different segmentation) — exactly the 5 with no production peasant-clothing variants. Upstream of ANY clothing route; blocks T-1089''s 11-of-11 acceptance criterion and resolves part of Q-060. Evidence: spikes/synty-intake/out/bodies_fit_log.json + out/qa_bodies/ (44 screenshots) + out/bodies_recon.json.
RESOLVED (pending review). Root cause: fork bodies (thin/heavy/child) had a mesh<->armature mismatch. blender_segment_body.py apply_scale scaled mesh vertices ONLY, leaving the embedded armature at source scale; since character_visual.gd binds every segment onto ONE shared skeleton (skeleton/armature.glb) by bone name, each segment must be internally consistent with its own embedded armature — the mesh-only scale dropped the head ~0.35m below the Head bone and flung limbs apart on relocation. thin/heavy were additionally stale high-poly artifacts (torso 1419-1704v vs 375v) missing seg_hips entirely. Fix: replaced apply_scale with apply_fork_scale (tooling/blender_segment_body.py) which scales mesh AND armature together and BAKES via Blender transform_apply (edit-bone poking alone shears connected chains — arms/fingers still exploded). Rebuilt all 5 via new tooling/blender_rebuild_forks.py from Source-tier gltf; now 19 segments incl seg_hips, low-poly matching the healthy six. Bare-body QA (idle+walk, front+side, real CharacterVisual): all 5 coherent, head/hair attached, no explosion; healthy 6 untouched. Q-060: all 3 peasant garments Surface-Deform-fit onto all 5 forks (15/15 surface_deform, 0 shrinkwrap fallback) — Surface Deform HOLDS at the extremes on repaired bodies. No manifest change (body ids/paths unchanged).', 'in_progress', 'high', NULL, 'client', NULL, '2026-07-06 10:38:16.593', '2026-07-06 20:02:30.990', NULL, 'faba67bd9fa9be117a752e07459c5814', 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 ('06FKE0EW2617K9VE4QHSYSR9HW', 'bug', '06FB0TNSRXX29H5X0EB0F0GBW4', '5 of 11 body types misrender bare in CharacterVisual (thin_f/m, heavy_f/m, child)', 'Found by the T-1089 Synty intake spike (2026-07-06), body-only controls with NO garment attached: thin_f/m, heavy_f/m, child misrender in CharacterVisual (exploded/spider arms, detached head/hair; child 303mm max warp, 87 collapsed edges; f-bodies bust-through). These are the newer high-poly bodies (no seg_hips, different segmentation) — exactly the 5 with no production peasant-clothing variants. Upstream of ANY clothing route; blocks T-1089''s 11-of-11 acceptance criterion and resolves part of Q-060. Evidence: spikes/synty-intake/out/bodies_fit_log.json + out/qa_bodies/ (44 screenshots) + out/bodies_recon.json.
RESOLVED (pending review). Root cause: fork bodies (thin/heavy/child) had a mesh<->armature mismatch. blender_segment_body.py apply_scale scaled mesh vertices ONLY, leaving the embedded armature at source scale; since character_visual.gd binds every segment onto ONE shared skeleton (skeleton/armature.glb) by bone name, each segment must be internally consistent with its own embedded armature — the mesh-only scale dropped the head ~0.35m below the Head bone and flung limbs apart on relocation. thin/heavy were additionally stale high-poly artifacts (torso 1419-1704v vs 375v) missing seg_hips entirely. Fix: replaced apply_scale with apply_fork_scale (tooling/blender_segment_body.py) which scales mesh AND armature together and BAKES via Blender transform_apply (edit-bone poking alone shears connected chains — arms/fingers still exploded). Rebuilt all 5 via new tooling/blender_rebuild_forks.py from Source-tier gltf; now 19 segments incl seg_hips, low-poly matching the healthy six. Bare-body QA (idle+walk, front+side, real CharacterVisual): all 5 coherent, head/hair attached, no explosion; healthy 6 untouched. Q-060: all 3 peasant garments Surface-Deform-fit onto all 5 forks (15/15 surface_deform, 0 shrinkwrap fallback) — Surface Deform HOLDS at the extremes on repaired bodies. No manifest change (body ids/paths unchanged).', 'review', 'high', NULL, 'client', NULL, '2026-07-06 10:38:16.593', '2026-07-06 20:02:31.012', NULL, '5cec3f5395cb96910151ae43d10076dc', 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 ('06FKJ25KG3ADM4M5XRXCVR2GQG', 'task', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Child/teen bodies composite at adult height — shared-skeleton normalization discards stature', 'Found during T-1090 repair (2026-07-06): the shared-skeleton compositor (character_visual.gd binds every segment to the one Regular-sized armature by bone name) normalizes global scale — only cross-sectional shape survives, so thin reads narrow and heavy reads wide (correct) but child and teen stand at ADULT height. Pre-existing behavior, not a T-1090 regression. If stature matters (it will — children in crowds), options: per-body runtime skeleton, node-level scale on CharacterVisual, or bone-length retarget at load. Interacts with animation retargeting (shorter limbs vs shared UAL clips) and D-164. Sprint-28 verdict warns runtime BONE scaling fails — evaluate NODE scale first.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-06 20:04:59.392', '2026-07-06 20:04:59.392', NULL, '7ceb0fb9f90b36988d9d2b75765c9585', 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 ('06FKE0EW2617K9VE4QHSYSR9HW', 'bug', '06FB0TNSRXX29H5X0EB0F0GBW4', '5 of 11 body types misrender bare in CharacterVisual (thin_f/m, heavy_f/m, child)', 'Found by the T-1089 Synty intake spike (2026-07-06), body-only controls with NO garment attached: thin_f/m, heavy_f/m, child misrender in CharacterVisual (exploded/spider arms, detached head/hair; child 303mm max warp, 87 collapsed edges; f-bodies bust-through). These are the newer high-poly bodies (no seg_hips, different segmentation) — exactly the 5 with no production peasant-clothing variants. Upstream of ANY clothing route; blocks T-1089''s 11-of-11 acceptance criterion and resolves part of Q-060. Evidence: spikes/synty-intake/out/bodies_fit_log.json + out/qa_bodies/ (44 screenshots) + out/bodies_recon.json.
RESOLVED (pending review). Root cause: fork bodies (thin/heavy/child) had a mesh<->armature mismatch. blender_segment_body.py apply_scale scaled mesh vertices ONLY, leaving the embedded armature at source scale; since character_visual.gd binds every segment onto ONE shared skeleton (skeleton/armature.glb) by bone name, each segment must be internally consistent with its own embedded armature — the mesh-only scale dropped the head ~0.35m below the Head bone and flung limbs apart on relocation. thin/heavy were additionally stale high-poly artifacts (torso 1419-1704v vs 375v) missing seg_hips entirely. Fix: replaced apply_scale with apply_fork_scale (tooling/blender_segment_body.py) which scales mesh AND armature together and BAKES via Blender transform_apply (edit-bone poking alone shears connected chains — arms/fingers still exploded). Rebuilt all 5 via new tooling/blender_rebuild_forks.py from Source-tier gltf; now 19 segments incl seg_hips, low-poly matching the healthy six. Bare-body QA (idle+walk, front+side, real CharacterVisual): all 5 coherent, head/hair attached, no explosion; healthy 6 untouched. Q-060: all 3 peasant garments Surface-Deform-fit onto all 5 forks (15/15 surface_deform, 0 shrinkwrap fallback) — Surface Deform HOLDS at the extremes on repaired bodies. No manifest change (body ids/paths unchanged).', 'done', 'high', NULL, 'client', NULL, '2026-07-06 10:38:16.593', '2026-07-06 20:06:22.078', NULL, 'dd9ef4733867c6166098027e49a44569', 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 ('06FKCPBF41RWK9PW9M22Q7961C', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Clothing catalogue engine + basic set — recolorable, logo-capable everyday garments', 'Fable-5-window sidequest workstream 2 (user directive 2026-07-06, extends T-1088 scope family): build the clothing ENGINE and a basic set now, park variety for later. Target bar: RimWorld''s original apparel breadth — varied enough to not feel limiting. Goal experience: being able to pick and SHOP for clothes — colorize + slap logos on casualwear. Basic set: t-shirt (logo-capable), hoodie (logo-capable), button-down shirt, shorts, jeans, formal pants, jacket, colorable uniform, black suit (moved up so officials are includable). Skipped by explicit call: capes, dresses/skirts (parked — difficulty tier). Engine deliverables: (1) multi-region tint masks — requires Quaternius Source tier files (USD5 Patreon month, one-time download, CC0 retained — user purchase action); (2) logo decal region on garment chest UV — flat 2D artwork per D-244, diegetic hook into economy generated brands (wiki/economics/corporations/generated_brands.toml) for brand-logo shirts; (3) manifest + CharacterVisualDescriptor category coverage (slots/tints already generic); (4) batch-fit each authored garment to all 11 body types via the validated Sprint-28 Blender Surface Deform pipeline (tooling/blender_*.py). Authoring routes per garment TBD by feasibility pass: derive from owned Fantasy pack meshes (62 parts, e.g. tunic->tshirt sleeve cut, pants->shorts hem cut) vs hand-author low-poly on the 65-bone rig (Garment Tool). Related: T-1088 locomotion sandbox doubles as the garment-under-animation QA rig (cycle outfits through Walk/Sprint/Crouch_Fwd).
Correction (2026-07-06): Source tier is PARTIALLY owned already — docs/assets/downloads/ (gitignored vendor archives, present in both checkouts) holds Universal Base Characters[Source] (630MB: full .blend files for all 6 base bodies incl. FullBody + OnlyHead variants, hairstyle .blends origin-at-0 + rigged-to-head-bone, texture masters, Godot engine project) plus UBC[Standard], UAL 1+2[Standard], Modular Character Outfits Fantasy[Standard]. NOT owned: Fantasy Outfits[Source] — the garment recolor masks + named vertex groups. Consequence: authoring NEW garments is fully unblocked (author against source body .blends, create our own tint masks/vertex groups — no purchase needed); only pack-DERIVED garments'' clean masks would want one more USD5 Patreon month, decision deferred until the feasibility route table says how much derivation we actually use.
Art direction (user, 2026-07-06): garments read CYBERPUNK/MODERN — near-future everyday civilian + corporate wear — NOT fantasy/medieval. The owned Fantasy pack is at most a topology donor (low-poly base shapes like pants/shirts are genre-neutral; genre lives in silhouette details, textures, trims, and accessories) — no medieval trim/textures may survive into shipped garments. Weight the authoring routes accordingly: texture identity matters as much as mesh. Canon hook: the wiki already ships a garment brand — thrds (wiki/star-systems/GJ-475/index.md, Braemar fiber cooperative, corridor fashion markets) — so branded clothing connects to Phase-1 lore, not only generated_brands.toml. This emphasis also feeds the Quaternius-pivot evaluation: commercial alternatives skew sci-fi/urban while Quaternius clothing is fantasy-only.
Nuance (user, 2026-07-06): the cyberpunk/modern direction is the DEFAULT, not a prohibition — fashion in the Reach is cyclical, so fantasy/medieval-flavored garments may appear here and there as deliberate fashion statements (subculture pieces, luxury/revival trims, statement outerwear). Supersedes the previous note''s hard ''no medieval trim may survive'' line. Practical consequence: a small number of Fantasy-pack pieces may ship near-as-is where they read as intentional fashion; the basic set itself stays modern.
Art direction CONSOLIDATED (user, 2026-07-06 — supersedes the two notes above): the Reach has no single clothing style — ALL styles coexist, with modern/cyberpunk probably the most prevalent. Style is a prevalence DISTRIBUTION, not a genre rule, and may eventually vary by culture/settlement (fits the simulation DNA; fashion-as-cultural-data is a later phase concern, not this ticket). Consequences now: (1) the engine is style-agnostic by construction (slots/tints/logos carry no genre); (2) the basic set targets the prevalent center — modern casual + corporate — because it dresses the most NPCs and the shopping experience; (3) garments get a lightweight style tag in the manifest (e.g. style: modern|formal|fantasy|utility) as the cheap seam so per-culture distributions can be authored later without retrofitting; (4) Fantasy-pack pieces are legitimate catalogue members under their own tag, prevalence-weighted low.
Scope pin (user, 2026-07-06, final word on style): for simplicity, THIS ticket ships ONE style only — cyberpunk/modern. The pluralistic distribution model above remains the long-term truth (keep the manifest style tag as the seam), but the v1 basic set is single-style; other styles are catalogue growth, not scope.
Route challenge (user, 2026-07-06): not sold on staying Quaternius-primary — ''Quaternius is limited and will move the wall at too slow a pace; I''d rather pick something that has what we need now.'' Fresh store verification (2026-07-06): Sidekick packs are one-time USD199.99, perpetual, engine-agnostic license (Godot legal, unsupported); NO raw FBX — extraction from unitypackage (community-proven: unitypackage_godot, third-party Sidekick Creator Godot plugin incl. body blend shapes); ''Sold out'' labels are a broken-theme artifact per Shopify JSON (needs one browser cart check); SyntyPass rejected (assets stop being developable after cancel). Relevant packs: Starter (FREE, 57+91 parts), Modern Civilians (165 parts = v1 basic set territory), Sci-Fi Civilians (103 parts, cyberpunk accents), Modern Police (uniforms/officials). DECISION NOW GATED ON THE FREE SPIKE: user downloads free Starter Pack -> Bridge-C spike (extract garment -> Robust Weight Transfer onto our rig -> 11-body batch -> animation QA in T-1088 sandbox). Pass -> buy Modern Civilians, Synty = primary garment supply (+UAL1/UAL2 Source ~USD30 for clips — no Synty substitute exists for Godot animations; Fantasy Outfits Source drops to low priority under the single-style pin). Fail -> hand-author route stands. Stay-vs-pivot governance record HELD until spike evidence lands.
Scope guard (user concern, 2026-07-06): Synty intake must NOT displace the create-stuff-yourself pipeline — the user''s goal includes in-house garment creation capability, not just swapping vendor catalogs. Resolution: three-route portfolio sharing one engine — (a) Synty intake = variety bootstrap only; (b) derivation scripting (cuts/merges/retexture, agent-automatable) = create-yourself lite, turns any donor topology into original designs; (c) TRUE AUTHORING on owned UBC Source body .blends = the pipeline proper. GUARANTEE: the colorable uniform and black suit (no donor exists in any pack) are built via route (c) as the authoring pipeline''s end-to-end proof — non-negotiable deliverable of this ticket, so the pipeline exists even if Synty covers everything else. Spike status: extraction done inline by lead (158 FBX, sci-fi civilian theme confirmed); DECISIVE finding — Sidekick rig is UE-Mannequin-named, 52/65 bone-name overlap with our rig, sample garment binds to 12 vgroups of which 11 match ours exactly (only head->Head case rename) => transplant likely rename-and-rebind, far cheaper than the weight-transfer the pivot eval assumed; body blend shapes travel in the FBX. Transplant phase parked pending API recovery / token-window reset.
ROUTE DECISION (user, 2026-07-06, final): Synty intake REJECTED despite the spike''s technical PASS — (1) USD199.99/pack is too expensive; (2) Sidekick modularity is baked body-segment swap (clothes fused to body chunks), incompatible with our layered clothing-over-body system (11 bodies, skin tones, slot swapping); (3) user dislikes the Synty style. The spike still paid: transplant/batch scripts are source-agnostic and become the G1-family production pipeline; T-1090 (5 bodies broken bare) discovered; the Synty question is closed with evidence, not parked. OPERATIVE ROUTE: (b) derive from owned Fantasy parts + (c) hand-author — with a new preferred authoring technique: OFFSET-SHELL FROM OUR OWN BODY MESHES (extract body segment surface, offset outward into garment shell, cut neckline/hem/sleeves; weights inherited by construction, style fully ours, USD0). Suit + uniform remain the full hand-author proof. Purchases still open (separate concerns): UAL1/UAL2 Source ~USD30 for animation clips; Fantasy Outfits Source USD20 low-priority convenience.
Chromakey garment-QA harness DELIVERED (2026-07-06, commits eaca6c8c4/74fa16260/b09a50efb): two-pass depth-proximity clip detection — pass A keys covered body segments magenta, pass B re-renders garment-only with a depth-only 3cm epsilon bias toward camera; analyzer splits key pixels into exposed_skin (informational — sleeveless arms, collars) vs clip_through (gating — skin within 3cm in front of cloth). Run: tooling/garment-qa/run-garment-qa <config>. Verified on the peasant set (avg_m/f x Walk/Sprint/Crouch_Fwd x 3 frames x 4 yaws): 0/18 front-view false positives; genuine findings = sleeveless armhole seam (avg_f, sides), deep-crouch waist gap (worst 150px, back), collar nape. First real fit observations already on record for the peasant set. Sensitivity: clip_epsilon_m + --min-pixels, to calibrate against the first modern garments. This is the acceptance gate for every garment the wardrobe pipeline produces; pair with the T-1088 sandbox for in-world walk QA.
ENGINE DELIVERED (2026-07-06, commits e22ea0fa4 + 278e8b7c2; earlier eaca6c8c4/74fa16260/b09a50efb chromakey, b54b8189d bodies): G1 skinned batch-fit (blender_batch_fit_skinned.py), offset-shell authoring (blender_author_offset_shell.py — route c create-yourself, weights by construction, procedural RGBA masks, UV2 chest channel), toon_garment.gdshader (channel-blended 4-region tint + logo after-tint/before-toon), compositor plumb (multi_region coverage flag -> garment shader; descriptor clothing_logos: per-character brand choice), thrds logo stub, manifest style tag + logo_capable. PROOF: tshirt_modern on 6 healthy bodies, 18-assertion suite green, 216-capture chromakey QA. ROUTE GUIDANCE from QA evidence (Q-060): single-reference SD-fit of offset-shells degrades with girth divergence (muscular_m worst 859px clip at 24mm standoff; reference body clean) — offset-shell garments should be authored PER BODY (needs per-body masks + runtime <body>_mask.png loading, small change); SD-fit remains right for derived/hand-authored garments sharing one mask (suit, uniform, Fantasy cuts). REMAINING for this ticket: refit tshirt to the 5 repaired forks (T-1090 done — add to --only and rerun), per-body shell mode, then the basic set: hoodie, button-down, shorts, jeans, formal pants, jacket + the suit/uniform hand-author proof; creation-screen try-on UI for tints/logos.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-06 20:22:08.752', NULL, 'a8211523c2bef72893601e640205216e', 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 ('06FKCPBF41RWK9PW9M22Q7961C', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Clothing catalogue engine + basic set — recolorable, logo-capable everyday garments', 'Fable-5-window sidequest workstream 2 (user directive 2026-07-06, extends T-1088 scope family): build the clothing ENGINE and a basic set now, park variety for later. Target bar: RimWorld''s original apparel breadth — varied enough to not feel limiting. Goal experience: being able to pick and SHOP for clothes — colorize + slap logos on casualwear. Basic set: t-shirt (logo-capable), hoodie (logo-capable), button-down shirt, shorts, jeans, formal pants, jacket, colorable uniform, black suit (moved up so officials are includable). Skipped by explicit call: capes, dresses/skirts (parked — difficulty tier). Engine deliverables: (1) multi-region tint masks — requires Quaternius Source tier files (USD5 Patreon month, one-time download, CC0 retained — user purchase action); (2) logo decal region on garment chest UV — flat 2D artwork per D-244, diegetic hook into economy generated brands (wiki/economics/corporations/generated_brands.toml) for brand-logo shirts; (3) manifest + CharacterVisualDescriptor category coverage (slots/tints already generic); (4) batch-fit each authored garment to all 11 body types via the validated Sprint-28 Blender Surface Deform pipeline (tooling/blender_*.py). Authoring routes per garment TBD by feasibility pass: derive from owned Fantasy pack meshes (62 parts, e.g. tunic->tshirt sleeve cut, pants->shorts hem cut) vs hand-author low-poly on the 65-bone rig (Garment Tool). Related: T-1088 locomotion sandbox doubles as the garment-under-animation QA rig (cycle outfits through Walk/Sprint/Crouch_Fwd).
Correction (2026-07-06): Source tier is PARTIALLY owned already — docs/assets/downloads/ (gitignored vendor archives, present in both checkouts) holds Universal Base Characters[Source] (630MB: full .blend files for all 6 base bodies incl. FullBody + OnlyHead variants, hairstyle .blends origin-at-0 + rigged-to-head-bone, texture masters, Godot engine project) plus UBC[Standard], UAL 1+2[Standard], Modular Character Outfits Fantasy[Standard]. NOT owned: Fantasy Outfits[Source] — the garment recolor masks + named vertex groups. Consequence: authoring NEW garments is fully unblocked (author against source body .blends, create our own tint masks/vertex groups — no purchase needed); only pack-DERIVED garments'' clean masks would want one more USD5 Patreon month, decision deferred until the feasibility route table says how much derivation we actually use.
Art direction (user, 2026-07-06): garments read CYBERPUNK/MODERN — near-future everyday civilian + corporate wear — NOT fantasy/medieval. The owned Fantasy pack is at most a topology donor (low-poly base shapes like pants/shirts are genre-neutral; genre lives in silhouette details, textures, trims, and accessories) — no medieval trim/textures may survive into shipped garments. Weight the authoring routes accordingly: texture identity matters as much as mesh. Canon hook: the wiki already ships a garment brand — thrds (wiki/star-systems/GJ-475/index.md, Braemar fiber cooperative, corridor fashion markets) — so branded clothing connects to Phase-1 lore, not only generated_brands.toml. This emphasis also feeds the Quaternius-pivot evaluation: commercial alternatives skew sci-fi/urban while Quaternius clothing is fantasy-only.
Nuance (user, 2026-07-06): the cyberpunk/modern direction is the DEFAULT, not a prohibition — fashion in the Reach is cyclical, so fantasy/medieval-flavored garments may appear here and there as deliberate fashion statements (subculture pieces, luxury/revival trims, statement outerwear). Supersedes the previous note''s hard ''no medieval trim may survive'' line. Practical consequence: a small number of Fantasy-pack pieces may ship near-as-is where they read as intentional fashion; the basic set itself stays modern.
Art direction CONSOLIDATED (user, 2026-07-06 — supersedes the two notes above): the Reach has no single clothing style — ALL styles coexist, with modern/cyberpunk probably the most prevalent. Style is a prevalence DISTRIBUTION, not a genre rule, and may eventually vary by culture/settlement (fits the simulation DNA; fashion-as-cultural-data is a later phase concern, not this ticket). Consequences now: (1) the engine is style-agnostic by construction (slots/tints/logos carry no genre); (2) the basic set targets the prevalent center — modern casual + corporate — because it dresses the most NPCs and the shopping experience; (3) garments get a lightweight style tag in the manifest (e.g. style: modern|formal|fantasy|utility) as the cheap seam so per-culture distributions can be authored later without retrofitting; (4) Fantasy-pack pieces are legitimate catalogue members under their own tag, prevalence-weighted low.
Scope pin (user, 2026-07-06, final word on style): for simplicity, THIS ticket ships ONE style only — cyberpunk/modern. The pluralistic distribution model above remains the long-term truth (keep the manifest style tag as the seam), but the v1 basic set is single-style; other styles are catalogue growth, not scope.
Route challenge (user, 2026-07-06): not sold on staying Quaternius-primary — ''Quaternius is limited and will move the wall at too slow a pace; I''d rather pick something that has what we need now.'' Fresh store verification (2026-07-06): Sidekick packs are one-time USD199.99, perpetual, engine-agnostic license (Godot legal, unsupported); NO raw FBX — extraction from unitypackage (community-proven: unitypackage_godot, third-party Sidekick Creator Godot plugin incl. body blend shapes); ''Sold out'' labels are a broken-theme artifact per Shopify JSON (needs one browser cart check); SyntyPass rejected (assets stop being developable after cancel). Relevant packs: Starter (FREE, 57+91 parts), Modern Civilians (165 parts = v1 basic set territory), Sci-Fi Civilians (103 parts, cyberpunk accents), Modern Police (uniforms/officials). DECISION NOW GATED ON THE FREE SPIKE: user downloads free Starter Pack -> Bridge-C spike (extract garment -> Robust Weight Transfer onto our rig -> 11-body batch -> animation QA in T-1088 sandbox). Pass -> buy Modern Civilians, Synty = primary garment supply (+UAL1/UAL2 Source ~USD30 for clips — no Synty substitute exists for Godot animations; Fantasy Outfits Source drops to low priority under the single-style pin). Fail -> hand-author route stands. Stay-vs-pivot governance record HELD until spike evidence lands.
Scope guard (user concern, 2026-07-06): Synty intake must NOT displace the create-stuff-yourself pipeline — the user''s goal includes in-house garment creation capability, not just swapping vendor catalogs. Resolution: three-route portfolio sharing one engine — (a) Synty intake = variety bootstrap only; (b) derivation scripting (cuts/merges/retexture, agent-automatable) = create-yourself lite, turns any donor topology into original designs; (c) TRUE AUTHORING on owned UBC Source body .blends = the pipeline proper. GUARANTEE: the colorable uniform and black suit (no donor exists in any pack) are built via route (c) as the authoring pipeline''s end-to-end proof — non-negotiable deliverable of this ticket, so the pipeline exists even if Synty covers everything else. Spike status: extraction done inline by lead (158 FBX, sci-fi civilian theme confirmed); DECISIVE finding — Sidekick rig is UE-Mannequin-named, 52/65 bone-name overlap with our rig, sample garment binds to 12 vgroups of which 11 match ours exactly (only head->Head case rename) => transplant likely rename-and-rebind, far cheaper than the weight-transfer the pivot eval assumed; body blend shapes travel in the FBX. Transplant phase parked pending API recovery / token-window reset.
ROUTE DECISION (user, 2026-07-06, final): Synty intake REJECTED despite the spike''s technical PASS — (1) USD199.99/pack is too expensive; (2) Sidekick modularity is baked body-segment swap (clothes fused to body chunks), incompatible with our layered clothing-over-body system (11 bodies, skin tones, slot swapping); (3) user dislikes the Synty style. The spike still paid: transplant/batch scripts are source-agnostic and become the G1-family production pipeline; T-1090 (5 bodies broken bare) discovered; the Synty question is closed with evidence, not parked. OPERATIVE ROUTE: (b) derive from owned Fantasy parts + (c) hand-author — with a new preferred authoring technique: OFFSET-SHELL FROM OUR OWN BODY MESHES (extract body segment surface, offset outward into garment shell, cut neckline/hem/sleeves; weights inherited by construction, style fully ours, USD0). Suit + uniform remain the full hand-author proof. Purchases still open (separate concerns): UAL1/UAL2 Source ~USD30 for animation clips; Fantasy Outfits Source USD20 low-priority convenience.
Chromakey garment-QA harness DELIVERED (2026-07-06, commits eaca6c8c4/74fa16260/b09a50efb): two-pass depth-proximity clip detection — pass A keys covered body segments magenta, pass B re-renders garment-only with a depth-only 3cm epsilon bias toward camera; analyzer splits key pixels into exposed_skin (informational — sleeveless arms, collars) vs clip_through (gating — skin within 3cm in front of cloth). Run: tooling/garment-qa/run-garment-qa <config>. Verified on the peasant set (avg_m/f x Walk/Sprint/Crouch_Fwd x 3 frames x 4 yaws): 0/18 front-view false positives; genuine findings = sleeveless armhole seam (avg_f, sides), deep-crouch waist gap (worst 150px, back), collar nape. First real fit observations already on record for the peasant set. Sensitivity: clip_epsilon_m + --min-pixels, to calibrate against the first modern garments. This is the acceptance gate for every garment the wardrobe pipeline produces; pair with the T-1088 sandbox for in-world walk QA.
ENGINE DELIVERED (2026-07-06, commits e22ea0fa4 + 278e8b7c2; earlier eaca6c8c4/74fa16260/b09a50efb chromakey, b54b8189d bodies): G1 skinned batch-fit (blender_batch_fit_skinned.py), offset-shell authoring (blender_author_offset_shell.py — route c create-yourself, weights by construction, procedural RGBA masks, UV2 chest channel), toon_garment.gdshader (channel-blended 4-region tint + logo after-tint/before-toon), compositor plumb (multi_region coverage flag -> garment shader; descriptor clothing_logos: per-character brand choice), thrds logo stub, manifest style tag + logo_capable. PROOF: tshirt_modern on 6 healthy bodies, 18-assertion suite green, 216-capture chromakey QA. ROUTE GUIDANCE from QA evidence (Q-060): single-reference SD-fit of offset-shells degrades with girth divergence (muscular_m worst 859px clip at 24mm standoff; reference body clean) — offset-shell garments should be authored PER BODY (needs per-body masks + runtime <body>_mask.png loading, small change); SD-fit remains right for derived/hand-authored garments sharing one mask (suit, uniform, Fantasy cuts). REMAINING for this ticket: refit tshirt to the 5 repaired forks (T-1090 done — add to --only and rerun), per-body shell mode, then the basic set: hoodie, button-down, shorts, jeans, formal pants, jacket + the suit/uniform hand-author proof; creation-screen try-on UI for tints/logos.
Basic-set EXPANSION (user, 2026-07-06 bedtime directive: ''full set of the basics across the board''): wave 2 after the in-flight wave 1 — FOOTWEAR: sneakers_modern (trainers, everyday default), shoes_formal (suit pairing), boots_modern (ankle height); SPORT: tank_top (sleeveless, logo-capable), joggers_modern (cuffed, track-stripe tint region), track_jacket (with joggers = the training suit, logo-capable); SWIM: swim_trunks (bright), swimsuit_onepiece (color-block regions). Foot garments follow the peasant_shoes both-feet convention; sole geometry as an offset-shell param extension. Total modern catalogue after wave 2: 17 garments across tops/bottoms/feet/full-body x casual/formal/sport/swim. DEFERRED deliberately: hats/accessories — accessory slot path is unexercised and T-721 flags a filter bug; needs daylight, not a 3am debug. Overnight sequence: wave 1 -> wave 2 -> full garment x 11-body fitting matrix (the ''T-1090 fittings'' check, fork bodies especially) -> commits -> gated pushes -> morning report.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-06 21:19:48.839', NULL, '9da9b8e46990fd320bbaf9897b77620b', 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 ('06FKCPBF41RWK9PW9M22Q7961C', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Clothing catalogue engine + basic set — recolorable, logo-capable everyday garments', 'Fable-5-window sidequest workstream 2 (user directive 2026-07-06, extends T-1088 scope family): build the clothing ENGINE and a basic set now, park variety for later. Target bar: RimWorld''s original apparel breadth — varied enough to not feel limiting. Goal experience: being able to pick and SHOP for clothes — colorize + slap logos on casualwear. Basic set: t-shirt (logo-capable), hoodie (logo-capable), button-down shirt, shorts, jeans, formal pants, jacket, colorable uniform, black suit (moved up so officials are includable). Skipped by explicit call: capes, dresses/skirts (parked — difficulty tier). Engine deliverables: (1) multi-region tint masks — requires Quaternius Source tier files (USD5 Patreon month, one-time download, CC0 retained — user purchase action); (2) logo decal region on garment chest UV — flat 2D artwork per D-244, diegetic hook into economy generated brands (wiki/economics/corporations/generated_brands.toml) for brand-logo shirts; (3) manifest + CharacterVisualDescriptor category coverage (slots/tints already generic); (4) batch-fit each authored garment to all 11 body types via the validated Sprint-28 Blender Surface Deform pipeline (tooling/blender_*.py). Authoring routes per garment TBD by feasibility pass: derive from owned Fantasy pack meshes (62 parts, e.g. tunic->tshirt sleeve cut, pants->shorts hem cut) vs hand-author low-poly on the 65-bone rig (Garment Tool). Related: T-1088 locomotion sandbox doubles as the garment-under-animation QA rig (cycle outfits through Walk/Sprint/Crouch_Fwd).
Correction (2026-07-06): Source tier is PARTIALLY owned already — docs/assets/downloads/ (gitignored vendor archives, present in both checkouts) holds Universal Base Characters[Source] (630MB: full .blend files for all 6 base bodies incl. FullBody + OnlyHead variants, hairstyle .blends origin-at-0 + rigged-to-head-bone, texture masters, Godot engine project) plus UBC[Standard], UAL 1+2[Standard], Modular Character Outfits Fantasy[Standard]. NOT owned: Fantasy Outfits[Source] — the garment recolor masks + named vertex groups. Consequence: authoring NEW garments is fully unblocked (author against source body .blends, create our own tint masks/vertex groups — no purchase needed); only pack-DERIVED garments'' clean masks would want one more USD5 Patreon month, decision deferred until the feasibility route table says how much derivation we actually use.
Art direction (user, 2026-07-06): garments read CYBERPUNK/MODERN — near-future everyday civilian + corporate wear — NOT fantasy/medieval. The owned Fantasy pack is at most a topology donor (low-poly base shapes like pants/shirts are genre-neutral; genre lives in silhouette details, textures, trims, and accessories) — no medieval trim/textures may survive into shipped garments. Weight the authoring routes accordingly: texture identity matters as much as mesh. Canon hook: the wiki already ships a garment brand — thrds (wiki/star-systems/GJ-475/index.md, Braemar fiber cooperative, corridor fashion markets) — so branded clothing connects to Phase-1 lore, not only generated_brands.toml. This emphasis also feeds the Quaternius-pivot evaluation: commercial alternatives skew sci-fi/urban while Quaternius clothing is fantasy-only.
Nuance (user, 2026-07-06): the cyberpunk/modern direction is the DEFAULT, not a prohibition — fashion in the Reach is cyclical, so fantasy/medieval-flavored garments may appear here and there as deliberate fashion statements (subculture pieces, luxury/revival trims, statement outerwear). Supersedes the previous note''s hard ''no medieval trim may survive'' line. Practical consequence: a small number of Fantasy-pack pieces may ship near-as-is where they read as intentional fashion; the basic set itself stays modern.
Art direction CONSOLIDATED (user, 2026-07-06 — supersedes the two notes above): the Reach has no single clothing style — ALL styles coexist, with modern/cyberpunk probably the most prevalent. Style is a prevalence DISTRIBUTION, not a genre rule, and may eventually vary by culture/settlement (fits the simulation DNA; fashion-as-cultural-data is a later phase concern, not this ticket). Consequences now: (1) the engine is style-agnostic by construction (slots/tints/logos carry no genre); (2) the basic set targets the prevalent center — modern casual + corporate — because it dresses the most NPCs and the shopping experience; (3) garments get a lightweight style tag in the manifest (e.g. style: modern|formal|fantasy|utility) as the cheap seam so per-culture distributions can be authored later without retrofitting; (4) Fantasy-pack pieces are legitimate catalogue members under their own tag, prevalence-weighted low.
Scope pin (user, 2026-07-06, final word on style): for simplicity, THIS ticket ships ONE style only — cyberpunk/modern. The pluralistic distribution model above remains the long-term truth (keep the manifest style tag as the seam), but the v1 basic set is single-style; other styles are catalogue growth, not scope.
Route challenge (user, 2026-07-06): not sold on staying Quaternius-primary — ''Quaternius is limited and will move the wall at too slow a pace; I''d rather pick something that has what we need now.'' Fresh store verification (2026-07-06): Sidekick packs are one-time USD199.99, perpetual, engine-agnostic license (Godot legal, unsupported); NO raw FBX — extraction from unitypackage (community-proven: unitypackage_godot, third-party Sidekick Creator Godot plugin incl. body blend shapes); ''Sold out'' labels are a broken-theme artifact per Shopify JSON (needs one browser cart check); SyntyPass rejected (assets stop being developable after cancel). Relevant packs: Starter (FREE, 57+91 parts), Modern Civilians (165 parts = v1 basic set territory), Sci-Fi Civilians (103 parts, cyberpunk accents), Modern Police (uniforms/officials). DECISION NOW GATED ON THE FREE SPIKE: user downloads free Starter Pack -> Bridge-C spike (extract garment -> Robust Weight Transfer onto our rig -> 11-body batch -> animation QA in T-1088 sandbox). Pass -> buy Modern Civilians, Synty = primary garment supply (+UAL1/UAL2 Source ~USD30 for clips — no Synty substitute exists for Godot animations; Fantasy Outfits Source drops to low priority under the single-style pin). Fail -> hand-author route stands. Stay-vs-pivot governance record HELD until spike evidence lands.
Scope guard (user concern, 2026-07-06): Synty intake must NOT displace the create-stuff-yourself pipeline — the user''s goal includes in-house garment creation capability, not just swapping vendor catalogs. Resolution: three-route portfolio sharing one engine — (a) Synty intake = variety bootstrap only; (b) derivation scripting (cuts/merges/retexture, agent-automatable) = create-yourself lite, turns any donor topology into original designs; (c) TRUE AUTHORING on owned UBC Source body .blends = the pipeline proper. GUARANTEE: the colorable uniform and black suit (no donor exists in any pack) are built via route (c) as the authoring pipeline''s end-to-end proof — non-negotiable deliverable of this ticket, so the pipeline exists even if Synty covers everything else. Spike status: extraction done inline by lead (158 FBX, sci-fi civilian theme confirmed); DECISIVE finding — Sidekick rig is UE-Mannequin-named, 52/65 bone-name overlap with our rig, sample garment binds to 12 vgroups of which 11 match ours exactly (only head->Head case rename) => transplant likely rename-and-rebind, far cheaper than the weight-transfer the pivot eval assumed; body blend shapes travel in the FBX. Transplant phase parked pending API recovery / token-window reset.
ROUTE DECISION (user, 2026-07-06, final): Synty intake REJECTED despite the spike''s technical PASS — (1) USD199.99/pack is too expensive; (2) Sidekick modularity is baked body-segment swap (clothes fused to body chunks), incompatible with our layered clothing-over-body system (11 bodies, skin tones, slot swapping); (3) user dislikes the Synty style. The spike still paid: transplant/batch scripts are source-agnostic and become the G1-family production pipeline; T-1090 (5 bodies broken bare) discovered; the Synty question is closed with evidence, not parked. OPERATIVE ROUTE: (b) derive from owned Fantasy parts + (c) hand-author — with a new preferred authoring technique: OFFSET-SHELL FROM OUR OWN BODY MESHES (extract body segment surface, offset outward into garment shell, cut neckline/hem/sleeves; weights inherited by construction, style fully ours, USD0). Suit + uniform remain the full hand-author proof. Purchases still open (separate concerns): UAL1/UAL2 Source ~USD30 for animation clips; Fantasy Outfits Source USD20 low-priority convenience.
Chromakey garment-QA harness DELIVERED (2026-07-06, commits eaca6c8c4/74fa16260/b09a50efb): two-pass depth-proximity clip detection — pass A keys covered body segments magenta, pass B re-renders garment-only with a depth-only 3cm epsilon bias toward camera; analyzer splits key pixels into exposed_skin (informational — sleeveless arms, collars) vs clip_through (gating — skin within 3cm in front of cloth). Run: tooling/garment-qa/run-garment-qa <config>. Verified on the peasant set (avg_m/f x Walk/Sprint/Crouch_Fwd x 3 frames x 4 yaws): 0/18 front-view false positives; genuine findings = sleeveless armhole seam (avg_f, sides), deep-crouch waist gap (worst 150px, back), collar nape. First real fit observations already on record for the peasant set. Sensitivity: clip_epsilon_m + --min-pixels, to calibrate against the first modern garments. This is the acceptance gate for every garment the wardrobe pipeline produces; pair with the T-1088 sandbox for in-world walk QA.
ENGINE DELIVERED (2026-07-06, commits e22ea0fa4 + 278e8b7c2; earlier eaca6c8c4/74fa16260/b09a50efb chromakey, b54b8189d bodies): G1 skinned batch-fit (blender_batch_fit_skinned.py), offset-shell authoring (blender_author_offset_shell.py — route c create-yourself, weights by construction, procedural RGBA masks, UV2 chest channel), toon_garment.gdshader (channel-blended 4-region tint + logo after-tint/before-toon), compositor plumb (multi_region coverage flag -> garment shader; descriptor clothing_logos: per-character brand choice), thrds logo stub, manifest style tag + logo_capable. PROOF: tshirt_modern on 6 healthy bodies, 18-assertion suite green, 216-capture chromakey QA. ROUTE GUIDANCE from QA evidence (Q-060): single-reference SD-fit of offset-shells degrades with girth divergence (muscular_m worst 859px clip at 24mm standoff; reference body clean) — offset-shell garments should be authored PER BODY (needs per-body masks + runtime <body>_mask.png loading, small change); SD-fit remains right for derived/hand-authored garments sharing one mask (suit, uniform, Fantasy cuts). REMAINING for this ticket: refit tshirt to the 5 repaired forks (T-1090 done — add to --only and rerun), per-body shell mode, then the basic set: hoodie, button-down, shorts, jeans, formal pants, jacket + the suit/uniform hand-author proof; creation-screen try-on UI for tints/logos.
Basic-set EXPANSION (user, 2026-07-06 bedtime directive: ''full set of the basics across the board''): wave 2 after the in-flight wave 1 — FOOTWEAR: sneakers_modern (trainers, everyday default), shoes_formal (suit pairing), boots_modern (ankle height); SPORT: tank_top (sleeveless, logo-capable), joggers_modern (cuffed, track-stripe tint region), track_jacket (with joggers = the training suit, logo-capable); SWIM: swim_trunks (bright), swimsuit_onepiece (color-block regions). Foot garments follow the peasant_shoes both-feet convention; sole geometry as an offset-shell param extension. Total modern catalogue after wave 2: 17 garments across tops/bottoms/feet/full-body x casual/formal/sport/swim. DEFERRED deliberately: hats/accessories — accessory slot path is unexercised and T-721 flags a filter bug; needs daylight, not a 3am debug. Overnight sequence: wave 1 -> wave 2 -> full garment x 11-body fitting matrix (the ''T-1090 fittings'' check, fork bodies especially) -> commits -> gated pushes -> morning report.
Fill-in-the-blanks addendum (user bedtime directive 2 + lead judgment): wave 2 grows to 12 — adds sweater_modern (crew pullover), cargo_pants (utility/cyberpunk staple), parka_thrds (cold-weather was absent entirely; ships as the FIRST CANON-BRANDED garment — thrds is Braemar''s cold-weather fiber brand per wiki; hip-length to avoid the parked skirt-rigging tier), slides (swim footwear, near-free). PLUS outfit presets: data file of named complete looks (casual/business/sport/beach/worker/sleep as tint-palette presets over garments) authored by the lead at integration — makes the catalogue usable for NPC dressing and the shop as OUTFITS, not just items. Post-wave-2 catalogue: 21 modern pieces (tops 8 / bottoms 6 / feet 4 / full-body 2 / swim 3). Hats/accessories remain the deliberate daylight deferral (T-721).', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-06 21:20:30.325', NULL, '680076b0a42604327f9ea5701b6355c5', 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 ('06FKCPBF41RWK9PW9M22Q7961C', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Clothing catalogue engine + basic set — recolorable, logo-capable everyday garments', 'Fable-5-window sidequest workstream 2 (user directive 2026-07-06, extends T-1088 scope family): build the clothing ENGINE and a basic set now, park variety for later. Target bar: RimWorld''s original apparel breadth — varied enough to not feel limiting. Goal experience: being able to pick and SHOP for clothes — colorize + slap logos on casualwear. Basic set: t-shirt (logo-capable), hoodie (logo-capable), button-down shirt, shorts, jeans, formal pants, jacket, colorable uniform, black suit (moved up so officials are includable). Skipped by explicit call: capes, dresses/skirts (parked — difficulty tier). Engine deliverables: (1) multi-region tint masks — requires Quaternius Source tier files (USD5 Patreon month, one-time download, CC0 retained — user purchase action); (2) logo decal region on garment chest UV — flat 2D artwork per D-244, diegetic hook into economy generated brands (wiki/economics/corporations/generated_brands.toml) for brand-logo shirts; (3) manifest + CharacterVisualDescriptor category coverage (slots/tints already generic); (4) batch-fit each authored garment to all 11 body types via the validated Sprint-28 Blender Surface Deform pipeline (tooling/blender_*.py). Authoring routes per garment TBD by feasibility pass: derive from owned Fantasy pack meshes (62 parts, e.g. tunic->tshirt sleeve cut, pants->shorts hem cut) vs hand-author low-poly on the 65-bone rig (Garment Tool). Related: T-1088 locomotion sandbox doubles as the garment-under-animation QA rig (cycle outfits through Walk/Sprint/Crouch_Fwd).
Correction (2026-07-06): Source tier is PARTIALLY owned already — docs/assets/downloads/ (gitignored vendor archives, present in both checkouts) holds Universal Base Characters[Source] (630MB: full .blend files for all 6 base bodies incl. FullBody + OnlyHead variants, hairstyle .blends origin-at-0 + rigged-to-head-bone, texture masters, Godot engine project) plus UBC[Standard], UAL 1+2[Standard], Modular Character Outfits Fantasy[Standard]. NOT owned: Fantasy Outfits[Source] — the garment recolor masks + named vertex groups. Consequence: authoring NEW garments is fully unblocked (author against source body .blends, create our own tint masks/vertex groups — no purchase needed); only pack-DERIVED garments'' clean masks would want one more USD5 Patreon month, decision deferred until the feasibility route table says how much derivation we actually use.
Art direction (user, 2026-07-06): garments read CYBERPUNK/MODERN — near-future everyday civilian + corporate wear — NOT fantasy/medieval. The owned Fantasy pack is at most a topology donor (low-poly base shapes like pants/shirts are genre-neutral; genre lives in silhouette details, textures, trims, and accessories) — no medieval trim/textures may survive into shipped garments. Weight the authoring routes accordingly: texture identity matters as much as mesh. Canon hook: the wiki already ships a garment brand — thrds (wiki/star-systems/GJ-475/index.md, Braemar fiber cooperative, corridor fashion markets) — so branded clothing connects to Phase-1 lore, not only generated_brands.toml. This emphasis also feeds the Quaternius-pivot evaluation: commercial alternatives skew sci-fi/urban while Quaternius clothing is fantasy-only.
Nuance (user, 2026-07-06): the cyberpunk/modern direction is the DEFAULT, not a prohibition — fashion in the Reach is cyclical, so fantasy/medieval-flavored garments may appear here and there as deliberate fashion statements (subculture pieces, luxury/revival trims, statement outerwear). Supersedes the previous note''s hard ''no medieval trim may survive'' line. Practical consequence: a small number of Fantasy-pack pieces may ship near-as-is where they read as intentional fashion; the basic set itself stays modern.
Art direction CONSOLIDATED (user, 2026-07-06 — supersedes the two notes above): the Reach has no single clothing style — ALL styles coexist, with modern/cyberpunk probably the most prevalent. Style is a prevalence DISTRIBUTION, not a genre rule, and may eventually vary by culture/settlement (fits the simulation DNA; fashion-as-cultural-data is a later phase concern, not this ticket). Consequences now: (1) the engine is style-agnostic by construction (slots/tints/logos carry no genre); (2) the basic set targets the prevalent center — modern casual + corporate — because it dresses the most NPCs and the shopping experience; (3) garments get a lightweight style tag in the manifest (e.g. style: modern|formal|fantasy|utility) as the cheap seam so per-culture distributions can be authored later without retrofitting; (4) Fantasy-pack pieces are legitimate catalogue members under their own tag, prevalence-weighted low.
Scope pin (user, 2026-07-06, final word on style): for simplicity, THIS ticket ships ONE style only — cyberpunk/modern. The pluralistic distribution model above remains the long-term truth (keep the manifest style tag as the seam), but the v1 basic set is single-style; other styles are catalogue growth, not scope.
Route challenge (user, 2026-07-06): not sold on staying Quaternius-primary — ''Quaternius is limited and will move the wall at too slow a pace; I''d rather pick something that has what we need now.'' Fresh store verification (2026-07-06): Sidekick packs are one-time USD199.99, perpetual, engine-agnostic license (Godot legal, unsupported); NO raw FBX — extraction from unitypackage (community-proven: unitypackage_godot, third-party Sidekick Creator Godot plugin incl. body blend shapes); ''Sold out'' labels are a broken-theme artifact per Shopify JSON (needs one browser cart check); SyntyPass rejected (assets stop being developable after cancel). Relevant packs: Starter (FREE, 57+91 parts), Modern Civilians (165 parts = v1 basic set territory), Sci-Fi Civilians (103 parts, cyberpunk accents), Modern Police (uniforms/officials). DECISION NOW GATED ON THE FREE SPIKE: user downloads free Starter Pack -> Bridge-C spike (extract garment -> Robust Weight Transfer onto our rig -> 11-body batch -> animation QA in T-1088 sandbox). Pass -> buy Modern Civilians, Synty = primary garment supply (+UAL1/UAL2 Source ~USD30 for clips — no Synty substitute exists for Godot animations; Fantasy Outfits Source drops to low priority under the single-style pin). Fail -> hand-author route stands. Stay-vs-pivot governance record HELD until spike evidence lands.
Scope guard (user concern, 2026-07-06): Synty intake must NOT displace the create-stuff-yourself pipeline — the user''s goal includes in-house garment creation capability, not just swapping vendor catalogs. Resolution: three-route portfolio sharing one engine — (a) Synty intake = variety bootstrap only; (b) derivation scripting (cuts/merges/retexture, agent-automatable) = create-yourself lite, turns any donor topology into original designs; (c) TRUE AUTHORING on owned UBC Source body .blends = the pipeline proper. GUARANTEE: the colorable uniform and black suit (no donor exists in any pack) are built via route (c) as the authoring pipeline''s end-to-end proof — non-negotiable deliverable of this ticket, so the pipeline exists even if Synty covers everything else. Spike status: extraction done inline by lead (158 FBX, sci-fi civilian theme confirmed); DECISIVE finding — Sidekick rig is UE-Mannequin-named, 52/65 bone-name overlap with our rig, sample garment binds to 12 vgroups of which 11 match ours exactly (only head->Head case rename) => transplant likely rename-and-rebind, far cheaper than the weight-transfer the pivot eval assumed; body blend shapes travel in the FBX. Transplant phase parked pending API recovery / token-window reset.
ROUTE DECISION (user, 2026-07-06, final): Synty intake REJECTED despite the spike''s technical PASS — (1) USD199.99/pack is too expensive; (2) Sidekick modularity is baked body-segment swap (clothes fused to body chunks), incompatible with our layered clothing-over-body system (11 bodies, skin tones, slot swapping); (3) user dislikes the Synty style. The spike still paid: transplant/batch scripts are source-agnostic and become the G1-family production pipeline; T-1090 (5 bodies broken bare) discovered; the Synty question is closed with evidence, not parked. OPERATIVE ROUTE: (b) derive from owned Fantasy parts + (c) hand-author — with a new preferred authoring technique: OFFSET-SHELL FROM OUR OWN BODY MESHES (extract body segment surface, offset outward into garment shell, cut neckline/hem/sleeves; weights inherited by construction, style fully ours, USD0). Suit + uniform remain the full hand-author proof. Purchases still open (separate concerns): UAL1/UAL2 Source ~USD30 for animation clips; Fantasy Outfits Source USD20 low-priority convenience.
Chromakey garment-QA harness DELIVERED (2026-07-06, commits eaca6c8c4/74fa16260/b09a50efb): two-pass depth-proximity clip detection — pass A keys covered body segments magenta, pass B re-renders garment-only with a depth-only 3cm epsilon bias toward camera; analyzer splits key pixels into exposed_skin (informational — sleeveless arms, collars) vs clip_through (gating — skin within 3cm in front of cloth). Run: tooling/garment-qa/run-garment-qa <config>. Verified on the peasant set (avg_m/f x Walk/Sprint/Crouch_Fwd x 3 frames x 4 yaws): 0/18 front-view false positives; genuine findings = sleeveless armhole seam (avg_f, sides), deep-crouch waist gap (worst 150px, back), collar nape. First real fit observations already on record for the peasant set. Sensitivity: clip_epsilon_m + --min-pixels, to calibrate against the first modern garments. This is the acceptance gate for every garment the wardrobe pipeline produces; pair with the T-1088 sandbox for in-world walk QA.
ENGINE DELIVERED (2026-07-06, commits e22ea0fa4 + 278e8b7c2; earlier eaca6c8c4/74fa16260/b09a50efb chromakey, b54b8189d bodies): G1 skinned batch-fit (blender_batch_fit_skinned.py), offset-shell authoring (blender_author_offset_shell.py — route c create-yourself, weights by construction, procedural RGBA masks, UV2 chest channel), toon_garment.gdshader (channel-blended 4-region tint + logo after-tint/before-toon), compositor plumb (multi_region coverage flag -> garment shader; descriptor clothing_logos: per-character brand choice), thrds logo stub, manifest style tag + logo_capable. PROOF: tshirt_modern on 6 healthy bodies, 18-assertion suite green, 216-capture chromakey QA. ROUTE GUIDANCE from QA evidence (Q-060): single-reference SD-fit of offset-shells degrades with girth divergence (muscular_m worst 859px clip at 24mm standoff; reference body clean) — offset-shell garments should be authored PER BODY (needs per-body masks + runtime <body>_mask.png loading, small change); SD-fit remains right for derived/hand-authored garments sharing one mask (suit, uniform, Fantasy cuts). REMAINING for this ticket: refit tshirt to the 5 repaired forks (T-1090 done — add to --only and rerun), per-body shell mode, then the basic set: hoodie, button-down, shorts, jeans, formal pants, jacket + the suit/uniform hand-author proof; creation-screen try-on UI for tints/logos.
Basic-set EXPANSION (user, 2026-07-06 bedtime directive: ''full set of the basics across the board''): wave 2 after the in-flight wave 1 — FOOTWEAR: sneakers_modern (trainers, everyday default), shoes_formal (suit pairing), boots_modern (ankle height); SPORT: tank_top (sleeveless, logo-capable), joggers_modern (cuffed, track-stripe tint region), track_jacket (with joggers = the training suit, logo-capable); SWIM: swim_trunks (bright), swimsuit_onepiece (color-block regions). Foot garments follow the peasant_shoes both-feet convention; sole geometry as an offset-shell param extension. Total modern catalogue after wave 2: 17 garments across tops/bottoms/feet/full-body x casual/formal/sport/swim. DEFERRED deliberately: hats/accessories — accessory slot path is unexercised and T-721 flags a filter bug; needs daylight, not a 3am debug. Overnight sequence: wave 1 -> wave 2 -> full garment x 11-body fitting matrix (the ''T-1090 fittings'' check, fork bodies especially) -> commits -> gated pushes -> morning report.
Fill-in-the-blanks addendum (user bedtime directive 2 + lead judgment): wave 2 grows to 12 — adds sweater_modern (crew pullover), cargo_pants (utility/cyberpunk staple), parka_thrds (cold-weather was absent entirely; ships as the FIRST CANON-BRANDED garment — thrds is Braemar''s cold-weather fiber brand per wiki; hip-length to avoid the parked skirt-rigging tier), slides (swim footwear, near-free). PLUS outfit presets: data file of named complete looks (casual/business/sport/beach/worker/sleep as tint-palette presets over garments) authored by the lead at integration — makes the catalogue usable for NPC dressing and the shop as OUTFITS, not just items. Post-wave-2 catalogue: 21 modern pieces (tops 8 / bottoms 6 / feet 4 / full-body 2 / swim 3). Hats/accessories remain the deliberate daylight deferral (T-721).', 'review', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-07 09:23:38.257', NULL, '15fb82c297b4afafa017d7247c7ba67e', 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 ('06FKR283RTWYXD2M005K65S52M', 'bug', '06FB0TNSRXX29H5X0EB0F0GBW4', 'head_001 template renders grey (untinted) on child/teen/muscular bodies', 'Found by the T-1089 lookbook pass (2026-07-07): overlay head templates (head_001) render grey faces on child/teen/muscular bodies — the template appears not to receive the body''s skin-tone tint, while each body''s own seg_head renders correctly. Workaround used in the lookbook: head_id empty -> body seg_head shows. If head templates are meant to be body-agnostic (D-161 head-as-BoneAttachment3D), the skin-tone shader application needs to cover template heads per body; investigate _apply skin tone path in character_visual.gd for the head template branch.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-07 10:04:11.590', '2026-07-07 10:04:11.590', NULL, 'fee4a7bdc5705284493e52c31dd588c1', 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 ('06FKCPBF41RWK9PW9M22Q7961C', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Clothing catalogue engine + basic set — recolorable, logo-capable everyday garments', 'Fable-5-window sidequest workstream 2 (user directive 2026-07-06, extends T-1088 scope family): build the clothing ENGINE and a basic set now, park variety for later. Target bar: RimWorld''s original apparel breadth — varied enough to not feel limiting. Goal experience: being able to pick and SHOP for clothes — colorize + slap logos on casualwear. Basic set: t-shirt (logo-capable), hoodie (logo-capable), button-down shirt, shorts, jeans, formal pants, jacket, colorable uniform, black suit (moved up so officials are includable). Skipped by explicit call: capes, dresses/skirts (parked — difficulty tier). Engine deliverables: (1) multi-region tint masks — requires Quaternius Source tier files (USD5 Patreon month, one-time download, CC0 retained — user purchase action); (2) logo decal region on garment chest UV — flat 2D artwork per D-244, diegetic hook into economy generated brands (wiki/economics/corporations/generated_brands.toml) for brand-logo shirts; (3) manifest + CharacterVisualDescriptor category coverage (slots/tints already generic); (4) batch-fit each authored garment to all 11 body types via the validated Sprint-28 Blender Surface Deform pipeline (tooling/blender_*.py). Authoring routes per garment TBD by feasibility pass: derive from owned Fantasy pack meshes (62 parts, e.g. tunic->tshirt sleeve cut, pants->shorts hem cut) vs hand-author low-poly on the 65-bone rig (Garment Tool). Related: T-1088 locomotion sandbox doubles as the garment-under-animation QA rig (cycle outfits through Walk/Sprint/Crouch_Fwd).
Correction (2026-07-06): Source tier is PARTIALLY owned already — docs/assets/downloads/ (gitignored vendor archives, present in both checkouts) holds Universal Base Characters[Source] (630MB: full .blend files for all 6 base bodies incl. FullBody + OnlyHead variants, hairstyle .blends origin-at-0 + rigged-to-head-bone, texture masters, Godot engine project) plus UBC[Standard], UAL 1+2[Standard], Modular Character Outfits Fantasy[Standard]. NOT owned: Fantasy Outfits[Source] — the garment recolor masks + named vertex groups. Consequence: authoring NEW garments is fully unblocked (author against source body .blends, create our own tint masks/vertex groups — no purchase needed); only pack-DERIVED garments'' clean masks would want one more USD5 Patreon month, decision deferred until the feasibility route table says how much derivation we actually use.
Art direction (user, 2026-07-06): garments read CYBERPUNK/MODERN — near-future everyday civilian + corporate wear — NOT fantasy/medieval. The owned Fantasy pack is at most a topology donor (low-poly base shapes like pants/shirts are genre-neutral; genre lives in silhouette details, textures, trims, and accessories) — no medieval trim/textures may survive into shipped garments. Weight the authoring routes accordingly: texture identity matters as much as mesh. Canon hook: the wiki already ships a garment brand — thrds (wiki/star-systems/GJ-475/index.md, Braemar fiber cooperative, corridor fashion markets) — so branded clothing connects to Phase-1 lore, not only generated_brands.toml. This emphasis also feeds the Quaternius-pivot evaluation: commercial alternatives skew sci-fi/urban while Quaternius clothing is fantasy-only.
Nuance (user, 2026-07-06): the cyberpunk/modern direction is the DEFAULT, not a prohibition — fashion in the Reach is cyclical, so fantasy/medieval-flavored garments may appear here and there as deliberate fashion statements (subculture pieces, luxury/revival trims, statement outerwear). Supersedes the previous note''s hard ''no medieval trim may survive'' line. Practical consequence: a small number of Fantasy-pack pieces may ship near-as-is where they read as intentional fashion; the basic set itself stays modern.
Art direction CONSOLIDATED (user, 2026-07-06 — supersedes the two notes above): the Reach has no single clothing style — ALL styles coexist, with modern/cyberpunk probably the most prevalent. Style is a prevalence DISTRIBUTION, not a genre rule, and may eventually vary by culture/settlement (fits the simulation DNA; fashion-as-cultural-data is a later phase concern, not this ticket). Consequences now: (1) the engine is style-agnostic by construction (slots/tints/logos carry no genre); (2) the basic set targets the prevalent center — modern casual + corporate — because it dresses the most NPCs and the shopping experience; (3) garments get a lightweight style tag in the manifest (e.g. style: modern|formal|fantasy|utility) as the cheap seam so per-culture distributions can be authored later without retrofitting; (4) Fantasy-pack pieces are legitimate catalogue members under their own tag, prevalence-weighted low.
Scope pin (user, 2026-07-06, final word on style): for simplicity, THIS ticket ships ONE style only — cyberpunk/modern. The pluralistic distribution model above remains the long-term truth (keep the manifest style tag as the seam), but the v1 basic set is single-style; other styles are catalogue growth, not scope.
Route challenge (user, 2026-07-06): not sold on staying Quaternius-primary — ''Quaternius is limited and will move the wall at too slow a pace; I''d rather pick something that has what we need now.'' Fresh store verification (2026-07-06): Sidekick packs are one-time USD199.99, perpetual, engine-agnostic license (Godot legal, unsupported); NO raw FBX — extraction from unitypackage (community-proven: unitypackage_godot, third-party Sidekick Creator Godot plugin incl. body blend shapes); ''Sold out'' labels are a broken-theme artifact per Shopify JSON (needs one browser cart check); SyntyPass rejected (assets stop being developable after cancel). Relevant packs: Starter (FREE, 57+91 parts), Modern Civilians (165 parts = v1 basic set territory), Sci-Fi Civilians (103 parts, cyberpunk accents), Modern Police (uniforms/officials). DECISION NOW GATED ON THE FREE SPIKE: user downloads free Starter Pack -> Bridge-C spike (extract garment -> Robust Weight Transfer onto our rig -> 11-body batch -> animation QA in T-1088 sandbox). Pass -> buy Modern Civilians, Synty = primary garment supply (+UAL1/UAL2 Source ~USD30 for clips — no Synty substitute exists for Godot animations; Fantasy Outfits Source drops to low priority under the single-style pin). Fail -> hand-author route stands. Stay-vs-pivot governance record HELD until spike evidence lands.
Scope guard (user concern, 2026-07-06): Synty intake must NOT displace the create-stuff-yourself pipeline — the user''s goal includes in-house garment creation capability, not just swapping vendor catalogs. Resolution: three-route portfolio sharing one engine — (a) Synty intake = variety bootstrap only; (b) derivation scripting (cuts/merges/retexture, agent-automatable) = create-yourself lite, turns any donor topology into original designs; (c) TRUE AUTHORING on owned UBC Source body .blends = the pipeline proper. GUARANTEE: the colorable uniform and black suit (no donor exists in any pack) are built via route (c) as the authoring pipeline''s end-to-end proof — non-negotiable deliverable of this ticket, so the pipeline exists even if Synty covers everything else. Spike status: extraction done inline by lead (158 FBX, sci-fi civilian theme confirmed); DECISIVE finding — Sidekick rig is UE-Mannequin-named, 52/65 bone-name overlap with our rig, sample garment binds to 12 vgroups of which 11 match ours exactly (only head->Head case rename) => transplant likely rename-and-rebind, far cheaper than the weight-transfer the pivot eval assumed; body blend shapes travel in the FBX. Transplant phase parked pending API recovery / token-window reset.
ROUTE DECISION (user, 2026-07-06, final): Synty intake REJECTED despite the spike''s technical PASS — (1) USD199.99/pack is too expensive; (2) Sidekick modularity is baked body-segment swap (clothes fused to body chunks), incompatible with our layered clothing-over-body system (11 bodies, skin tones, slot swapping); (3) user dislikes the Synty style. The spike still paid: transplant/batch scripts are source-agnostic and become the G1-family production pipeline; T-1090 (5 bodies broken bare) discovered; the Synty question is closed with evidence, not parked. OPERATIVE ROUTE: (b) derive from owned Fantasy parts + (c) hand-author — with a new preferred authoring technique: OFFSET-SHELL FROM OUR OWN BODY MESHES (extract body segment surface, offset outward into garment shell, cut neckline/hem/sleeves; weights inherited by construction, style fully ours, USD0). Suit + uniform remain the full hand-author proof. Purchases still open (separate concerns): UAL1/UAL2 Source ~USD30 for animation clips; Fantasy Outfits Source USD20 low-priority convenience.
Chromakey garment-QA harness DELIVERED (2026-07-06, commits eaca6c8c4/74fa16260/b09a50efb): two-pass depth-proximity clip detection — pass A keys covered body segments magenta, pass B re-renders garment-only with a depth-only 3cm epsilon bias toward camera; analyzer splits key pixels into exposed_skin (informational — sleeveless arms, collars) vs clip_through (gating — skin within 3cm in front of cloth). Run: tooling/garment-qa/run-garment-qa <config>. Verified on the peasant set (avg_m/f x Walk/Sprint/Crouch_Fwd x 3 frames x 4 yaws): 0/18 front-view false positives; genuine findings = sleeveless armhole seam (avg_f, sides), deep-crouch waist gap (worst 150px, back), collar nape. First real fit observations already on record for the peasant set. Sensitivity: clip_epsilon_m + --min-pixels, to calibrate against the first modern garments. This is the acceptance gate for every garment the wardrobe pipeline produces; pair with the T-1088 sandbox for in-world walk QA.
ENGINE DELIVERED (2026-07-06, commits e22ea0fa4 + 278e8b7c2; earlier eaca6c8c4/74fa16260/b09a50efb chromakey, b54b8189d bodies): G1 skinned batch-fit (blender_batch_fit_skinned.py), offset-shell authoring (blender_author_offset_shell.py — route c create-yourself, weights by construction, procedural RGBA masks, UV2 chest channel), toon_garment.gdshader (channel-blended 4-region tint + logo after-tint/before-toon), compositor plumb (multi_region coverage flag -> garment shader; descriptor clothing_logos: per-character brand choice), thrds logo stub, manifest style tag + logo_capable. PROOF: tshirt_modern on 6 healthy bodies, 18-assertion suite green, 216-capture chromakey QA. ROUTE GUIDANCE from QA evidence (Q-060): single-reference SD-fit of offset-shells degrades with girth divergence (muscular_m worst 859px clip at 24mm standoff; reference body clean) — offset-shell garments should be authored PER BODY (needs per-body masks + runtime <body>_mask.png loading, small change); SD-fit remains right for derived/hand-authored garments sharing one mask (suit, uniform, Fantasy cuts). REMAINING for this ticket: refit tshirt to the 5 repaired forks (T-1090 done — add to --only and rerun), per-body shell mode, then the basic set: hoodie, button-down, shorts, jeans, formal pants, jacket + the suit/uniform hand-author proof; creation-screen try-on UI for tints/logos.
Basic-set EXPANSION (user, 2026-07-06 bedtime directive: ''full set of the basics across the board''): wave 2 after the in-flight wave 1 — FOOTWEAR: sneakers_modern (trainers, everyday default), shoes_formal (suit pairing), boots_modern (ankle height); SPORT: tank_top (sleeveless, logo-capable), joggers_modern (cuffed, track-stripe tint region), track_jacket (with joggers = the training suit, logo-capable); SWIM: swim_trunks (bright), swimsuit_onepiece (color-block regions). Foot garments follow the peasant_shoes both-feet convention; sole geometry as an offset-shell param extension. Total modern catalogue after wave 2: 17 garments across tops/bottoms/feet/full-body x casual/formal/sport/swim. DEFERRED deliberately: hats/accessories — accessory slot path is unexercised and T-721 flags a filter bug; needs daylight, not a 3am debug. Overnight sequence: wave 1 -> wave 2 -> full garment x 11-body fitting matrix (the ''T-1090 fittings'' check, fork bodies especially) -> commits -> gated pushes -> morning report.
Fill-in-the-blanks addendum (user bedtime directive 2 + lead judgment): wave 2 grows to 12 — adds sweater_modern (crew pullover), cargo_pants (utility/cyberpunk staple), parka_thrds (cold-weather was absent entirely; ships as the FIRST CANON-BRANDED garment — thrds is Braemar''s cold-weather fiber brand per wiki; hip-length to avoid the parked skirt-rigging tier), slides (swim footwear, near-free). PLUS outfit presets: data file of named complete looks (casual/business/sport/beach/worker/sleep as tint-palette presets over garments) authored by the lead at integration — makes the catalogue usable for NPC dressing and the shop as OUTFITS, not just items. Post-wave-2 catalogue: 21 modern pieces (tops 8 / bottoms 6 / feet 4 / full-body 2 / swim 3). Hats/accessories remain the deliberate daylight deferral (T-721).
ACCEPTED for first release (user review, 2026-07-07): full catalogue + lookbook approved; toe-box fix reviewed — ''I still see some toes, but this will do''. Residual toe hints on closed footwear accepted as first-release quality; further toe-box smoothing is polish backlog, not scope. Closing.', 'review', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-07 12:04:38.699', NULL, 'fe689e46ed80988afbb62827b85c5a9f', 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 ('06FKCPBF41RWK9PW9M22Q7961C', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Clothing catalogue engine + basic set — recolorable, logo-capable everyday garments', 'Fable-5-window sidequest workstream 2 (user directive 2026-07-06, extends T-1088 scope family): build the clothing ENGINE and a basic set now, park variety for later. Target bar: RimWorld''s original apparel breadth — varied enough to not feel limiting. Goal experience: being able to pick and SHOP for clothes — colorize + slap logos on casualwear. Basic set: t-shirt (logo-capable), hoodie (logo-capable), button-down shirt, shorts, jeans, formal pants, jacket, colorable uniform, black suit (moved up so officials are includable). Skipped by explicit call: capes, dresses/skirts (parked — difficulty tier). Engine deliverables: (1) multi-region tint masks — requires Quaternius Source tier files (USD5 Patreon month, one-time download, CC0 retained — user purchase action); (2) logo decal region on garment chest UV — flat 2D artwork per D-244, diegetic hook into economy generated brands (wiki/economics/corporations/generated_brands.toml) for brand-logo shirts; (3) manifest + CharacterVisualDescriptor category coverage (slots/tints already generic); (4) batch-fit each authored garment to all 11 body types via the validated Sprint-28 Blender Surface Deform pipeline (tooling/blender_*.py). Authoring routes per garment TBD by feasibility pass: derive from owned Fantasy pack meshes (62 parts, e.g. tunic->tshirt sleeve cut, pants->shorts hem cut) vs hand-author low-poly on the 65-bone rig (Garment Tool). Related: T-1088 locomotion sandbox doubles as the garment-under-animation QA rig (cycle outfits through Walk/Sprint/Crouch_Fwd).
Correction (2026-07-06): Source tier is PARTIALLY owned already — docs/assets/downloads/ (gitignored vendor archives, present in both checkouts) holds Universal Base Characters[Source] (630MB: full .blend files for all 6 base bodies incl. FullBody + OnlyHead variants, hairstyle .blends origin-at-0 + rigged-to-head-bone, texture masters, Godot engine project) plus UBC[Standard], UAL 1+2[Standard], Modular Character Outfits Fantasy[Standard]. NOT owned: Fantasy Outfits[Source] — the garment recolor masks + named vertex groups. Consequence: authoring NEW garments is fully unblocked (author against source body .blends, create our own tint masks/vertex groups — no purchase needed); only pack-DERIVED garments'' clean masks would want one more USD5 Patreon month, decision deferred until the feasibility route table says how much derivation we actually use.
Art direction (user, 2026-07-06): garments read CYBERPUNK/MODERN — near-future everyday civilian + corporate wear — NOT fantasy/medieval. The owned Fantasy pack is at most a topology donor (low-poly base shapes like pants/shirts are genre-neutral; genre lives in silhouette details, textures, trims, and accessories) — no medieval trim/textures may survive into shipped garments. Weight the authoring routes accordingly: texture identity matters as much as mesh. Canon hook: the wiki already ships a garment brand — thrds (wiki/star-systems/GJ-475/index.md, Braemar fiber cooperative, corridor fashion markets) — so branded clothing connects to Phase-1 lore, not only generated_brands.toml. This emphasis also feeds the Quaternius-pivot evaluation: commercial alternatives skew sci-fi/urban while Quaternius clothing is fantasy-only.
Nuance (user, 2026-07-06): the cyberpunk/modern direction is the DEFAULT, not a prohibition — fashion in the Reach is cyclical, so fantasy/medieval-flavored garments may appear here and there as deliberate fashion statements (subculture pieces, luxury/revival trims, statement outerwear). Supersedes the previous note''s hard ''no medieval trim may survive'' line. Practical consequence: a small number of Fantasy-pack pieces may ship near-as-is where they read as intentional fashion; the basic set itself stays modern.
Art direction CONSOLIDATED (user, 2026-07-06 — supersedes the two notes above): the Reach has no single clothing style — ALL styles coexist, with modern/cyberpunk probably the most prevalent. Style is a prevalence DISTRIBUTION, not a genre rule, and may eventually vary by culture/settlement (fits the simulation DNA; fashion-as-cultural-data is a later phase concern, not this ticket). Consequences now: (1) the engine is style-agnostic by construction (slots/tints/logos carry no genre); (2) the basic set targets the prevalent center — modern casual + corporate — because it dresses the most NPCs and the shopping experience; (3) garments get a lightweight style tag in the manifest (e.g. style: modern|formal|fantasy|utility) as the cheap seam so per-culture distributions can be authored later without retrofitting; (4) Fantasy-pack pieces are legitimate catalogue members under their own tag, prevalence-weighted low.
Scope pin (user, 2026-07-06, final word on style): for simplicity, THIS ticket ships ONE style only — cyberpunk/modern. The pluralistic distribution model above remains the long-term truth (keep the manifest style tag as the seam), but the v1 basic set is single-style; other styles are catalogue growth, not scope.
Route challenge (user, 2026-07-06): not sold on staying Quaternius-primary — ''Quaternius is limited and will move the wall at too slow a pace; I''d rather pick something that has what we need now.'' Fresh store verification (2026-07-06): Sidekick packs are one-time USD199.99, perpetual, engine-agnostic license (Godot legal, unsupported); NO raw FBX — extraction from unitypackage (community-proven: unitypackage_godot, third-party Sidekick Creator Godot plugin incl. body blend shapes); ''Sold out'' labels are a broken-theme artifact per Shopify JSON (needs one browser cart check); SyntyPass rejected (assets stop being developable after cancel). Relevant packs: Starter (FREE, 57+91 parts), Modern Civilians (165 parts = v1 basic set territory), Sci-Fi Civilians (103 parts, cyberpunk accents), Modern Police (uniforms/officials). DECISION NOW GATED ON THE FREE SPIKE: user downloads free Starter Pack -> Bridge-C spike (extract garment -> Robust Weight Transfer onto our rig -> 11-body batch -> animation QA in T-1088 sandbox). Pass -> buy Modern Civilians, Synty = primary garment supply (+UAL1/UAL2 Source ~USD30 for clips — no Synty substitute exists for Godot animations; Fantasy Outfits Source drops to low priority under the single-style pin). Fail -> hand-author route stands. Stay-vs-pivot governance record HELD until spike evidence lands.
Scope guard (user concern, 2026-07-06): Synty intake must NOT displace the create-stuff-yourself pipeline — the user''s goal includes in-house garment creation capability, not just swapping vendor catalogs. Resolution: three-route portfolio sharing one engine — (a) Synty intake = variety bootstrap only; (b) derivation scripting (cuts/merges/retexture, agent-automatable) = create-yourself lite, turns any donor topology into original designs; (c) TRUE AUTHORING on owned UBC Source body .blends = the pipeline proper. GUARANTEE: the colorable uniform and black suit (no donor exists in any pack) are built via route (c) as the authoring pipeline''s end-to-end proof — non-negotiable deliverable of this ticket, so the pipeline exists even if Synty covers everything else. Spike status: extraction done inline by lead (158 FBX, sci-fi civilian theme confirmed); DECISIVE finding — Sidekick rig is UE-Mannequin-named, 52/65 bone-name overlap with our rig, sample garment binds to 12 vgroups of which 11 match ours exactly (only head->Head case rename) => transplant likely rename-and-rebind, far cheaper than the weight-transfer the pivot eval assumed; body blend shapes travel in the FBX. Transplant phase parked pending API recovery / token-window reset.
ROUTE DECISION (user, 2026-07-06, final): Synty intake REJECTED despite the spike''s technical PASS — (1) USD199.99/pack is too expensive; (2) Sidekick modularity is baked body-segment swap (clothes fused to body chunks), incompatible with our layered clothing-over-body system (11 bodies, skin tones, slot swapping); (3) user dislikes the Synty style. The spike still paid: transplant/batch scripts are source-agnostic and become the G1-family production pipeline; T-1090 (5 bodies broken bare) discovered; the Synty question is closed with evidence, not parked. OPERATIVE ROUTE: (b) derive from owned Fantasy parts + (c) hand-author — with a new preferred authoring technique: OFFSET-SHELL FROM OUR OWN BODY MESHES (extract body segment surface, offset outward into garment shell, cut neckline/hem/sleeves; weights inherited by construction, style fully ours, USD0). Suit + uniform remain the full hand-author proof. Purchases still open (separate concerns): UAL1/UAL2 Source ~USD30 for animation clips; Fantasy Outfits Source USD20 low-priority convenience.
Chromakey garment-QA harness DELIVERED (2026-07-06, commits eaca6c8c4/74fa16260/b09a50efb): two-pass depth-proximity clip detection — pass A keys covered body segments magenta, pass B re-renders garment-only with a depth-only 3cm epsilon bias toward camera; analyzer splits key pixels into exposed_skin (informational — sleeveless arms, collars) vs clip_through (gating — skin within 3cm in front of cloth). Run: tooling/garment-qa/run-garment-qa <config>. Verified on the peasant set (avg_m/f x Walk/Sprint/Crouch_Fwd x 3 frames x 4 yaws): 0/18 front-view false positives; genuine findings = sleeveless armhole seam (avg_f, sides), deep-crouch waist gap (worst 150px, back), collar nape. First real fit observations already on record for the peasant set. Sensitivity: clip_epsilon_m + --min-pixels, to calibrate against the first modern garments. This is the acceptance gate for every garment the wardrobe pipeline produces; pair with the T-1088 sandbox for in-world walk QA.
ENGINE DELIVERED (2026-07-06, commits e22ea0fa4 + 278e8b7c2; earlier eaca6c8c4/74fa16260/b09a50efb chromakey, b54b8189d bodies): G1 skinned batch-fit (blender_batch_fit_skinned.py), offset-shell authoring (blender_author_offset_shell.py — route c create-yourself, weights by construction, procedural RGBA masks, UV2 chest channel), toon_garment.gdshader (channel-blended 4-region tint + logo after-tint/before-toon), compositor plumb (multi_region coverage flag -> garment shader; descriptor clothing_logos: per-character brand choice), thrds logo stub, manifest style tag + logo_capable. PROOF: tshirt_modern on 6 healthy bodies, 18-assertion suite green, 216-capture chromakey QA. ROUTE GUIDANCE from QA evidence (Q-060): single-reference SD-fit of offset-shells degrades with girth divergence (muscular_m worst 859px clip at 24mm standoff; reference body clean) — offset-shell garments should be authored PER BODY (needs per-body masks + runtime <body>_mask.png loading, small change); SD-fit remains right for derived/hand-authored garments sharing one mask (suit, uniform, Fantasy cuts). REMAINING for this ticket: refit tshirt to the 5 repaired forks (T-1090 done — add to --only and rerun), per-body shell mode, then the basic set: hoodie, button-down, shorts, jeans, formal pants, jacket + the suit/uniform hand-author proof; creation-screen try-on UI for tints/logos.
Basic-set EXPANSION (user, 2026-07-06 bedtime directive: ''full set of the basics across the board''): wave 2 after the in-flight wave 1 — FOOTWEAR: sneakers_modern (trainers, everyday default), shoes_formal (suit pairing), boots_modern (ankle height); SPORT: tank_top (sleeveless, logo-capable), joggers_modern (cuffed, track-stripe tint region), track_jacket (with joggers = the training suit, logo-capable); SWIM: swim_trunks (bright), swimsuit_onepiece (color-block regions). Foot garments follow the peasant_shoes both-feet convention; sole geometry as an offset-shell param extension. Total modern catalogue after wave 2: 17 garments across tops/bottoms/feet/full-body x casual/formal/sport/swim. DEFERRED deliberately: hats/accessories — accessory slot path is unexercised and T-721 flags a filter bug; needs daylight, not a 3am debug. Overnight sequence: wave 1 -> wave 2 -> full garment x 11-body fitting matrix (the ''T-1090 fittings'' check, fork bodies especially) -> commits -> gated pushes -> morning report.
Fill-in-the-blanks addendum (user bedtime directive 2 + lead judgment): wave 2 grows to 12 — adds sweater_modern (crew pullover), cargo_pants (utility/cyberpunk staple), parka_thrds (cold-weather was absent entirely; ships as the FIRST CANON-BRANDED garment — thrds is Braemar''s cold-weather fiber brand per wiki; hip-length to avoid the parked skirt-rigging tier), slides (swim footwear, near-free). PLUS outfit presets: data file of named complete looks (casual/business/sport/beach/worker/sleep as tint-palette presets over garments) authored by the lead at integration — makes the catalogue usable for NPC dressing and the shop as OUTFITS, not just items. Post-wave-2 catalogue: 21 modern pieces (tops 8 / bottoms 6 / feet 4 / full-body 2 / swim 3). Hats/accessories remain the deliberate daylight deferral (T-721).
ACCEPTED for first release (user review, 2026-07-07): full catalogue + lookbook approved; toe-box fix reviewed — ''I still see some toes, but this will do''. Residual toe hints on closed footwear accepted as first-release quality; further toe-box smoothing is polish backlog, not scope. Closing.', 'done', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-07 12:04:49.833', NULL, '348ec6c1104b8c20ae8ee3b2c63797b7', 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 ('06FKR283RTWYXD2M005K65S52M', 'bug', '06FB0TNSRXX29H5X0EB0F0GBW4', 'head_001 template renders grey (untinted) on child/teen/muscular bodies', 'Found by the T-1089 lookbook pass (2026-07-07): overlay head templates (head_001) render grey faces on child/teen/muscular bodies — the template appears not to receive the body''s skin-tone tint, while each body''s own seg_head renders correctly. Workaround used in the lookbook: head_id empty -> body seg_head shows. If head templates are meant to be body-agnostic (D-161 head-as-BoneAttachment3D), the skin-tone shader application needs to cover template heads per body; investigate _apply skin tone path in character_visual.gd for the head template branch.', 'done', 'medium', NULL, 'client', NULL, '2026-07-07 10:04:11.590', '2026-07-07 12:08:15.679', NULL, '8ed376d80da36bf8f1361c3d4dcf9bbf', 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 ('06FKRZ29KH70NFAY0Z1GCVR4N0', 'task', '06FB0TNSRXX29H5X0EB0F0GBW4', 'S9 live feel-tuning session — locomotion constants against the tuned eye', 'The one unfinished piece of T-1088, deliberately parked at sidequest close (2026-07-07) because it needs Jeroen at the keyboard: (1) verify turn-in-place handedness (headless could not — positive shortest-arc delta maps to _L; one-line TURN_CLIP swap if mirrored); (2) judge TURN_SPEED_SCALE=2.0 (UAL turn clips ~1.7-2.0s vs the ~0.3s yaw ease) and the turn-on-stop behavior; (3) general feel constants pass (NATIVE_MPS cadence sync, blends, camera follow/lookahead, cutaway corridor width) — every knob in client/scripts/sandbox/sandbox_constants.gd with the DebugHud showing live numbers. Launch: server --test-mode + SR_LIVE=1 locomotion_sandbox.tscn. Also revisit during the session: Q-084''s walk-vs-aim animation split (the purchased 8-dir walk/jog/crouch sets make direction-matched clips tractable).', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-07 12:10:06.108', '2026-07-07 12:10:06.108', NULL, '85414a301d4c7313046c3be096542165', 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 ('06FKCER95EK266H8RWPW8XT1F0', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', '3D character locomotion sidequest — composited character walks the Gauntlet on the live server', 'Fable-5-window sidequest (sanctioned cascade exception, 2026-07-05): the presentation layer that turns discrete server tile steps into a believable walking 3D character. Scope: 3D sandbox dev scene (SR_LIVE=1, greybox tiles derived from server snapshots, no hand-authored map), CharacterVisual instantiated in-world, locomotion rig (tile-step + facing-octant interpolation, stance-aware speeds), UAL animation state machine (idle/walk/sprint/careful/crouch), D-148 camera rig (30deg low-angle follow, 45deg rotation). Server-authoritative from day one — no client-local movement model. Builds on D-148/D-149/D-244/D-066 and the T-417 stance system. If the Fable 5 window closes mid-flight, work parks here.
Visual reference (user, 2026-07-06): Xenonauts 2 nails the target tiles/occlusion/character perspective — readable tile grid, low-angle tactical camera with angle options (matches D-148 30deg low-angle + 45deg rotation steps and the spikes/3dpipeline angle presets), and crucially its wall/roof occlusion treatment: camera-facing walls cut down/ghosted so interiors stay readable, floors peel per z-level. One deliberate difference: our camera locks to the player character (D-015/T-116) — no free map panning. Implication for the locomotion sandbox: greybox walls should prototype camera-facing cutaway occlusion early, since wall readability interacts with the locked low-angle camera.
Scoping call (user + lead agreement, 2026-07-06): wall cutaway is CLIENT-SIDE presentation, decoupled from fog-of-perception. Rationale: two occlusion systems with different subjects — perception/fog governs what the CHARACTER knows (server-enforced info boundary; client only receives observer-filtered geometry), cutaway governs what the PLAYER''S CAMERA sees (pure render mode on wall data the client already holds). They compose: cutaway cuts render height; fog overlay still gates what is visible behind. Walls remain first-class client data objects (future destruction/explosion interactions), cutaway is a render mode on them like tint/LOD. Nuance parked for Phase 5 proper: destroyed-while-unobserved walls should render in remembered state (fog-memory applied to geometry). If this holds through implementation, mint a D-record (perception domain) when the sidequest lands.
Animation supply resolved (2026-07-06): user purchased UAL1[Source+Pro] (120 clips, identical lists) + UAL2[Source] (134 clips), unzipped in docs/assets/downloads/. Gains: Turn90_L/R + Turn180_L/R (turn-in-place), Sprint_Enter/Exit + Crouch_Enter/Exit (stance transitions), full 8-direction Walk/Jog/Crouch sets incl. diagonals, Counter_Idle + talking/phone/sitting idles (life-sim verbs), plus _RM root-motion variants (unused — in-place is correct for server-authoritative interpolation). Still absent everywhere: walk start/stop + stairs — the only remaining custom clips. Integration is NOT a drop-in swap: upstream renamed clips (Walk_Loop -> Walk_Fwd_Loop etc.), so wire via the design''s recorded plan — explicit library names + lib/Clip addressing + gait-table update. NOTE for facing design: the 8-dir walk sets unlock Q-084''s parked walk-vs-aim split (direction-matched clips relative to facing) — revisit during the S9 tuning session.
Live-session feedback (user, 2026-07-06): (1) Corridor cutaway verdict — two minds: ''extremely trippy, could be the game''s own look'' vs ''player cannot see what is in the room unless close''. Resolution: cutaway MODES, toggled live in the sandbox (corridor / bottom-walls-low / both) — bottom-wall rule is data-driven (wall drops to stub when a KNOWN floor tile sits on its camera-far side; camera-facing directions derived from WorldRoot basis at runtime, not hand-derived). Default picked by feel during the session; the trippy corridor stays available as a signature-look option. (2) NEW: mouse-over move-here marker on the hovered tile + optimal path line — client-side A* over the never-evict KNOWN tile store (info-boundary-honest: the character plans only through known tiles; fog is unpathable), 8-dir with diagonal step cost 1 (time-optimal per the no-sqrt2 wire), terrain-cost provider seam (uniform today, Phase-4 terrain later), click executes by streaming ordinary Move* step commands through the existing throttle (zero protocol change, server validates every step), cancelled by WASD override / path invalidation / arrival. Settles more of Q-084 (click-to-move half).
RMB gesture vocabulary (user, live session 2026-07-06, added to the path-preview feature): single right-click = walk there (current stance); DOUBLE right-click = sprint there (ladder ToggleStanceUp burst to Sprint via the ordinary command queue, pre-sprint stance RESTORED on arrival; a double upgrades the already-started follow in place); LONG-PRESS right-click (~0.4s, commit on release) = ''take cover at location'', prototyped as go-there-then-Crouch-on-arrival (no restore — crouching was the point). Real cover mechanics (wall adjacency, directional protection) are future combat design; the gesture is the input-vocabulary prototype. WASD always cancels any follow without stance restore (player override is deliberate).
Follow-facing refinement (user, live session 2026-07-06): during an RMB path-follow the BODY commits to the leg/movement direction (mouse no longer swings body yaw); the mouse instead drives a LAYERED LOOK-AT — Head bone (~70deg limit) plus torso twist via a spine bone (~30deg) for looking far lateral/behind; beyond their sum the character physically cannot look further without turning (correct). Pure client presentation on the D-249 model; SetFacing continues to ride the wire during follows, so the SERVER vision cone still follows the mouse (diegetic: the character looks where the player points while walking a committed path). Implementation: Godot LookAtModifier3D pair under the CharacterVisual skeleton, influence-faded in/out on follow start/end; rig gains a commit_body_to_motion flag (yaw from leg velocity while set).
SIDEQUEST CLOSE-OUT (2026-07-07): all scope delivered and pushed — sandbox (scene, greybox store, rig, gait machine, camera, cutaway modes w/ user-verdict mode-2 default recorded as D-250 amendment), RMB navigation vocabulary, layered head/torso look-at, D-252 view/movement protocol split (server+client), purchased UAL tiers wired (turn-in-place, stance enter/exits), design doc committed to docs/design/t1088-locomotion-sandbox-design.md (the ''design §'' references in sandbox code resolve in-repo now). Design-delta close-out notes: no new Gauntlet room was needed (constraint 12 never triggered, server untouched except D-252); ~40 lines of main.gd boot boilerplate duplicated with Pattern: markers for the T-962 extraction; seek-during-blend + remove_immutable_tracks caveats did not manifest in practice; ual_extended wiring superseded by the purchased-tier ual1/ual2 libraries; 2D select_yaw_target moving-branch is dead code post-D-252 (cleanup rides Phase 5). Remaining feel work parked as T-1096 (S9 live tuning — needs the user). Closing.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:01:06.475', '2026-07-07 12:10:20.956', NULL, '990a574e76aab1e943fb4997885c7542', 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 ('06FKCER95EK266H8RWPW8XT1F0', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', '3D character locomotion sidequest — composited character walks the Gauntlet on the live server', 'Fable-5-window sidequest (sanctioned cascade exception, 2026-07-05): the presentation layer that turns discrete server tile steps into a believable walking 3D character. Scope: 3D sandbox dev scene (SR_LIVE=1, greybox tiles derived from server snapshots, no hand-authored map), CharacterVisual instantiated in-world, locomotion rig (tile-step + facing-octant interpolation, stance-aware speeds), UAL animation state machine (idle/walk/sprint/careful/crouch), D-148 camera rig (30deg low-angle follow, 45deg rotation). Server-authoritative from day one — no client-local movement model. Builds on D-148/D-149/D-244/D-066 and the T-417 stance system. If the Fable 5 window closes mid-flight, work parks here.
Visual reference (user, 2026-07-06): Xenonauts 2 nails the target tiles/occlusion/character perspective — readable tile grid, low-angle tactical camera with angle options (matches D-148 30deg low-angle + 45deg rotation steps and the spikes/3dpipeline angle presets), and crucially its wall/roof occlusion treatment: camera-facing walls cut down/ghosted so interiors stay readable, floors peel per z-level. One deliberate difference: our camera locks to the player character (D-015/T-116) — no free map panning. Implication for the locomotion sandbox: greybox walls should prototype camera-facing cutaway occlusion early, since wall readability interacts with the locked low-angle camera.
Scoping call (user + lead agreement, 2026-07-06): wall cutaway is CLIENT-SIDE presentation, decoupled from fog-of-perception. Rationale: two occlusion systems with different subjects — perception/fog governs what the CHARACTER knows (server-enforced info boundary; client only receives observer-filtered geometry), cutaway governs what the PLAYER''S CAMERA sees (pure render mode on wall data the client already holds). They compose: cutaway cuts render height; fog overlay still gates what is visible behind. Walls remain first-class client data objects (future destruction/explosion interactions), cutaway is a render mode on them like tint/LOD. Nuance parked for Phase 5 proper: destroyed-while-unobserved walls should render in remembered state (fog-memory applied to geometry). If this holds through implementation, mint a D-record (perception domain) when the sidequest lands.
Animation supply resolved (2026-07-06): user purchased UAL1[Source+Pro] (120 clips, identical lists) + UAL2[Source] (134 clips), unzipped in docs/assets/downloads/. Gains: Turn90_L/R + Turn180_L/R (turn-in-place), Sprint_Enter/Exit + Crouch_Enter/Exit (stance transitions), full 8-direction Walk/Jog/Crouch sets incl. diagonals, Counter_Idle + talking/phone/sitting idles (life-sim verbs), plus _RM root-motion variants (unused — in-place is correct for server-authoritative interpolation). Still absent everywhere: walk start/stop + stairs — the only remaining custom clips. Integration is NOT a drop-in swap: upstream renamed clips (Walk_Loop -> Walk_Fwd_Loop etc.), so wire via the design''s recorded plan — explicit library names + lib/Clip addressing + gait-table update. NOTE for facing design: the 8-dir walk sets unlock Q-084''s parked walk-vs-aim split (direction-matched clips relative to facing) — revisit during the S9 tuning session.
Live-session feedback (user, 2026-07-06): (1) Corridor cutaway verdict — two minds: ''extremely trippy, could be the game''s own look'' vs ''player cannot see what is in the room unless close''. Resolution: cutaway MODES, toggled live in the sandbox (corridor / bottom-walls-low / both) — bottom-wall rule is data-driven (wall drops to stub when a KNOWN floor tile sits on its camera-far side; camera-facing directions derived from WorldRoot basis at runtime, not hand-derived). Default picked by feel during the session; the trippy corridor stays available as a signature-look option. (2) NEW: mouse-over move-here marker on the hovered tile + optimal path line — client-side A* over the never-evict KNOWN tile store (info-boundary-honest: the character plans only through known tiles; fog is unpathable), 8-dir with diagonal step cost 1 (time-optimal per the no-sqrt2 wire), terrain-cost provider seam (uniform today, Phase-4 terrain later), click executes by streaming ordinary Move* step commands through the existing throttle (zero protocol change, server validates every step), cancelled by WASD override / path invalidation / arrival. Settles more of Q-084 (click-to-move half).
RMB gesture vocabulary (user, live session 2026-07-06, added to the path-preview feature): single right-click = walk there (current stance); DOUBLE right-click = sprint there (ladder ToggleStanceUp burst to Sprint via the ordinary command queue, pre-sprint stance RESTORED on arrival; a double upgrades the already-started follow in place); LONG-PRESS right-click (~0.4s, commit on release) = ''take cover at location'', prototyped as go-there-then-Crouch-on-arrival (no restore — crouching was the point). Real cover mechanics (wall adjacency, directional protection) are future combat design; the gesture is the input-vocabulary prototype. WASD always cancels any follow without stance restore (player override is deliberate).
Follow-facing refinement (user, live session 2026-07-06): during an RMB path-follow the BODY commits to the leg/movement direction (mouse no longer swings body yaw); the mouse instead drives a LAYERED LOOK-AT — Head bone (~70deg limit) plus torso twist via a spine bone (~30deg) for looking far lateral/behind; beyond their sum the character physically cannot look further without turning (correct). Pure client presentation on the D-249 model; SetFacing continues to ride the wire during follows, so the SERVER vision cone still follows the mouse (diegetic: the character looks where the player points while walking a committed path). Implementation: Godot LookAtModifier3D pair under the CharacterVisual skeleton, influence-faded in/out on follow start/end; rig gains a commit_body_to_motion flag (yaw from leg velocity while set).
SIDEQUEST CLOSE-OUT (2026-07-07): all scope delivered and pushed — sandbox (scene, greybox store, rig, gait machine, camera, cutaway modes w/ user-verdict mode-2 default recorded as D-250 amendment), RMB navigation vocabulary, layered head/torso look-at, D-252 view/movement protocol split (server+client), purchased UAL tiers wired (turn-in-place, stance enter/exits), design doc committed to docs/design/t1088-locomotion-sandbox-design.md (the ''design §'' references in sandbox code resolve in-repo now). Design-delta close-out notes: no new Gauntlet room was needed (constraint 12 never triggered, server untouched except D-252); ~40 lines of main.gd boot boilerplate duplicated with Pattern: markers for the T-962 extraction; seek-during-blend + remove_immutable_tracks caveats did not manifest in practice; ual_extended wiring superseded by the purchased-tier ual1/ual2 libraries; 2D select_yaw_target moving-branch is dead code post-D-252 (cleanup rides Phase 5). Remaining feel work parked as T-1096 (S9 live tuning — needs the user). Closing.', 'done', 'high', NULL, 'client', NULL, '2026-07-06 07:01:06.475', '2026-07-07 12:10:20.986', NULL, 'ce4896d59ece08a42a88e7e026b7d15f', 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 ('06FB0TNSRX65YFYFR5RNCM9MCG', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'Gemma architecture-flavor methodology doc (stage1 register + stage2 structured)', 'Methodology doc analogous to the D-223 naming one: stage-1 register selection from wiki prose, stage-2 structured output {tag, allow, block, weight, trauma_decay_rate}. Defines the Gemma prompt + review at GTTR/wiki authoring.
Round 3 reframe: Gemma is a BOUNDED corpus-distillation pass (one read of all wiki -> propose catalog templates -> human-gated), NOT per-body open-vocab generation. Plus core catalog hand-authoring (Miri = cultural meaning + eligibility; Araminta = matching visual_bundle).
Round-3 refinement (2026-05-26): reframed as a FOLLOW-ON extension, not a blocker of the core catalog (1005). Runs AFTER the core hand-authored set exists, to propose additional templates (corpus-distillation, human-gated) toward the 40-45 target. Needs an architecture-flavor Gemma methodology doc (analogous to the naming one) defining the extraction prompt + structured output schema + human-review gate.
---
Refinement (/whats-next 2026-07-07): The stage-2 schema in the description is stale round-2 text — there is no trauma_decay_rate anywhere in schema or code; D-232 round 3 (trait-template model) is authoritative. RESOLVED (user): Gemma proposes TEXT FIELDS ONLY — tag, label, cultural_description, candidate allow_tags/block_tags. Humans set all numeric/eligibility fields with whole-pool knowledge (Miri: gates, base_weight, weight_mods, zone_affinity, corridor_pool/geographic_sector; Araminta: visual_bundle) — the catalog-wide CI guardrails (V-TT-01 pool>=5, V-TT-02 weight<=60%) cannot be satisfied by a per-template extraction pass, so Gemma never sets numbers. Doc target: docs/gemma-architecture-flavor-methodology.md (analogous to docs/gemma-naming-methodology.md). Stage-1 register selection reads the current wiki tree (wiki/star-systems/*/index.md); state that assumption explicitly in the doc — location may move if Q-107 resolves. Q-106 is dissolved: no era-band logic in the doc.', 'backlog', 'high', NULL, 'server', 'D-232', '2026-05-25 19:11:41', '2026-07-07 16:57:22.053', NULL, 'b7e7fec5b37551d04fc6249ab697de70', 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 ('06FB0TNSRWYHR72SC80F5T0TBW', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'allow/block weighted filter at chunk fill over zone-type ObjectTag pool', 'Apply the flavor profile as a filter over the D-142 zone-type ObjectTag baseline pool: block removed, allow weighted up by weight; dominant wins categorical, weighted-avg continuous. Three-layer: body x district x era-band (depth Q-106).
Round 3: this becomes the THREE-PHASE DRAW (body K-draw [K=5/3/1/0 by complexity_tier, SeedChain, coverage-aware] -> district-dominant by zone_affinity -> within-template seed picks) + CI guardrails, per D-232 — not a flat per-body allow/block filter.
---
Refinement (/whats-next 2026-07-07): Paragraph 1 of the description is superseded round-1/2 prose — the "D-142 zone-type ObjectTag baseline pool" is a miscite (D-142 = NPC-behavior zone taxonomy; no such pool exists anywhere) and era-bands were dissolved with Q-106. The round-3 three-phase draw is the whole scope. RESOLVED (user decisions):
1. "District-dominant" = the TRUE D-243 2048m District tier (4 quarters), NOT the 512m Quarter. The District tier has no code representation in the cascade yet — building the District aggregation (grouping quarters, assigning one dominant template per 2048m District) is IN SCOPE for this ticket. The D-222/D-243 district redefinition was never captured into D-232 — amend D-232 with a dated note pinning "district" = the D-243 2048m tier as part of this work.
2. The dominant-template pick runs at GenerateSkeleton (NOT "FillChunk head" — FillChunk is pure/cache-free by design per T-987); store the resolved template on skeleton/block tags.
3. Coverage: thread a body-level district-type-mix aggregate into CityGenerationContext (from settlement placement / the L3-L4 dispatch) so draw_body_vocabulary() is coverage-aware up front (>=1 eligible template per present district type).
4. Seed the body K-draw from SeedChain::for_body(world_seed, body_id) — NOT the per-dispatch chain — so every city/quarter on a body draws the identical closed vocabulary (D-232 invariant).
5. Files: server/src/atlas/skeleton_gen.rs is the primary site — replace the shipped flat assign_block_tags/zone_type_hash flavor_index pick (round-1/2 model) with the three-phase draw. The CI guardrails already exist and pass (V-TT-01/V-TT-02 in tooling/economy-db/economy_import/traits.py) — no importer work needed.
6. geographic_sector remains a SOFT weight, two-part pool join (PR #148 note stands).', 'backlog', 'medium', NULL, 'server', 'D-232', '2026-05-25 19:11:45', '2026-07-07 16:57:22.461', NULL, 'f6c6be1e3a52525551f1c3bce89c8dac', 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 ('06FB0TNSRZCG08B0P6PRNQV8KG', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'ObjectTag vocabulary authoring (Miri + Araminta co-maintenance)', 'Author the ObjectTag + exterior-material vocabulary the allow/block lists and D-235 grammar reference. Carried over from the retired generator-arch D-READY-9 requirement. Content task, Miri + Araminta.
---
Refinement (/whats-next 2026-07-07): RESOLVED (user): the shipped 28-tag material palette in wiki/economics/architecture_trait_catalog.toml (T-1005) is RATIFIED as canonical. D-235''s example WallMaterial/RoofForm/FacadeRhythm/StreetSurface token lists must be amended to match shipped reality as part of this ticket. Deliverable = formalize-and-validate: (a) a machine-readable tag registry (proposal: wiki/economics/object_tag_vocabulary.toml, imported by economy_import/traits.py) covering every tag used in allow_tags/block_tags/visual_bundle; (b) importer validation that all catalog tags resolve against the registry; (c) fallback-graph validation absorbed from T-1004 (parked to Phase 5): every specific token declares a generic parent (generic_wall/roof/facade/street) and resolution is acyclic — build-time check. Cite Q-049 (ObjectTag ownership/co-maintenance, still open — Miri: cultural meaning, Araminta: visual) — this ticket''s registry + review flow is the practical answer; close or update Q-049 with it. Q-107: the registry is generator-facing and Q-107-invariant like the catalog table; note the location assumption in the file header.', 'backlog', 'medium', NULL, 'server', 'D-232', '2026-05-25 19:11:47', '2026-07-07 16:57:23.405', NULL, 'ab05a81fedd1e474794fe03fe8eff4f4', 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 ('06FB0TNSRWGDT3AJJE5QD0E3FR', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'Deviation/swerve system — closed-default + rare per-building wildcard', 'Closed vocabulary by default; a rare per-building wildcard draws a COHERENT whole foreign template (cultural-only — function still passes economic hard gates; never an axis-scramble). Two OPPOSED active drivers: foreign-import (another corridor) scaled UP by cosmopolitanism/centrality/transit/Epicenter tier; heritage-callback (the body''s own corridor heritage sub-pool) scaled UP by remoteness/isolation/conservatism. Passive past-vogue holdover from age (D-217 wear). Sparsity escape-hatch = the SAME mechanism triggered by necessity when the closed vocabulary cannot serve a district. Per D-232.
---
Refinement (/whats-next 2026-07-07): Blocked by T-994 (edge added) — the swerve overrides the three-phase draw; implement after it lands, within this batch. RESOLVED (user): resolve driver mappings now. Proposed mappings to REAL fields (integer bps mods, constants in one tunable block):
- foreign-import scale-ups: WorldTier::Epicenter / WorldTier::Passage (transit), dominant_faction == mixed (cosmopolitanism), road/rail-graph hub degree where available in context (centrality; else world_tier proxy).
- heritage-callback scale-ups: remoteness/isolation from star_systems dist_ly / earth_proximity percentile plus absence of Passage tier; conservatism from founding_age_years (older = stronger callback; T-1000 backfilled the field).
- Placeholder wildcard rate: base 100 bps (1%) per building, driver mods multiplicative, hard cap 300 bps; all constants integer bps per D-010 determinism.
- Passive past-vogue holdover rides D-217 wear/era; no new fields.
Data-model prerequisite: extend ArchitectureFlavorRef (server/src/simulation/generator.rs:826, currently a u8 index into trait_selection) to represent an out-of-vocabulary template (e.g. InVocabulary(u8) | Swerve(template_tag)) — the wildcard draws outside the closed K-set by definition.
Sparsity escape-hatch: the SAME mechanism, necessity-triggered at District-dominant assignment time (2048m tier per T-994 refinement) when zero eligible templates serve the district type post-hard-gate.', 'backlog', 'medium', NULL, 'server', 'D-232', '2026-05-25 20:36:39', '2026-07-07 16:57:23.876', NULL, '9ea58bb1214745200ab1cd3f9ff22011', 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 ('06FB0TNSRZNNMZPXSEZ7HN778R', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'Visual-asset fallback hierarchy (specific->generic tokens, patch-upgradable)', 'Every specific texture/material token declares a generic parent it degrades to: temple_wall_wood -> generic wood_wall placeholder until the specific asset ships, then it upgrades IN PLACE. The logical token a building uses is fixed + deterministic at generation (frozen world); only render fidelity is incremental. This is where patched-in themes/textures live -> NO catalog versioning needed for art (versioning only re-enters if a NEW logical template is added post-launch). Per D-232 + D-235. Cross-refs ticket 988 (exterior visual grammar).
---
Refinement (/whats-next 2026-07-07): PARKED TO PHASE 5 (user decision) — reparented under the Phase 5 epic. D-235 itself marks the patch-upgrade render mechanism Phase 5+/post-launch; the fallback DATA already shipped (T-1005 catalog visual_bundle.fallback maps + the four generic placeholders generic_wall/roof/facade/street). The Phase-4-needed slice (build-time fallback-graph validation: every specific token resolves to a generic, no cycles) moved to T-995. When picked up in Phase 5: the mechanism is client/asset-pipeline resolution (which assets have shipped -> in-place upgrade); coordinate with T-988 (BuildingExteriorTag grammar), T-1050 (production-status catalog, held) and open Q-119 (generic-parent asset set enumeration).', 'backlog', 'medium', NULL, 'server', 'D-235', '2026-05-25 20:36:42', '2026-07-07 16:57:24.538', NULL, '130f4ab3b4173c89730061c68dc85aa6', 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 ('06FB0TNSRZNNMZPXSEZ7HN778R', 'task', '06FB0TNSRXX29H5X0EB0F0GBW4', 'Visual-asset fallback hierarchy (specific->generic tokens, patch-upgradable)', 'Every specific texture/material token declares a generic parent it degrades to: temple_wall_wood -> generic wood_wall placeholder until the specific asset ships, then it upgrades IN PLACE. The logical token a building uses is fixed + deterministic at generation (frozen world); only render fidelity is incremental. This is where patched-in themes/textures live -> NO catalog versioning needed for art (versioning only re-enters if a NEW logical template is added post-launch). Per D-232 + D-235. Cross-refs ticket 988 (exterior visual grammar).
---
Refinement (/whats-next 2026-07-07): PARKED TO PHASE 5 (user decision) — reparented under the Phase 5 epic. D-235 itself marks the patch-upgrade render mechanism Phase 5+/post-launch; the fallback DATA already shipped (T-1005 catalog visual_bundle.fallback maps + the four generic placeholders generic_wall/roof/facade/street). The Phase-4-needed slice (build-time fallback-graph validation: every specific token resolves to a generic, no cycles) moved to T-995. When picked up in Phase 5: the mechanism is client/asset-pipeline resolution (which assets have shipped -> in-place upgrade); coordinate with T-988 (BuildingExteriorTag grammar), T-1050 (production-status catalog, held) and open Q-119 (generic-parent asset set enumeration).', 'backlog', 'medium', NULL, 'server', 'D-235', '2026-05-25 20:36:42', '2026-07-07 16:57:31.896', NULL, 'b5ac81f800b50c83375168b8890577e6', 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 ('06FB0TNSRX65YFYFR5RNCM9MCG', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'Gemma architecture-flavor methodology doc (stage1 register + stage2 structured)', 'Methodology doc analogous to the D-223 naming one: stage-1 register selection from wiki prose, stage-2 structured output {tag, allow, block, weight, trauma_decay_rate}. Defines the Gemma prompt + review at GTTR/wiki authoring.
Round 3 reframe: Gemma is a BOUNDED corpus-distillation pass (one read of all wiki -> propose catalog templates -> human-gated), NOT per-body open-vocab generation. Plus core catalog hand-authoring (Miri = cultural meaning + eligibility; Araminta = matching visual_bundle).
Round-3 refinement (2026-05-26): reframed as a FOLLOW-ON extension, not a blocker of the core catalog (1005). Runs AFTER the core hand-authored set exists, to propose additional templates (corpus-distillation, human-gated) toward the 40-45 target. Needs an architecture-flavor Gemma methodology doc (analogous to the naming one) defining the extraction prompt + structured output schema + human-review gate.
---
Refinement (/whats-next 2026-07-07): The stage-2 schema in the description is stale round-2 text — there is no trauma_decay_rate anywhere in schema or code; D-232 round 3 (trait-template model) is authoritative. RESOLVED (user): Gemma proposes TEXT FIELDS ONLY — tag, label, cultural_description, candidate allow_tags/block_tags. Humans set all numeric/eligibility fields with whole-pool knowledge (Miri: gates, base_weight, weight_mods, zone_affinity, corridor_pool/geographic_sector; Araminta: visual_bundle) — the catalog-wide CI guardrails (V-TT-01 pool>=5, V-TT-02 weight<=60%) cannot be satisfied by a per-template extraction pass, so Gemma never sets numbers. Doc target: docs/gemma-architecture-flavor-methodology.md (analogous to docs/gemma-naming-methodology.md). Stage-1 register selection reads the current wiki tree (wiki/star-systems/*/index.md); state that assumption explicitly in the doc — location may move if Q-107 resolves. Q-106 is dissolved: no era-band logic in the doc.', 'in_progress', 'high', NULL, 'server', 'D-232', '2026-05-25 19:11:41', '2026-07-07 17:18:47.872', NULL, 'a3d2c8fddd45a640973e0d5540d88d5c', 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 ('06FB0TNSRWYHR72SC80F5T0TBW', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'allow/block weighted filter at chunk fill over zone-type ObjectTag pool', 'Apply the flavor profile as a filter over the D-142 zone-type ObjectTag baseline pool: block removed, allow weighted up by weight; dominant wins categorical, weighted-avg continuous. Three-layer: body x district x era-band (depth Q-106).
Round 3: this becomes the THREE-PHASE DRAW (body K-draw [K=5/3/1/0 by complexity_tier, SeedChain, coverage-aware] -> district-dominant by zone_affinity -> within-template seed picks) + CI guardrails, per D-232 — not a flat per-body allow/block filter.
---
Refinement (/whats-next 2026-07-07): Paragraph 1 of the description is superseded round-1/2 prose — the "D-142 zone-type ObjectTag baseline pool" is a miscite (D-142 = NPC-behavior zone taxonomy; no such pool exists anywhere) and era-bands were dissolved with Q-106. The round-3 three-phase draw is the whole scope. RESOLVED (user decisions):
1. "District-dominant" = the TRUE D-243 2048m District tier (4 quarters), NOT the 512m Quarter. The District tier has no code representation in the cascade yet — building the District aggregation (grouping quarters, assigning one dominant template per 2048m District) is IN SCOPE for this ticket. The D-222/D-243 district redefinition was never captured into D-232 — amend D-232 with a dated note pinning "district" = the D-243 2048m tier as part of this work.
2. The dominant-template pick runs at GenerateSkeleton (NOT "FillChunk head" — FillChunk is pure/cache-free by design per T-987); store the resolved template on skeleton/block tags.
3. Coverage: thread a body-level district-type-mix aggregate into CityGenerationContext (from settlement placement / the L3-L4 dispatch) so draw_body_vocabulary() is coverage-aware up front (>=1 eligible template per present district type).
4. Seed the body K-draw from SeedChain::for_body(world_seed, body_id) — NOT the per-dispatch chain — so every city/quarter on a body draws the identical closed vocabulary (D-232 invariant).
5. Files: server/src/atlas/skeleton_gen.rs is the primary site — replace the shipped flat assign_block_tags/zone_type_hash flavor_index pick (round-1/2 model) with the three-phase draw. The CI guardrails already exist and pass (V-TT-01/V-TT-02 in tooling/economy-db/economy_import/traits.py) — no importer work needed.
6. geographic_sector remains a SOFT weight, two-part pool join (PR #148 note stands).', 'in_progress', 'medium', NULL, 'server', 'D-232', '2026-05-25 19:11:45', '2026-07-07 17:18:47.875', NULL, '939966b06bcf04503b737256f2d2a148', 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 ('06FB0TNSRZCG08B0P6PRNQV8KG', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'ObjectTag vocabulary authoring (Miri + Araminta co-maintenance)', 'Author the ObjectTag + exterior-material vocabulary the allow/block lists and D-235 grammar reference. Carried over from the retired generator-arch D-READY-9 requirement. Content task, Miri + Araminta.
---
Refinement (/whats-next 2026-07-07): RESOLVED (user): the shipped 28-tag material palette in wiki/economics/architecture_trait_catalog.toml (T-1005) is RATIFIED as canonical. D-235''s example WallMaterial/RoofForm/FacadeRhythm/StreetSurface token lists must be amended to match shipped reality as part of this ticket. Deliverable = formalize-and-validate: (a) a machine-readable tag registry (proposal: wiki/economics/object_tag_vocabulary.toml, imported by economy_import/traits.py) covering every tag used in allow_tags/block_tags/visual_bundle; (b) importer validation that all catalog tags resolve against the registry; (c) fallback-graph validation absorbed from T-1004 (parked to Phase 5): every specific token declares a generic parent (generic_wall/roof/facade/street) and resolution is acyclic — build-time check. Cite Q-049 (ObjectTag ownership/co-maintenance, still open — Miri: cultural meaning, Araminta: visual) — this ticket''s registry + review flow is the practical answer; close or update Q-049 with it. Q-107: the registry is generator-facing and Q-107-invariant like the catalog table; note the location assumption in the file header.', 'in_progress', 'medium', NULL, 'server', 'D-232', '2026-05-25 19:11:47', '2026-07-07 17:18:47.875', NULL, 'd7f3d3ab41881fcdfa12682680a8eff1', 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 ('06FB0TNSRWGDT3AJJE5QD0E3FR', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'Deviation/swerve system — closed-default + rare per-building wildcard', 'Closed vocabulary by default; a rare per-building wildcard draws a COHERENT whole foreign template (cultural-only — function still passes economic hard gates; never an axis-scramble). Two OPPOSED active drivers: foreign-import (another corridor) scaled UP by cosmopolitanism/centrality/transit/Epicenter tier; heritage-callback (the body''s own corridor heritage sub-pool) scaled UP by remoteness/isolation/conservatism. Passive past-vogue holdover from age (D-217 wear). Sparsity escape-hatch = the SAME mechanism triggered by necessity when the closed vocabulary cannot serve a district. Per D-232.
---
Refinement (/whats-next 2026-07-07): Blocked by T-994 (edge added) — the swerve overrides the three-phase draw; implement after it lands, within this batch. RESOLVED (user): resolve driver mappings now. Proposed mappings to REAL fields (integer bps mods, constants in one tunable block):
- foreign-import scale-ups: WorldTier::Epicenter / WorldTier::Passage (transit), dominant_faction == mixed (cosmopolitanism), road/rail-graph hub degree where available in context (centrality; else world_tier proxy).
- heritage-callback scale-ups: remoteness/isolation from star_systems dist_ly / earth_proximity percentile plus absence of Passage tier; conservatism from founding_age_years (older = stronger callback; T-1000 backfilled the field).
- Placeholder wildcard rate: base 100 bps (1%) per building, driver mods multiplicative, hard cap 300 bps; all constants integer bps per D-010 determinism.
- Passive past-vogue holdover rides D-217 wear/era; no new fields.
Data-model prerequisite: extend ArchitectureFlavorRef (server/src/simulation/generator.rs:826, currently a u8 index into trait_selection) to represent an out-of-vocabulary template (e.g. InVocabulary(u8) | Swerve(template_tag)) — the wildcard draws outside the closed K-set by definition.
Sparsity escape-hatch: the SAME mechanism, necessity-triggered at District-dominant assignment time (2048m tier per T-994 refinement) when zero eligible templates serve the district type post-hard-gate.', 'in_progress', 'medium', NULL, 'server', 'D-232', '2026-05-25 20:36:39', '2026-07-07 17:18:47.876', NULL, '12387d8b75191ae9e2a0ace0b43e2c9d', 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 ('06FB0TNSRX65YFYFR5RNCM9MCG', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'Gemma architecture-flavor methodology doc (stage1 register + stage2 structured)', 'Methodology doc analogous to the D-223 naming one: stage-1 register selection from wiki prose, stage-2 structured output {tag, allow, block, weight, trauma_decay_rate}. Defines the Gemma prompt + review at GTTR/wiki authoring.
Round 3 reframe: Gemma is a BOUNDED corpus-distillation pass (one read of all wiki -> propose catalog templates -> human-gated), NOT per-body open-vocab generation. Plus core catalog hand-authoring (Miri = cultural meaning + eligibility; Araminta = matching visual_bundle).
Round-3 refinement (2026-05-26): reframed as a FOLLOW-ON extension, not a blocker of the core catalog (1005). Runs AFTER the core hand-authored set exists, to propose additional templates (corpus-distillation, human-gated) toward the 40-45 target. Needs an architecture-flavor Gemma methodology doc (analogous to the naming one) defining the extraction prompt + structured output schema + human-review gate.
---
Refinement (/whats-next 2026-07-07): The stage-2 schema in the description is stale round-2 text — there is no trauma_decay_rate anywhere in schema or code; D-232 round 3 (trait-template model) is authoritative. RESOLVED (user): Gemma proposes TEXT FIELDS ONLY — tag, label, cultural_description, candidate allow_tags/block_tags. Humans set all numeric/eligibility fields with whole-pool knowledge (Miri: gates, base_weight, weight_mods, zone_affinity, corridor_pool/geographic_sector; Araminta: visual_bundle) — the catalog-wide CI guardrails (V-TT-01 pool>=5, V-TT-02 weight<=60%) cannot be satisfied by a per-template extraction pass, so Gemma never sets numbers. Doc target: docs/gemma-architecture-flavor-methodology.md (analogous to docs/gemma-naming-methodology.md). Stage-1 register selection reads the current wiki tree (wiki/star-systems/*/index.md); state that assumption explicitly in the doc — location may move if Q-107 resolves. Q-106 is dissolved: no era-band logic in the doc.', 'review', 'high', NULL, 'server', 'D-232', '2026-05-25 19:11:41', '2026-07-08 08:01:20.003', NULL, 'a3ef8c8ce1df05b2e599ad8d0b8b1c4c', 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 ('06FB0TNSRWYHR72SC80F5T0TBW', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'allow/block weighted filter at chunk fill over zone-type ObjectTag pool', 'Apply the flavor profile as a filter over the D-142 zone-type ObjectTag baseline pool: block removed, allow weighted up by weight; dominant wins categorical, weighted-avg continuous. Three-layer: body x district x era-band (depth Q-106).
Round 3: this becomes the THREE-PHASE DRAW (body K-draw [K=5/3/1/0 by complexity_tier, SeedChain, coverage-aware] -> district-dominant by zone_affinity -> within-template seed picks) + CI guardrails, per D-232 — not a flat per-body allow/block filter.
---
Refinement (/whats-next 2026-07-07): Paragraph 1 of the description is superseded round-1/2 prose — the "D-142 zone-type ObjectTag baseline pool" is a miscite (D-142 = NPC-behavior zone taxonomy; no such pool exists anywhere) and era-bands were dissolved with Q-106. The round-3 three-phase draw is the whole scope. RESOLVED (user decisions):
1. "District-dominant" = the TRUE D-243 2048m District tier (4 quarters), NOT the 512m Quarter. The District tier has no code representation in the cascade yet — building the District aggregation (grouping quarters, assigning one dominant template per 2048m District) is IN SCOPE for this ticket. The D-222/D-243 district redefinition was never captured into D-232 — amend D-232 with a dated note pinning "district" = the D-243 2048m tier as part of this work.
2. The dominant-template pick runs at GenerateSkeleton (NOT "FillChunk head" — FillChunk is pure/cache-free by design per T-987); store the resolved template on skeleton/block tags.
3. Coverage: thread a body-level district-type-mix aggregate into CityGenerationContext (from settlement placement / the L3-L4 dispatch) so draw_body_vocabulary() is coverage-aware up front (>=1 eligible template per present district type).
4. Seed the body K-draw from SeedChain::for_body(world_seed, body_id) — NOT the per-dispatch chain — so every city/quarter on a body draws the identical closed vocabulary (D-232 invariant).
5. Files: server/src/atlas/skeleton_gen.rs is the primary site — replace the shipped flat assign_block_tags/zone_type_hash flavor_index pick (round-1/2 model) with the three-phase draw. The CI guardrails already exist and pass (V-TT-01/V-TT-02 in tooling/economy-db/economy_import/traits.py) — no importer work needed.
6. geographic_sector remains a SOFT weight, two-part pool join (PR #148 note stands).', 'review', 'medium', NULL, 'server', 'D-232', '2026-05-25 19:11:45', '2026-07-08 08:01:20.007', NULL, 'e58054f9c43aea8887480459658e4507', 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 ('06FB0TNSRZCG08B0P6PRNQV8KG', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'ObjectTag vocabulary authoring (Miri + Araminta co-maintenance)', 'Author the ObjectTag + exterior-material vocabulary the allow/block lists and D-235 grammar reference. Carried over from the retired generator-arch D-READY-9 requirement. Content task, Miri + Araminta.
---
Refinement (/whats-next 2026-07-07): RESOLVED (user): the shipped 28-tag material palette in wiki/economics/architecture_trait_catalog.toml (T-1005) is RATIFIED as canonical. D-235''s example WallMaterial/RoofForm/FacadeRhythm/StreetSurface token lists must be amended to match shipped reality as part of this ticket. Deliverable = formalize-and-validate: (a) a machine-readable tag registry (proposal: wiki/economics/object_tag_vocabulary.toml, imported by economy_import/traits.py) covering every tag used in allow_tags/block_tags/visual_bundle; (b) importer validation that all catalog tags resolve against the registry; (c) fallback-graph validation absorbed from T-1004 (parked to Phase 5): every specific token declares a generic parent (generic_wall/roof/facade/street) and resolution is acyclic — build-time check. Cite Q-049 (ObjectTag ownership/co-maintenance, still open — Miri: cultural meaning, Araminta: visual) — this ticket''s registry + review flow is the practical answer; close or update Q-049 with it. Q-107: the registry is generator-facing and Q-107-invariant like the catalog table; note the location assumption in the file header.', 'review', 'medium', NULL, 'server', 'D-232', '2026-05-25 19:11:47', '2026-07-08 08:01:20.008', NULL, '94d8036021cbecaf72dcf40bd1ff4041', 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 ('06FB0TNSRWGDT3AJJE5QD0E3FR', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'Deviation/swerve system — closed-default + rare per-building wildcard', 'Closed vocabulary by default; a rare per-building wildcard draws a COHERENT whole foreign template (cultural-only — function still passes economic hard gates; never an axis-scramble). Two OPPOSED active drivers: foreign-import (another corridor) scaled UP by cosmopolitanism/centrality/transit/Epicenter tier; heritage-callback (the body''s own corridor heritage sub-pool) scaled UP by remoteness/isolation/conservatism. Passive past-vogue holdover from age (D-217 wear). Sparsity escape-hatch = the SAME mechanism triggered by necessity when the closed vocabulary cannot serve a district. Per D-232.
---
Refinement (/whats-next 2026-07-07): Blocked by T-994 (edge added) — the swerve overrides the three-phase draw; implement after it lands, within this batch. RESOLVED (user): resolve driver mappings now. Proposed mappings to REAL fields (integer bps mods, constants in one tunable block):
- foreign-import scale-ups: WorldTier::Epicenter / WorldTier::Passage (transit), dominant_faction == mixed (cosmopolitanism), road/rail-graph hub degree where available in context (centrality; else world_tier proxy).
- heritage-callback scale-ups: remoteness/isolation from star_systems dist_ly / earth_proximity percentile plus absence of Passage tier; conservatism from founding_age_years (older = stronger callback; T-1000 backfilled the field).
- Placeholder wildcard rate: base 100 bps (1%) per building, driver mods multiplicative, hard cap 300 bps; all constants integer bps per D-010 determinism.
- Passive past-vogue holdover rides D-217 wear/era; no new fields.
Data-model prerequisite: extend ArchitectureFlavorRef (server/src/simulation/generator.rs:826, currently a u8 index into trait_selection) to represent an out-of-vocabulary template (e.g. InVocabulary(u8) | Swerve(template_tag)) — the wildcard draws outside the closed K-set by definition.
Sparsity escape-hatch: the SAME mechanism, necessity-triggered at District-dominant assignment time (2048m tier per T-994 refinement) when zero eligible templates serve the district type post-hard-gate.', 'review', 'medium', NULL, 'server', 'D-232', '2026-05-25 20:36:39', '2026-07-08 08:01:20.010', NULL, '2bfd9945118124da5bcac7c3a16f8c30', 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 ('06FB0TNSRX65YFYFR5RNCM9MCG', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'Gemma architecture-flavor methodology doc (stage1 register + stage2 structured)', 'Methodology doc analogous to the D-223 naming one: stage-1 register selection from wiki prose, stage-2 structured output {tag, allow, block, weight, trauma_decay_rate}. Defines the Gemma prompt + review at GTTR/wiki authoring.
Round 3 reframe: Gemma is a BOUNDED corpus-distillation pass (one read of all wiki -> propose catalog templates -> human-gated), NOT per-body open-vocab generation. Plus core catalog hand-authoring (Miri = cultural meaning + eligibility; Araminta = matching visual_bundle).
Round-3 refinement (2026-05-26): reframed as a FOLLOW-ON extension, not a blocker of the core catalog (1005). Runs AFTER the core hand-authored set exists, to propose additional templates (corpus-distillation, human-gated) toward the 40-45 target. Needs an architecture-flavor Gemma methodology doc (analogous to the naming one) defining the extraction prompt + structured output schema + human-review gate.
---
Refinement (/whats-next 2026-07-07): The stage-2 schema in the description is stale round-2 text — there is no trauma_decay_rate anywhere in schema or code; D-232 round 3 (trait-template model) is authoritative. RESOLVED (user): Gemma proposes TEXT FIELDS ONLY — tag, label, cultural_description, candidate allow_tags/block_tags. Humans set all numeric/eligibility fields with whole-pool knowledge (Miri: gates, base_weight, weight_mods, zone_affinity, corridor_pool/geographic_sector; Araminta: visual_bundle) — the catalog-wide CI guardrails (V-TT-01 pool>=5, V-TT-02 weight<=60%) cannot be satisfied by a per-template extraction pass, so Gemma never sets numbers. Doc target: docs/gemma-architecture-flavor-methodology.md (analogous to docs/gemma-naming-methodology.md). Stage-1 register selection reads the current wiki tree (wiki/star-systems/*/index.md); state that assumption explicitly in the doc — location may move if Q-107 resolves. Q-106 is dissolved: no era-band logic in the doc.', 'done', 'high', NULL, 'server', 'D-232', '2026-05-25 19:11:41', '2026-07-08 13:57:21.366', NULL, '8b15a2a9d07a51002b2acbf662bb228d', 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 ('06FB0TNSRWGDT3AJJE5QD0E3FR', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'Deviation/swerve system — closed-default + rare per-building wildcard', 'Closed vocabulary by default; a rare per-building wildcard draws a COHERENT whole foreign template (cultural-only — function still passes economic hard gates; never an axis-scramble). Two OPPOSED active drivers: foreign-import (another corridor) scaled UP by cosmopolitanism/centrality/transit/Epicenter tier; heritage-callback (the body''s own corridor heritage sub-pool) scaled UP by remoteness/isolation/conservatism. Passive past-vogue holdover from age (D-217 wear). Sparsity escape-hatch = the SAME mechanism triggered by necessity when the closed vocabulary cannot serve a district. Per D-232.
---
Refinement (/whats-next 2026-07-07): Blocked by T-994 (edge added) — the swerve overrides the three-phase draw; implement after it lands, within this batch. RESOLVED (user): resolve driver mappings now. Proposed mappings to REAL fields (integer bps mods, constants in one tunable block):
- foreign-import scale-ups: WorldTier::Epicenter / WorldTier::Passage (transit), dominant_faction == mixed (cosmopolitanism), road/rail-graph hub degree where available in context (centrality; else world_tier proxy).
- heritage-callback scale-ups: remoteness/isolation from star_systems dist_ly / earth_proximity percentile plus absence of Passage tier; conservatism from founding_age_years (older = stronger callback; T-1000 backfilled the field).
- Placeholder wildcard rate: base 100 bps (1%) per building, driver mods multiplicative, hard cap 300 bps; all constants integer bps per D-010 determinism.
- Passive past-vogue holdover rides D-217 wear/era; no new fields.
Data-model prerequisite: extend ArchitectureFlavorRef (server/src/simulation/generator.rs:826, currently a u8 index into trait_selection) to represent an out-of-vocabulary template (e.g. InVocabulary(u8) | Swerve(template_tag)) — the wildcard draws outside the closed K-set by definition.
Sparsity escape-hatch: the SAME mechanism, necessity-triggered at District-dominant assignment time (2048m tier per T-994 refinement) when zero eligible templates serve the district type post-hard-gate.', 'done', 'medium', NULL, 'server', 'D-232', '2026-05-25 20:36:39', '2026-07-08 13:57:21.375', NULL, '299ff8e1df1e007b464e990c4a4d40f1', 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 ('06FB0TNSRWYHR72SC80F5T0TBW', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'allow/block weighted filter at chunk fill over zone-type ObjectTag pool', 'Apply the flavor profile as a filter over the D-142 zone-type ObjectTag baseline pool: block removed, allow weighted up by weight; dominant wins categorical, weighted-avg continuous. Three-layer: body x district x era-band (depth Q-106).
Round 3: this becomes the THREE-PHASE DRAW (body K-draw [K=5/3/1/0 by complexity_tier, SeedChain, coverage-aware] -> district-dominant by zone_affinity -> within-template seed picks) + CI guardrails, per D-232 — not a flat per-body allow/block filter.
---
Refinement (/whats-next 2026-07-07): Paragraph 1 of the description is superseded round-1/2 prose — the "D-142 zone-type ObjectTag baseline pool" is a miscite (D-142 = NPC-behavior zone taxonomy; no such pool exists anywhere) and era-bands were dissolved with Q-106. The round-3 three-phase draw is the whole scope. RESOLVED (user decisions):
1. "District-dominant" = the TRUE D-243 2048m District tier (4 quarters), NOT the 512m Quarter. The District tier has no code representation in the cascade yet — building the District aggregation (grouping quarters, assigning one dominant template per 2048m District) is IN SCOPE for this ticket. The D-222/D-243 district redefinition was never captured into D-232 — amend D-232 with a dated note pinning "district" = the D-243 2048m tier as part of this work.
2. The dominant-template pick runs at GenerateSkeleton (NOT "FillChunk head" — FillChunk is pure/cache-free by design per T-987); store the resolved template on skeleton/block tags.
3. Coverage: thread a body-level district-type-mix aggregate into CityGenerationContext (from settlement placement / the L3-L4 dispatch) so draw_body_vocabulary() is coverage-aware up front (>=1 eligible template per present district type).
4. Seed the body K-draw from SeedChain::for_body(world_seed, body_id) — NOT the per-dispatch chain — so every city/quarter on a body draws the identical closed vocabulary (D-232 invariant).
5. Files: server/src/atlas/skeleton_gen.rs is the primary site — replace the shipped flat assign_block_tags/zone_type_hash flavor_index pick (round-1/2 model) with the three-phase draw. The CI guardrails already exist and pass (V-TT-01/V-TT-02 in tooling/economy-db/economy_import/traits.py) — no importer work needed.
6. geographic_sector remains a SOFT weight, two-part pool join (PR #148 note stands).', 'done', 'medium', NULL, 'server', 'D-232', '2026-05-25 19:11:45', '2026-07-08 13:57:21.375', NULL, '9b26e2b199f409ce4b68b905a8da81a4', 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 ('06FB0TNSRZCG08B0P6PRNQV8KG', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'ObjectTag vocabulary authoring (Miri + Araminta co-maintenance)', 'Author the ObjectTag + exterior-material vocabulary the allow/block lists and D-235 grammar reference. Carried over from the retired generator-arch D-READY-9 requirement. Content task, Miri + Araminta.
---
Refinement (/whats-next 2026-07-07): RESOLVED (user): the shipped 28-tag material palette in wiki/economics/architecture_trait_catalog.toml (T-1005) is RATIFIED as canonical. D-235''s example WallMaterial/RoofForm/FacadeRhythm/StreetSurface token lists must be amended to match shipped reality as part of this ticket. Deliverable = formalize-and-validate: (a) a machine-readable tag registry (proposal: wiki/economics/object_tag_vocabulary.toml, imported by economy_import/traits.py) covering every tag used in allow_tags/block_tags/visual_bundle; (b) importer validation that all catalog tags resolve against the registry; (c) fallback-graph validation absorbed from T-1004 (parked to Phase 5): every specific token declares a generic parent (generic_wall/roof/facade/street) and resolution is acyclic — build-time check. Cite Q-049 (ObjectTag ownership/co-maintenance, still open — Miri: cultural meaning, Araminta: visual) — this ticket''s registry + review flow is the practical answer; close or update Q-049 with it. Q-107: the registry is generator-facing and Q-107-invariant like the catalog table; note the location assumption in the file header.', 'done', 'medium', NULL, 'server', 'D-232', '2026-05-25 19:11:47', '2026-07-08 13:57:21.375', NULL, 'bba85ea7c4e2b53cb09bf1915a68c5ed', 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 ('06FB0TNSRWMZBFV1V6F79VW8S4', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Architecture-flavor pipeline — wiki to generator cultural grammar (D-232)', 'Architecture-domain completion of D-167. (a) Gemma architecture-flavor methodology doc: stage-1 register selection + stage-2 structured output {tag, allow:[ObjectTag], block:[ObjectTag], weight, trauma_decay_rate}, analogous to the D-223 naming methodology. (b) trait_templates catalog + atlas_body_trait_bias bake (see round-3 note) (GTTR+index.md). (c) allow/block weighted filter applied at chunk fill over the D-142 zone-type ObjectTag pool (dominant wins categorical; weighted-avg continuous). (d) ObjectTag vocabulary authoring (Miri+Araminta co-maintenance). Three-layer resolution body x district x era-band (era-band depth = Q-106). NOTE: source-authoring location rides on Q-107 (content consolidation); the table+filter are buildable independently. Formally retires D-104/D-105/D-101-axisA/D-107. Flavor stays purely wiki-authored; economics (D-233) is a parallel channel composing only at the filter.
Round 3 (2026-05-25): D-232 rewritten to the TRAIT-TEMPLATE model (not per-body Gemma arrays). Build: (1) trait_templates table = shared holistic catalog (~35 floor / 40-45 target); two-tier eligibility = integer-bps hard gates (bulk_class/prosperity/ubiquity) + weight mods (role/faction/age/corridor/morphology); corridor (geographic_sector) is a WEIGHT, never a gate. (2) atlas_body_trait_bias (sparse, ~30-40 hero bodies: pin/boost/suppress); RETIRE atlas_body_culture + atlas_body_culture_era. (3) Three-phase draw: body vocabulary K-draw (K=5/3/1/0 by complexity_tier, SeedChain D-224, coverage-aware, pin counts toward K) -> district-dominant by zone_affinity (one template/district, applied whole) -> within-template seed picks. (4) Deviation/swerve system + (5) visual fallback hierarchy = separate tasks. CI guardrails (Nigel): eligible pool >=5 post-gate, no template >60pct weight, bias per-body, catalog grows with corpus. Result trait_selection: Vec<String> on skeleton, re-derivable. Channel-separate from D-233 (what vs how). Era = maintenance/wear (D-217), NOT a tech ladder. Source location -> Q-107.
Scope note (2026-05-26): the catalog CONTENT authoring is split out to story #1005 (blocking). 977 is now the MECHANISM only — tables, three-phase draw, district-dominant, deviation system, fallback hierarchy. #1005 (the ~35-template content) blocks the draw/fill/visual tasks.
Refinement (2026-05-26): READY behind 1006/1005. Mechanism only. trait_templates + atlas_body_trait_bias DDL into import_economics.py MIGRATION_SQL + systems-schema.sql, integer-bps weights throughout. draw_body_vocabulary() inside the GenerateSkeleton Rayon task: hard-gate filter -> weight (mods x bias) -> SeedChain weighted draw of K (5/3/1/0 by ComplexityTier) -> trait_selection on skeleton; pin counts toward K; coverage-aware (>=1 template per present district type, else sparsity escape-hatch). pick_district_dominant() at the FillChunk head (zone_affinity-weighted). CI guardrails in importer validation (pool>=5, weight<=60pct, bias per-body). Swerve (1003) + fallback (1004) are child tasks. SeedChain complete, no change. Files: import_economics.py, generator.rs.
--- From PR #148 review (Tyre): the trait_templates.geographic_sector column is a pool-narrowing hint for baseline/heritage sub-pools and a SOFT weight (never a gate); it composes with weight_mods.geographic_sector. The draw must join both, not treat the column as a gate. (D-232 corridor = two-part pool.)', 'done', 'high', NULL, 'server', 'D-232', '2026-05-25 19:08:55', '2026-07-08 13:57:27.196', NULL, '1f849322586a58890fdc6985f24ddfb5', 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 ('06FB0TNSRZKXHAYMT7Q6WW3XDC', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Layer 5 — Chunk→Tile fill (floors, walls, doors, objects)', 'THE primary gap (generator.rs is stubbed). Generate walkable tiles from the block/chunk skeleton: floors, walls, doors, placed objects, using tile-condition thresholds 0.63/0.43/0.23 (D-217). Output is the walkable world data.
Refinement (round 2, 2026-05-25): Layer-5 fill is the D-230 on-demand derive phase (<5ms shell derivation Void/Wall/FloorSlab/Roof from the cached tags) plus the BuildingExteriorTag visual grammar and material vocabulary (D-235: WallMaterial/RoofForm/FacadeRhythm/StreetSurface, template-filtered (era = maintenance/wear, not a material gate)) plus interstitial and operations-surface fill driven by BulkClass coverage (D-233). Blocked by 957, 976, 977.
Refinement (2026-05-26): READY behind 1006/957/976/977/1005. shell_derive = rectangle-containment + z-range over cached tags (~0.5ms/chunk). FillChunk pre-condition: only after SkeletonGenerated; re-enqueue High if block_tags absent. Wire chunk_streaming.rs to enqueue FillChunk on load-radius entry (no GenerationQueue interaction today). Interstitial token derived from setback_tier (D-235) x position; bulk operations-surface tagged built-infra, not open space. Child 988 = exterior grammar, now 3-step template-filtered (era is wear, not a material gate). Files: gen_queue.rs FillChunk arm, workers/stubs.rs, plugin.rs SkeletonGenerated handler.
---
Refinement (/whats-next 2026-07-08): SCOPE PINNED post-PR #173. T-987 (shell derivation) shipped; remaining T-959 scope = floors/walls/materials + interstitial fill. RESOLVED (user): "placed objects" DESCOPED — no D-record, vocabulary, or code path exists; exterior props arrive with the asset pipeline and interiors are Phase 6 (cascade). Title updated accordingly. Doors stay deferred to T-979/T-998 (BuildingPropertyTag.doors = empty, commented "#979"). Confirmed non-gaps (Si sweep): D-217 thresholds 0.63/0.43/0.23 unchanged, initial_condition frozen at plan time and consumed by T-999 (not here); Atlas tile-layer visualization is T-960''s scope — T-959''s deliverable is DATA only; chunk_streaming.rs FillChunk enqueue wiring is a deliberate Phase-5 deferral gated by T-962 (per T-987 close-out), not this ticket. D-233 operations-surface fill split to a new child task (BulkClass-driven, distinct from T-988''s density/setback-driven interstitial type).', 'backlog', 'high', NULL, 'server', 'D-217', '2026-05-22 17:42:34', '2026-07-08 15:52:56.968', NULL, '2d80b43d71983b54d5ee2010c311cc85', 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 ('06FB0TNSRZWSSHC2S5KPKAZ5QR', 'task', '06FB0TNSRZKXHAYMT7Q6WW3XDC', 'BuildingExteriorTag visual grammar + material vocabulary (template-filtered)', '(architecture_flavor x zone_type x era x density) -> BuildingExteriorTag{wall_material, roof_form, setback_tier, facade_rhythm, color_range}. Vocabulary: WallMaterial/RoofForm/FacadeRhythm/StreetSurface (extends D-228 FloorMaterial). 4-step derivation: flavor filter -> zone bias -> era restrict -> density setback -> interstitial type. Color flavor-bounded/era-adjusted/seed-selected.
---
Refinement (/whats-next 2026-07-08): AUTHORED-DEPTH v1 (user decision — "deliver what we want to deliver in the first run"; pull Miri + Araminta into the ticket for the content catalog). RESOLVED:
1. Derivation is 3-STEP, era-free (D-235 amendment / D-232 reframe): template visual_bundle filter -> zone_type bias -> density setback + interstitial type. Drop the stale "era restrict" step and "era-adjusted" color — era reads as wear via D-217 condition, never a material gate.
2. Phase timing (per the T-994 precedent, same rationale): BuildingExteriorTag is resolved at GenerateSkeleton dispatch/plan time — where context, catalog, and flavor_ref resolution are available — and stored FROZEN on BuildingPropertyTag; FillChunk only reads it (T-987 purity). Swerve(tag) refs resolve against the full catalog at the same point.
3. trait_catalog_reader must be extended to parse visual_bundle (+ the new fields below) — currently explicitly unparsed ("D-235 territory").
4. Vocabulary becomes append-only integer-discriminant Rust enums (WallMaterial/RoofForm/FacadeRhythm/StreetSurface), matching the SeedDomain/DistrictType convention; unknown registry strings parse to the axis generic with a tracing warning (patch tolerance). Registry TOML remains the authoring source (V-TT-03/04 validated).
5. Zone bias is a REAL authored table: per-template zone_type -> token weight overrides (integer bps), authored by Miri (cultural meaning) + Araminta (visual coherence) into the catalog TOML alongside visual_bundle; importer-validated (new V-TT check: referenced tokens exist in the template''s own visual_bundle axis). Uniform fallback where unauthored.
6. color_range is a NUMERIC model now: per-color_register authored integer ranges (e.g. HSV/OKLCH components as integer bps bands, D-010 — no floats), one table keyed by the 28 color_register values, Araminta-authored; per-building seed pick within the template''s register band, stored as integers on BuildingExteriorTag.
7. StreetSurface is IN SCOPE here: derived per street/interstitial run with the same template filter (D-235 "same filter as buildings"), stored alongside the interstitial type.
8. setback_tier: fixed 5-tier vocabulary (zero_lot | tight | standard | generous | campus) derived from BlockSkeleton.density_pct bands (the shipped axis; D-220 DensityClass has no code representation) — thresholds documented as calibration placeholders.
Content deliverables (Miri + Araminta): zone-bias table + color_register numeric bands for all 28 templates/registers. Code deliverables: reader extension, enums, derivation fn at skeleton time, importer validation, tests.', 'backlog', 'medium', NULL, 'server', 'D-235', '2026-05-25 19:11:19', '2026-07-08 15:52:57.844', NULL, '5c1a1bc668ea235f97d6a89ff21b6d45', 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 ('06FBPTXQGAX5GT4PTTBQYGJCV4', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Triage the Phase-4 question set before T-959 starts: Q-102, Q-103, Q-105, Q-109', '(description follows in first append)
---
RECOVERED DESCRIPTION (lost to a same-second create+append LWW tie-break on changelog rebuild; recovered from .pql/changelog/ticket_history/2026-06.sql — pql bug filed upstream): "The remaining Phase-4 spine is T-959 (Layer 5 tile fill) -> T-962 (validation + Phase-5 gate). Four open architecture questions sit directly under that work with no owners: Q-103 (tile-mutator op schema — needed before T-959 Layer 5), Q-102 (cohesion-matrix seam-free variation — needed for tile fill; relates to T-1042), Q-105 (region seasonal/clock state), Q-109 (generation-source dispatch: planet/station/mod/save). Answer or ticket each before T-959 kicks off; Q-103 and Q-102 are the most load-bearing. Also now open from the audit: Q-110 (scale anchoring — blocks the spine ticket) and Q-111 (live-econ coupling — gates T-1007/T-982 sequencing)."
Refinement (/whats-next 2026-07-08): triage dispositions RESOLVED (user):
- Q-102: RESOLVED-IN-PRACTICE — close with a resolution note citing D-246 voxel_mosaic (noise basis/keying/determinism), T-1080 (meso gradients), T-1077 (ladder), and D-228''s categorical FloorMaterial override stack (authored features composite without bleed).
- Q-103: DEFER with dated note — its own text scopes to Phase 5+ save system; T-987 shipped Layer-5 shell fill without mutators (empirical non-blocker). Revisit at Phase-5 save-system kickoff.
- Q-109: DEFER with dated note — planetary path is the only live source; D-229''s 2026-06-05 amendment carved the station interim. Revisit before stations/mods/save-only bodies.
- Q-105: CLAIM THE D-RECORD NOW (user decision) — design the region transient mechanics as part of this ticket: seasonal phase stepping, tidal term, weather term, snow/ice depth rise/fall, crop-cycle cadence, recompute-on-phase-change; grounded in D-243 §3/§4 region-clock structure (T-1078) and the three shipped punts (freeze/snow model, region climate stack, water-body gen). Claim via pql decisions claim D architecture; resolve Q-105 against it.
Deliverables: governance notes for all four Q-records + the new Q-105 D-record. This ticket gates T-959''s start.', 'backlog', 'medium', NULL, 'server', NULL, '2026-06-12 10:40:59', '2026-07-08 15:52:58.293', NULL, 'b6296472a586d43f5a4005acc173ed3d', 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 ('06FB0TNSRZKXHAYMT7Q6WW3XDC', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Layer 5 — Chunk→Tile fill (floors, walls, materials, interstitial)', 'THE primary gap (generator.rs is stubbed). Generate walkable tiles from the block/chunk skeleton: floors, walls, doors, placed objects, using tile-condition thresholds 0.63/0.43/0.23 (D-217). Output is the walkable world data.
Refinement (round 2, 2026-05-25): Layer-5 fill is the D-230 on-demand derive phase (<5ms shell derivation Void/Wall/FloorSlab/Roof from the cached tags) plus the BuildingExteriorTag visual grammar and material vocabulary (D-235: WallMaterial/RoofForm/FacadeRhythm/StreetSurface, template-filtered (era = maintenance/wear, not a material gate)) plus interstitial and operations-surface fill driven by BulkClass coverage (D-233). Blocked by 957, 976, 977.
Refinement (2026-05-26): READY behind 1006/957/976/977/1005. shell_derive = rectangle-containment + z-range over cached tags (~0.5ms/chunk). FillChunk pre-condition: only after SkeletonGenerated; re-enqueue High if block_tags absent. Wire chunk_streaming.rs to enqueue FillChunk on load-radius entry (no GenerationQueue interaction today). Interstitial token derived from setback_tier (D-235) x position; bulk operations-surface tagged built-infra, not open space. Child 988 = exterior grammar, now 3-step template-filtered (era is wear, not a material gate). Files: gen_queue.rs FillChunk arm, workers/stubs.rs, plugin.rs SkeletonGenerated handler.
---
Refinement (/whats-next 2026-07-08): SCOPE PINNED post-PR #173. T-987 (shell derivation) shipped; remaining T-959 scope = floors/walls/materials + interstitial fill. RESOLVED (user): "placed objects" DESCOPED — no D-record, vocabulary, or code path exists; exterior props arrive with the asset pipeline and interiors are Phase 6 (cascade). Title updated accordingly. Doors stay deferred to T-979/T-998 (BuildingPropertyTag.doors = empty, commented "#979"). Confirmed non-gaps (Si sweep): D-217 thresholds 0.63/0.43/0.23 unchanged, initial_condition frozen at plan time and consumed by T-999 (not here); Atlas tile-layer visualization is T-960''s scope — T-959''s deliverable is DATA only; chunk_streaming.rs FillChunk enqueue wiring is a deliberate Phase-5 deferral gated by T-962 (per T-987 close-out), not this ticket. D-233 operations-surface fill split to a new child task (BulkClass-driven, distinct from T-988''s density/setback-driven interstitial type).', 'backlog', 'high', NULL, 'server', 'D-217', '2026-05-22 17:42:34', '2026-07-08 15:53:07.203', NULL, '469603b65fd57bb53f9c1e907f3ffedf', 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 ('06FM4VQCXZ9JEXPCM4ZQD5WXNW', 'task', '06FB0TNSRZKXHAYMT7Q6WW3XDC', 'Operations-surface fill — BulkClass-driven built-infrastructure tag over the non-roofed block remainder (D-233)', 'D-233: for bulk-industry blocks (BulkSolid/BulkLiquid), the non-roofed remainder is operations surface (haul roads, ore pads, conveyor runs, tank berms) — tagged built economic infrastructure, NOT generic interstitial open space. Today skeleton_gen.rs::subdivide_block_footprints computes roofed_coverage_pct from BulkClass but treats 100% of the remainder as generic interstitial (yards/parks/lots); no operations-surface tag exists anywhere. Distinct derivation axis from T-988''s density/setback-driven interstitial TYPE: this is the economic signal (dominant_bulk_class on context). Split out of T-959 during /whats-next refinement 2026-07-08 so it has a visible owner.', 'backlog', 'medium', NULL, 'server', 'D-233', '2026-07-08 15:53:13.711', '2026-07-08 15:53:13.711', NULL, 'b1de043a6d6d9d8191a39ef3a70d9e5b', 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 ('06FB0TNSRZKXHAYMT7Q6WW3XDC', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Layer 5 — Chunk→Tile fill (floors, walls, materials, interstitial)', 'THE primary gap (generator.rs is stubbed). Generate walkable tiles from the block/chunk skeleton: floors, walls, doors, placed objects, using tile-condition thresholds 0.63/0.43/0.23 (D-217). Output is the walkable world data.
Refinement (round 2, 2026-05-25): Layer-5 fill is the D-230 on-demand derive phase (<5ms shell derivation Void/Wall/FloorSlab/Roof from the cached tags) plus the BuildingExteriorTag visual grammar and material vocabulary (D-235: WallMaterial/RoofForm/FacadeRhythm/StreetSurface, template-filtered (era = maintenance/wear, not a material gate)) plus interstitial and operations-surface fill driven by BulkClass coverage (D-233). Blocked by 957, 976, 977.
Refinement (2026-05-26): READY behind 1006/957/976/977/1005. shell_derive = rectangle-containment + z-range over cached tags (~0.5ms/chunk). FillChunk pre-condition: only after SkeletonGenerated; re-enqueue High if block_tags absent. Wire chunk_streaming.rs to enqueue FillChunk on load-radius entry (no GenerationQueue interaction today). Interstitial token derived from setback_tier (D-235) x position; bulk operations-surface tagged built-infra, not open space. Child 988 = exterior grammar, now 3-step template-filtered (era is wear, not a material gate). Files: gen_queue.rs FillChunk arm, workers/stubs.rs, plugin.rs SkeletonGenerated handler.
---
Refinement (/whats-next 2026-07-08): SCOPE PINNED post-PR #173. T-987 (shell derivation) shipped; remaining T-959 scope = floors/walls/materials + interstitial fill. RESOLVED (user): "placed objects" DESCOPED — no D-record, vocabulary, or code path exists; exterior props arrive with the asset pipeline and interiors are Phase 6 (cascade). Title updated accordingly. Doors stay deferred to T-979/T-998 (BuildingPropertyTag.doors = empty, commented "#979"). Confirmed non-gaps (Si sweep): D-217 thresholds 0.63/0.43/0.23 unchanged, initial_condition frozen at plan time and consumed by T-999 (not here); Atlas tile-layer visualization is T-960''s scope — T-959''s deliverable is DATA only; chunk_streaming.rs FillChunk enqueue wiring is a deliberate Phase-5 deferral gated by T-962 (per T-987 close-out), not this ticket. D-233 operations-surface fill split to a new child task (BulkClass-driven, distinct from T-988''s density/setback-driven interstitial type).', 'in_progress', 'high', NULL, 'server', 'D-217', '2026-05-22 17:42:34', '2026-07-08 20:44:50.724', NULL, '421d158cca609e7bb33006da7e561f61', 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 ('06FM4VQCXZ9JEXPCM4ZQD5WXNW', 'task', '06FB0TNSRZKXHAYMT7Q6WW3XDC', 'Operations-surface fill — BulkClass-driven built-infrastructure tag over the non-roofed block remainder (D-233)', 'D-233: for bulk-industry blocks (BulkSolid/BulkLiquid), the non-roofed remainder is operations surface (haul roads, ore pads, conveyor runs, tank berms) — tagged built economic infrastructure, NOT generic interstitial open space. Today skeleton_gen.rs::subdivide_block_footprints computes roofed_coverage_pct from BulkClass but treats 100% of the remainder as generic interstitial (yards/parks/lots); no operations-surface tag exists anywhere. Distinct derivation axis from T-988''s density/setback-driven interstitial TYPE: this is the economic signal (dominant_bulk_class on context). Split out of T-959 during /whats-next refinement 2026-07-08 so it has a visible owner.', 'in_progress', 'medium', NULL, 'server', 'D-233', '2026-07-08 15:53:13.711', '2026-07-08 20:44:50.734', NULL, '3834cff9f3de3b3667315816a26a26c0', 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 ('06FB0TNSRZWSSHC2S5KPKAZ5QR', 'task', '06FB0TNSRZKXHAYMT7Q6WW3XDC', 'BuildingExteriorTag visual grammar + material vocabulary (template-filtered)', '(architecture_flavor x zone_type x era x density) -> BuildingExteriorTag{wall_material, roof_form, setback_tier, facade_rhythm, color_range}. Vocabulary: WallMaterial/RoofForm/FacadeRhythm/StreetSurface (extends D-228 FloorMaterial). 4-step derivation: flavor filter -> zone bias -> era restrict -> density setback -> interstitial type. Color flavor-bounded/era-adjusted/seed-selected.
---
Refinement (/whats-next 2026-07-08): AUTHORED-DEPTH v1 (user decision — "deliver what we want to deliver in the first run"; pull Miri + Araminta into the ticket for the content catalog). RESOLVED:
1. Derivation is 3-STEP, era-free (D-235 amendment / D-232 reframe): template visual_bundle filter -> zone_type bias -> density setback + interstitial type. Drop the stale "era restrict" step and "era-adjusted" color — era reads as wear via D-217 condition, never a material gate.
2. Phase timing (per the T-994 precedent, same rationale): BuildingExteriorTag is resolved at GenerateSkeleton dispatch/plan time — where context, catalog, and flavor_ref resolution are available — and stored FROZEN on BuildingPropertyTag; FillChunk only reads it (T-987 purity). Swerve(tag) refs resolve against the full catalog at the same point.
3. trait_catalog_reader must be extended to parse visual_bundle (+ the new fields below) — currently explicitly unparsed ("D-235 territory").
4. Vocabulary becomes append-only integer-discriminant Rust enums (WallMaterial/RoofForm/FacadeRhythm/StreetSurface), matching the SeedDomain/DistrictType convention; unknown registry strings parse to the axis generic with a tracing warning (patch tolerance). Registry TOML remains the authoring source (V-TT-03/04 validated).
5. Zone bias is a REAL authored table: per-template zone_type -> token weight overrides (integer bps), authored by Miri (cultural meaning) + Araminta (visual coherence) into the catalog TOML alongside visual_bundle; importer-validated (new V-TT check: referenced tokens exist in the template''s own visual_bundle axis). Uniform fallback where unauthored.
6. color_range is a NUMERIC model now: per-color_register authored integer ranges (e.g. HSV/OKLCH components as integer bps bands, D-010 — no floats), one table keyed by the 28 color_register values, Araminta-authored; per-building seed pick within the template''s register band, stored as integers on BuildingExteriorTag.
7. StreetSurface is IN SCOPE here: derived per street/interstitial run with the same template filter (D-235 "same filter as buildings"), stored alongside the interstitial type.
8. setback_tier: fixed 5-tier vocabulary (zero_lot | tight | standard | generous | campus) derived from BlockSkeleton.density_pct bands (the shipped axis; D-220 DensityClass has no code representation) — thresholds documented as calibration placeholders.
Content deliverables (Miri + Araminta): zone-bias table + color_register numeric bands for all 28 templates/registers. Code deliverables: reader extension, enums, derivation fn at skeleton time, importer validation, tests.', 'in_progress', 'medium', NULL, 'server', 'D-235', '2026-05-25 19:11:19', '2026-07-08 20:44:50.734', NULL, '45b8da75039bba5fb1037a6ad8f577ff', 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 ('06FBPTXQGAX5GT4PTTBQYGJCV4', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Triage the Phase-4 question set before T-959 starts: Q-102, Q-103, Q-105, Q-109', '(description follows in first append)
---
RECOVERED DESCRIPTION (lost to a same-second create+append LWW tie-break on changelog rebuild; recovered from .pql/changelog/ticket_history/2026-06.sql — pql bug filed upstream): "The remaining Phase-4 spine is T-959 (Layer 5 tile fill) -> T-962 (validation + Phase-5 gate). Four open architecture questions sit directly under that work with no owners: Q-103 (tile-mutator op schema — needed before T-959 Layer 5), Q-102 (cohesion-matrix seam-free variation — needed for tile fill; relates to T-1042), Q-105 (region seasonal/clock state), Q-109 (generation-source dispatch: planet/station/mod/save). Answer or ticket each before T-959 kicks off; Q-103 and Q-102 are the most load-bearing. Also now open from the audit: Q-110 (scale anchoring — blocks the spine ticket) and Q-111 (live-econ coupling — gates T-1007/T-982 sequencing)."
Refinement (/whats-next 2026-07-08): triage dispositions RESOLVED (user):
- Q-102: RESOLVED-IN-PRACTICE — close with a resolution note citing D-246 voxel_mosaic (noise basis/keying/determinism), T-1080 (meso gradients), T-1077 (ladder), and D-228''s categorical FloorMaterial override stack (authored features composite without bleed).
- Q-103: DEFER with dated note — its own text scopes to Phase 5+ save system; T-987 shipped Layer-5 shell fill without mutators (empirical non-blocker). Revisit at Phase-5 save-system kickoff.
- Q-109: DEFER with dated note — planetary path is the only live source; D-229''s 2026-06-05 amendment carved the station interim. Revisit before stations/mods/save-only bodies.
- Q-105: CLAIM THE D-RECORD NOW (user decision) — design the region transient mechanics as part of this ticket: seasonal phase stepping, tidal term, weather term, snow/ice depth rise/fall, crop-cycle cadence, recompute-on-phase-change; grounded in D-243 §3/§4 region-clock structure (T-1078) and the three shipped punts (freeze/snow model, region climate stack, water-body gen). Claim via pql decisions claim D architecture; resolve Q-105 against it.
Deliverables: governance notes for all four Q-records + the new Q-105 D-record. This ticket gates T-959''s start.', 'in_progress', 'medium', NULL, 'server', NULL, '2026-06-12 10:40:59', '2026-07-08 20:44:50.735', NULL, 'd598f7732eb21ee703709653dd76c509', 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 ('06FMZZ0184R5B8YMFJW5NWPNXW', 'task', '06FB0TNSRZKXHAYMT7Q6WW3XDC', 'Interstitial fill — tile character between building footprints (setback-driven open space vs. built ops-surface) at chunk derive', 'Deferred slice of T-959 (Layer 5 fill), split out during /whats-next review 2026-07-08. T-988 landed BuildingExteriorTag.setback_tier + BlockSkeleton.interstitial_character (OpenSpace|OperationsSurface, D-233 bulk-driven), and FillChunk now reads materials onto Wall/Roof shell voxels. What remains: actually FILL the between-footprint tiles at chunk-derive time — resolve each non-footprint ground tile to its character (setback-driven interstitial type: void/court/garden/plaza/dock_slip/market_pad/open_lawn per D-235; or the block''s OperationsSurface tag for bulk industry per D-233/T-1097).\n\nWHY DEFERRED (Dudley''s rationale, PR review): FillChunk''s work item today carries only Vec<BuildingPropertyTag> (per-building) with zero block-level metadata; setback_tier is per-building on exterior, but ''the gap between buildings'' is block-shaped. And ''what is an interstitial tile inside a 64m chunk derive'' is a real geometry question (the BSP leaves that lost the coverage roll in subdivide_block_footprints? a separate ground-fill pass?) that needs a design pass, not a guess. Nothing consumes FillChunk in production until Phase 5, so this blocks nothing today.\n\nSCOPE: (1) thread block-level metadata (interstitial_character + setback/density) into the FillChunk work item; (2) decide + implement the ground-fill geometry (which chunk tiles are interstitial vs footprint-covered vs street); (3) resolve each to its D-235 interstitial type / D-233 ops-surface tag; (4) stays pure/cache-free (T-987). Write a short brief pinning the geometry model before implementing.', 'backlog', 'medium', NULL, 'server', 'D-235', '2026-07-11 07:02:23.297', '2026-07-11 07:02:23.297', NULL, '686857e9be72d86ffaadb08b37298f50', 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 ('06FB0TNSRZWSSHC2S5KPKAZ5QR', 'task', '06FB0TNSRZKXHAYMT7Q6WW3XDC', 'BuildingExteriorTag visual grammar + material vocabulary (template-filtered)', '(architecture_flavor x zone_type x era x density) -> BuildingExteriorTag{wall_material, roof_form, setback_tier, facade_rhythm, color_range}. Vocabulary: WallMaterial/RoofForm/FacadeRhythm/StreetSurface (extends D-228 FloorMaterial). 4-step derivation: flavor filter -> zone bias -> era restrict -> density setback -> interstitial type. Color flavor-bounded/era-adjusted/seed-selected.
---
Refinement (/whats-next 2026-07-08): AUTHORED-DEPTH v1 (user decision — "deliver what we want to deliver in the first run"; pull Miri + Araminta into the ticket for the content catalog). RESOLVED:
1. Derivation is 3-STEP, era-free (D-235 amendment / D-232 reframe): template visual_bundle filter -> zone_type bias -> density setback + interstitial type. Drop the stale "era restrict" step and "era-adjusted" color — era reads as wear via D-217 condition, never a material gate.
2. Phase timing (per the T-994 precedent, same rationale): BuildingExteriorTag is resolved at GenerateSkeleton dispatch/plan time — where context, catalog, and flavor_ref resolution are available — and stored FROZEN on BuildingPropertyTag; FillChunk only reads it (T-987 purity). Swerve(tag) refs resolve against the full catalog at the same point.
3. trait_catalog_reader must be extended to parse visual_bundle (+ the new fields below) — currently explicitly unparsed ("D-235 territory").
4. Vocabulary becomes append-only integer-discriminant Rust enums (WallMaterial/RoofForm/FacadeRhythm/StreetSurface), matching the SeedDomain/DistrictType convention; unknown registry strings parse to the axis generic with a tracing warning (patch tolerance). Registry TOML remains the authoring source (V-TT-03/04 validated).
5. Zone bias is a REAL authored table: per-template zone_type -> token weight overrides (integer bps), authored by Miri (cultural meaning) + Araminta (visual coherence) into the catalog TOML alongside visual_bundle; importer-validated (new V-TT check: referenced tokens exist in the template''s own visual_bundle axis). Uniform fallback where unauthored.
6. color_range is a NUMERIC model now: per-color_register authored integer ranges (e.g. HSV/OKLCH components as integer bps bands, D-010 — no floats), one table keyed by the 28 color_register values, Araminta-authored; per-building seed pick within the template''s register band, stored as integers on BuildingExteriorTag.
7. StreetSurface is IN SCOPE here: derived per street/interstitial run with the same template filter (D-235 "same filter as buildings"), stored alongside the interstitial type.
8. setback_tier: fixed 5-tier vocabulary (zero_lot | tight | standard | generous | campus) derived from BlockSkeleton.density_pct bands (the shipped axis; D-220 DensityClass has no code representation) — thresholds documented as calibration placeholders.
Content deliverables (Miri + Araminta): zone-bias table + color_register numeric bands for all 28 templates/registers. Code deliverables: reader extension, enums, derivation fn at skeleton time, importer validation, tests.', 'review', 'medium', NULL, 'server', 'D-235', '2026-05-25 19:11:19', '2026-07-11 08:34:23.126', NULL, 'ecb09db587b56ff2b5b53dd1abef0085', 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 ('06FB0TNSRZKXHAYMT7Q6WW3XDC', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Layer 5 — Chunk→Tile fill (floors, walls, materials, interstitial)', 'THE primary gap (generator.rs is stubbed). Generate walkable tiles from the block/chunk skeleton: floors, walls, doors, placed objects, using tile-condition thresholds 0.63/0.43/0.23 (D-217). Output is the walkable world data.
Refinement (round 2, 2026-05-25): Layer-5 fill is the D-230 on-demand derive phase (<5ms shell derivation Void/Wall/FloorSlab/Roof from the cached tags) plus the BuildingExteriorTag visual grammar and material vocabulary (D-235: WallMaterial/RoofForm/FacadeRhythm/StreetSurface, template-filtered (era = maintenance/wear, not a material gate)) plus interstitial and operations-surface fill driven by BulkClass coverage (D-233). Blocked by 957, 976, 977.
Refinement (2026-05-26): READY behind 1006/957/976/977/1005. shell_derive = rectangle-containment + z-range over cached tags (~0.5ms/chunk). FillChunk pre-condition: only after SkeletonGenerated; re-enqueue High if block_tags absent. Wire chunk_streaming.rs to enqueue FillChunk on load-radius entry (no GenerationQueue interaction today). Interstitial token derived from setback_tier (D-235) x position; bulk operations-surface tagged built-infra, not open space. Child 988 = exterior grammar, now 3-step template-filtered (era is wear, not a material gate). Files: gen_queue.rs FillChunk arm, workers/stubs.rs, plugin.rs SkeletonGenerated handler.
---
Refinement (/whats-next 2026-07-08): SCOPE PINNED post-PR #173. T-987 (shell derivation) shipped; remaining T-959 scope = floors/walls/materials + interstitial fill. RESOLVED (user): "placed objects" DESCOPED — no D-record, vocabulary, or code path exists; exterior props arrive with the asset pipeline and interiors are Phase 6 (cascade). Title updated accordingly. Doors stay deferred to T-979/T-998 (BuildingPropertyTag.doors = empty, commented "#979"). Confirmed non-gaps (Si sweep): D-217 thresholds 0.63/0.43/0.23 unchanged, initial_condition frozen at plan time and consumed by T-999 (not here); Atlas tile-layer visualization is T-960''s scope — T-959''s deliverable is DATA only; chunk_streaming.rs FillChunk enqueue wiring is a deliberate Phase-5 deferral gated by T-962 (per T-987 close-out), not this ticket. D-233 operations-surface fill split to a new child task (BulkClass-driven, distinct from T-988''s density/setback-driven interstitial type).', 'review', 'high', NULL, 'server', 'D-217', '2026-05-22 17:42:34', '2026-07-11 08:34:23.130', NULL, '62adad6eba716c26c983f29f5eef701e', 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 ('06FBPTXQGAX5GT4PTTBQYGJCV4', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Triage the Phase-4 question set before T-959 starts: Q-102, Q-103, Q-105, Q-109', '(description follows in first append)
---
RECOVERED DESCRIPTION (lost to a same-second create+append LWW tie-break on changelog rebuild; recovered from .pql/changelog/ticket_history/2026-06.sql — pql bug filed upstream): "The remaining Phase-4 spine is T-959 (Layer 5 tile fill) -> T-962 (validation + Phase-5 gate). Four open architecture questions sit directly under that work with no owners: Q-103 (tile-mutator op schema — needed before T-959 Layer 5), Q-102 (cohesion-matrix seam-free variation — needed for tile fill; relates to T-1042), Q-105 (region seasonal/clock state), Q-109 (generation-source dispatch: planet/station/mod/save). Answer or ticket each before T-959 kicks off; Q-103 and Q-102 are the most load-bearing. Also now open from the audit: Q-110 (scale anchoring — blocks the spine ticket) and Q-111 (live-econ coupling — gates T-1007/T-982 sequencing)."
Refinement (/whats-next 2026-07-08): triage dispositions RESOLVED (user):
- Q-102: RESOLVED-IN-PRACTICE — close with a resolution note citing D-246 voxel_mosaic (noise basis/keying/determinism), T-1080 (meso gradients), T-1077 (ladder), and D-228''s categorical FloorMaterial override stack (authored features composite without bleed).
- Q-103: DEFER with dated note — its own text scopes to Phase 5+ save system; T-987 shipped Layer-5 shell fill without mutators (empirical non-blocker). Revisit at Phase-5 save-system kickoff.
- Q-109: DEFER with dated note — planetary path is the only live source; D-229''s 2026-06-05 amendment carved the station interim. Revisit before stations/mods/save-only bodies.
- Q-105: CLAIM THE D-RECORD NOW (user decision) — design the region transient mechanics as part of this ticket: seasonal phase stepping, tidal term, weather term, snow/ice depth rise/fall, crop-cycle cadence, recompute-on-phase-change; grounded in D-243 §3/§4 region-clock structure (T-1078) and the three shipped punts (freeze/snow model, region climate stack, water-body gen). Claim via pql decisions claim D architecture; resolve Q-105 against it.
Deliverables: governance notes for all four Q-records + the new Q-105 D-record. This ticket gates T-959''s start.', 'review', 'medium', NULL, 'server', NULL, '2026-06-12 10:40:59', '2026-07-11 08:34:23.130', NULL, '8081e7d8a6e0e4a9620dd98b7765275d', 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 ('06FM4VQCXZ9JEXPCM4ZQD5WXNW', 'task', '06FB0TNSRZKXHAYMT7Q6WW3XDC', 'Operations-surface fill — BulkClass-driven built-infrastructure tag over the non-roofed block remainder (D-233)', 'D-233: for bulk-industry blocks (BulkSolid/BulkLiquid), the non-roofed remainder is operations surface (haul roads, ore pads, conveyor runs, tank berms) — tagged built economic infrastructure, NOT generic interstitial open space. Today skeleton_gen.rs::subdivide_block_footprints computes roofed_coverage_pct from BulkClass but treats 100% of the remainder as generic interstitial (yards/parks/lots); no operations-surface tag exists anywhere. Distinct derivation axis from T-988''s density/setback-driven interstitial TYPE: this is the economic signal (dominant_bulk_class on context). Split out of T-959 during /whats-next refinement 2026-07-08 so it has a visible owner.', 'review', 'medium', NULL, 'server', 'D-233', '2026-07-08 15:53:13.711', '2026-07-11 08:34:23.130', NULL, '9e81bcabefcaeefeeff5ab0da3dde4f6', 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 ('06FMZZ0184R5B8YMFJW5NWPNXW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Interstitial fill — tile character between building footprints (setback-driven open space vs. built ops-surface) at chunk derive', 'Deferred slice of T-959 (Layer 5 fill), split out during /whats-next review 2026-07-08. T-988 landed BuildingExteriorTag.setback_tier + BlockSkeleton.interstitial_character (OpenSpace|OperationsSurface, D-233 bulk-driven), and FillChunk now reads materials onto Wall/Roof shell voxels. What remains: actually FILL the between-footprint tiles at chunk-derive time — resolve each non-footprint ground tile to its character (setback-driven interstitial type: void/court/garden/plaza/dock_slip/market_pad/open_lawn per D-235; or the block''s OperationsSurface tag for bulk industry per D-233/T-1097).\n\nWHY DEFERRED (Dudley''s rationale, PR review): FillChunk''s work item today carries only Vec<BuildingPropertyTag> (per-building) with zero block-level metadata; setback_tier is per-building on exterior, but ''the gap between buildings'' is block-shaped. And ''what is an interstitial tile inside a 64m chunk derive'' is a real geometry question (the BSP leaves that lost the coverage roll in subdivide_block_footprints? a separate ground-fill pass?) that needs a design pass, not a guess. Nothing consumes FillChunk in production until Phase 5, so this blocks nothing today.\n\nSCOPE: (1) thread block-level metadata (interstitial_character + setback/density) into the FillChunk work item; (2) decide + implement the ground-fill geometry (which chunk tiles are interstitial vs footprint-covered vs street); (3) resolve each to its D-235 interstitial type / D-233 ops-surface tag; (4) stays pure/cache-free (T-987). Write a short brief pinning the geometry model before implementing.', 'backlog', 'medium', NULL, 'server', 'D-235', '2026-07-11 07:02:23.297', '2026-07-11 12:54:35.373', NULL, 'e85052ff6edeaa5fa18b9811d9be4451', 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 ('06FB0TNSRZWSSHC2S5KPKAZ5QR', 'task', '06FB0TNSRZKXHAYMT7Q6WW3XDC', 'BuildingExteriorTag visual grammar + material vocabulary (template-filtered)', '(architecture_flavor x zone_type x era x density) -> BuildingExteriorTag{wall_material, roof_form, setback_tier, facade_rhythm, color_range}. Vocabulary: WallMaterial/RoofForm/FacadeRhythm/StreetSurface (extends D-228 FloorMaterial). 4-step derivation: flavor filter -> zone bias -> era restrict -> density setback -> interstitial type. Color flavor-bounded/era-adjusted/seed-selected.
---
Refinement (/whats-next 2026-07-08): AUTHORED-DEPTH v1 (user decision — "deliver what we want to deliver in the first run"; pull Miri + Araminta into the ticket for the content catalog). RESOLVED:
1. Derivation is 3-STEP, era-free (D-235 amendment / D-232 reframe): template visual_bundle filter -> zone_type bias -> density setback + interstitial type. Drop the stale "era restrict" step and "era-adjusted" color — era reads as wear via D-217 condition, never a material gate.
2. Phase timing (per the T-994 precedent, same rationale): BuildingExteriorTag is resolved at GenerateSkeleton dispatch/plan time — where context, catalog, and flavor_ref resolution are available — and stored FROZEN on BuildingPropertyTag; FillChunk only reads it (T-987 purity). Swerve(tag) refs resolve against the full catalog at the same point.
3. trait_catalog_reader must be extended to parse visual_bundle (+ the new fields below) — currently explicitly unparsed ("D-235 territory").
4. Vocabulary becomes append-only integer-discriminant Rust enums (WallMaterial/RoofForm/FacadeRhythm/StreetSurface), matching the SeedDomain/DistrictType convention; unknown registry strings parse to the axis generic with a tracing warning (patch tolerance). Registry TOML remains the authoring source (V-TT-03/04 validated).
5. Zone bias is a REAL authored table: per-template zone_type -> token weight overrides (integer bps), authored by Miri (cultural meaning) + Araminta (visual coherence) into the catalog TOML alongside visual_bundle; importer-validated (new V-TT check: referenced tokens exist in the template''s own visual_bundle axis). Uniform fallback where unauthored.
6. color_range is a NUMERIC model now: per-color_register authored integer ranges (e.g. HSV/OKLCH components as integer bps bands, D-010 — no floats), one table keyed by the 28 color_register values, Araminta-authored; per-building seed pick within the template''s register band, stored as integers on BuildingExteriorTag.
7. StreetSurface is IN SCOPE here: derived per street/interstitial run with the same template filter (D-235 "same filter as buildings"), stored alongside the interstitial type.
8. setback_tier: fixed 5-tier vocabulary (zero_lot | tight | standard | generous | campus) derived from BlockSkeleton.density_pct bands (the shipped axis; D-220 DensityClass has no code representation) — thresholds documented as calibration placeholders.
Content deliverables (Miri + Araminta): zone-bias table + color_register numeric bands for all 28 templates/registers. Code deliverables: reader extension, enums, derivation fn at skeleton time, importer validation, tests.', 'done', 'medium', NULL, 'server', 'D-235', '2026-05-25 19:11:19', '2026-07-11 12:54:38.771', NULL, 'e78de3cf2889a25aa80aab7d8bb7241f', 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 ('06FM4VQCXZ9JEXPCM4ZQD5WXNW', 'task', '06FB0TNSRZKXHAYMT7Q6WW3XDC', 'Operations-surface fill — BulkClass-driven built-infrastructure tag over the non-roofed block remainder (D-233)', 'D-233: for bulk-industry blocks (BulkSolid/BulkLiquid), the non-roofed remainder is operations surface (haul roads, ore pads, conveyor runs, tank berms) — tagged built economic infrastructure, NOT generic interstitial open space. Today skeleton_gen.rs::subdivide_block_footprints computes roofed_coverage_pct from BulkClass but treats 100% of the remainder as generic interstitial (yards/parks/lots); no operations-surface tag exists anywhere. Distinct derivation axis from T-988''s density/setback-driven interstitial TYPE: this is the economic signal (dominant_bulk_class on context). Split out of T-959 during /whats-next refinement 2026-07-08 so it has a visible owner.', 'done', 'medium', NULL, 'server', 'D-233', '2026-07-08 15:53:13.711', '2026-07-11 12:54:38.778', NULL, 'de9023ba8bde9ca813b846492a52f54b', 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 ('06FB0TNSRZKXHAYMT7Q6WW3XDC', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Layer 5 — Chunk→Tile fill (floors, walls, materials, interstitial)', 'THE primary gap (generator.rs is stubbed). Generate walkable tiles from the block/chunk skeleton: floors, walls, doors, placed objects, using tile-condition thresholds 0.63/0.43/0.23 (D-217). Output is the walkable world data.
Refinement (round 2, 2026-05-25): Layer-5 fill is the D-230 on-demand derive phase (<5ms shell derivation Void/Wall/FloorSlab/Roof from the cached tags) plus the BuildingExteriorTag visual grammar and material vocabulary (D-235: WallMaterial/RoofForm/FacadeRhythm/StreetSurface, template-filtered (era = maintenance/wear, not a material gate)) plus interstitial and operations-surface fill driven by BulkClass coverage (D-233). Blocked by 957, 976, 977.
Refinement (2026-05-26): READY behind 1006/957/976/977/1005. shell_derive = rectangle-containment + z-range over cached tags (~0.5ms/chunk). FillChunk pre-condition: only after SkeletonGenerated; re-enqueue High if block_tags absent. Wire chunk_streaming.rs to enqueue FillChunk on load-radius entry (no GenerationQueue interaction today). Interstitial token derived from setback_tier (D-235) x position; bulk operations-surface tagged built-infra, not open space. Child 988 = exterior grammar, now 3-step template-filtered (era is wear, not a material gate). Files: gen_queue.rs FillChunk arm, workers/stubs.rs, plugin.rs SkeletonGenerated handler.
---
Refinement (/whats-next 2026-07-08): SCOPE PINNED post-PR #173. T-987 (shell derivation) shipped; remaining T-959 scope = floors/walls/materials + interstitial fill. RESOLVED (user): "placed objects" DESCOPED — no D-record, vocabulary, or code path exists; exterior props arrive with the asset pipeline and interiors are Phase 6 (cascade). Title updated accordingly. Doors stay deferred to T-979/T-998 (BuildingPropertyTag.doors = empty, commented "#979"). Confirmed non-gaps (Si sweep): D-217 thresholds 0.63/0.43/0.23 unchanged, initial_condition frozen at plan time and consumed by T-999 (not here); Atlas tile-layer visualization is T-960''s scope — T-959''s deliverable is DATA only; chunk_streaming.rs FillChunk enqueue wiring is a deliberate Phase-5 deferral gated by T-962 (per T-987 close-out), not this ticket. D-233 operations-surface fill split to a new child task (BulkClass-driven, distinct from T-988''s density/setback-driven interstitial type).', 'done', 'high', NULL, 'server', 'D-217', '2026-05-22 17:42:34', '2026-07-11 12:54:38.779', NULL, '7a71646b2f3490f61fcd9a4ee7f8804a', 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 ('06FBPTXQGAX5GT4PTTBQYGJCV4', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Triage the Phase-4 question set before T-959 starts: Q-102, Q-103, Q-105, Q-109', '(description follows in first append)
---
RECOVERED DESCRIPTION (lost to a same-second create+append LWW tie-break on changelog rebuild; recovered from .pql/changelog/ticket_history/2026-06.sql — pql bug filed upstream): "The remaining Phase-4 spine is T-959 (Layer 5 tile fill) -> T-962 (validation + Phase-5 gate). Four open architecture questions sit directly under that work with no owners: Q-103 (tile-mutator op schema — needed before T-959 Layer 5), Q-102 (cohesion-matrix seam-free variation — needed for tile fill; relates to T-1042), Q-105 (region seasonal/clock state), Q-109 (generation-source dispatch: planet/station/mod/save). Answer or ticket each before T-959 kicks off; Q-103 and Q-102 are the most load-bearing. Also now open from the audit: Q-110 (scale anchoring — blocks the spine ticket) and Q-111 (live-econ coupling — gates T-1007/T-982 sequencing)."
Refinement (/whats-next 2026-07-08): triage dispositions RESOLVED (user):
- Q-102: RESOLVED-IN-PRACTICE — close with a resolution note citing D-246 voxel_mosaic (noise basis/keying/determinism), T-1080 (meso gradients), T-1077 (ladder), and D-228''s categorical FloorMaterial override stack (authored features composite without bleed).
- Q-103: DEFER with dated note — its own text scopes to Phase 5+ save system; T-987 shipped Layer-5 shell fill without mutators (empirical non-blocker). Revisit at Phase-5 save-system kickoff.
- Q-109: DEFER with dated note — planetary path is the only live source; D-229''s 2026-06-05 amendment carved the station interim. Revisit before stations/mods/save-only bodies.
- Q-105: CLAIM THE D-RECORD NOW (user decision) — design the region transient mechanics as part of this ticket: seasonal phase stepping, tidal term, weather term, snow/ice depth rise/fall, crop-cycle cadence, recompute-on-phase-change; grounded in D-243 §3/§4 region-clock structure (T-1078) and the three shipped punts (freeze/snow model, region climate stack, water-body gen). Claim via pql decisions claim D architecture; resolve Q-105 against it.
Deliverables: governance notes for all four Q-records + the new Q-105 D-record. This ticket gates T-959''s start.', 'done', 'medium', NULL, 'server', NULL, '2026-06-12 10:40:59', '2026-07-11 12:54:38.779', NULL, 'd23c1f352027727a0ba8f231fffcf96e', 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 ('06FN2TV9HWGK5P7MJ9REHFKVFC', 'initiative', '06FBPPMZNNEV052DBYYY3A897C', 'Claude estate cleanup — 2026-07-02 .claude review', 'Execution of CLAUDE-CLEANUP.md (repo-root brief, adversarially verified 2026-07-02): ~60 findings across settings, skills, tooling scripts, and the agent roster. Each finding re-verified against the current tree before edit (repo moved since 2026-07-02). Security item (GEMINI_API_KEY) done first — key invalidated by user + replaced with KEY_GOES_HERE placeholder in the untracked settings.local.json. Shipped on branch claude-estate-cleanup via /pr-process.', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-11 13:43:41.711', '2026-07-11 13:43:41.711', NULL, '997b851cc8a02b228a5b4f819d8df14a', 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 ('06FN2TWG2HF2YHQFJFTSGTYS48', 'epic', '06FN2TV9HWGK5P7MJ9REHFKVFC', 'Settings & security hygiene (CLAUDE-CLEANUP §0-1)', '§0 security (key done) + §0.2 scheduled_tasks.lock gitignore; §1 settings.json/settings.local.json permission pruning, deny-list syntax normalization (never weaken deny rules), sqlite3 contradiction, blanket-allow review, promote stable allows into settings.json.', 'backlog', 'high', NULL, 'server', NULL, '2026-07-11 13:43:51.572', '2026-07-11 13:43:51.572', NULL, 'b2b621107a891de30880be9719f35209', 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 ('06FN2TWSXH3XF64SYP4FS89P7G', 'epic', '06FN2TV9HWGK5P7MJ9REHFKVFC', 'Remove skills promoted to user scope (CLAUDE-CLEANUP §2)', 'Delete .claude/skills/{d2-diagram,frame0-wireframe,skill-create,clide} after verifying user-scope copies at ~/.claude/skills/ and no .pql-install.json marker; move any repo-specific tailoring delta into docs/ first.', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-11 13:43:54.092', '2026-07-11 13:43:54.092', NULL, 'fb28d6960f0a8d7b237ee59902cfb4cf', 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 ('06FN2TX5V43575PG96F14AH0N0', 'epic', '06FN2TV9HWGK5P7MJ9REHFKVFC', 'Workflow-skill correctness (CLAUDE-CLEANUP §3-7)', '§3 whats-next pql-durability + --status footgun ports; §4 git-commit stale attribution trailer; §5 workshop-start (Task→Agent, write-req, SI role); §6 pr-review (sprint narrative, main-path guard, Write grant); §7 pr-process (dead /pr-push refs, orphan-check regex, stale watch-list, bash hygiene).', 'backlog', 'high', NULL, 'server', NULL, '2026-07-11 13:43:57.145', '2026-07-11 13:43:57.145', NULL, '96e715c3054eafdf9c7884dbbfe3df59', 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 ('06FN2TXFEDCYPSARF3DYRN9VAG', 'epic', '06FN2TV9HWGK5P7MJ9REHFKVFC', 'Asset & tooling-skill accuracy (CLAUDE-CLEANUP §8-14)', '§8 atlas (removed populate, body-ID naming); §9 glb-gen; §10 image-gen (indent crash both copies — real bug); §11 audio-gen; §12 sprite-gen; §13 bug-report; §14 ticket. Doc accuracy + a few genuine script bugs.', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-11 13:43:59.603', '2026-07-11 13:43:59.603', NULL, '07722e327c8276c06a0b2a2761483b58', 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 ('06FN2TXSWVJGQWV6Q2RF5EF35W', 'epic', '06FN2TV9HWGK5P7MJ9REHFKVFC', 'Agent roster refresh (CLAUDE-CLEANUP §15)', 'STANDBY flips (dudley/stig/justine/mellanie/oscar misrouting active work); DISCUSSION.md dead-pointer sweep (8 profiles + README); tool grants vs duties; routing disambiguation; araminta asset-gen section (wholly wrong); inigo half-integration; individual fixes (clerk, gore, burnelli-sheldon); briefings tooling/db/ticket sweep.', 'backlog', 'high', NULL, 'server', NULL, '2026-07-11 13:44:02.278', '2026-07-11 13:44:02.278', NULL, 'ed275843ab6138769a3005c1790d2b15', 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 ('06FN2VQEJ20NM1RS413DEXCN98', 'task', '06FN2TX5V43575PG96F14AH0N0', 'Workflow-skill fixes — whats-next/git-commit/workshop-start/pr-review/pr-process (§3-7)', NULL, 'backlog', 'high', NULL, 'server', NULL, '2026-07-11 13:47:32.368', '2026-07-11 13:47:32.368', NULL, 'ecaafe50cbefbc32cad7e12fb10466df', 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 ('06FN2VTA380WZT33Q07TJES7Q8', 'task', '06FN2TXFEDCYPSARF3DYRN9VAG', 'Asset/tooling-skill fixes — atlas/glb-gen/image-gen/audio-gen/sprite-gen/bug-report/ticket (§8-14)', NULL, 'backlog', 'medium', NULL, 'server', NULL, '2026-07-11 13:47:55.802', '2026-07-11 13:47:55.802', NULL, '582da85d0dcb9d760114d48f303bb7bd', 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 ('06FN2VXFRB6K4TDBEPVH0P8XHG', 'task', '06FN2TXSWVJGQWV6Q2RF5EF35W', 'Agent roster refresh — STANDBY flips, DISCUSSION.md sweep, routing/grants, araminta/inigo, briefings (§15)', NULL, 'backlog', 'high', NULL, 'server', NULL, '2026-07-11 13:48:21.826', '2026-07-11 13:48:21.826', NULL, 'cf1ae738bf61c3b7d1fe846a7f5f796c', 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 ('06FNR3FCNH2EMGJMQ81MFG8SCR', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Smoke-test image-gen --aspect against the paid Gemini API', 'Estate cleanup (T-1099/PR #175) implemented generationConfig.imageConfig.aspectRatio in tooling/db/image_connector.py per official Google docs, but could not live-test without a paid API call. Run one real generate with --aspect 16:9 and confirm the output honours it. Needs a valid GEMINI_API_KEY (current one invalidated; placeholder in settings.local.json).', 'backlog', 'low', NULL, 'assets', NULL, '2026-07-13 15:17:24.268', '2026-07-13 15:17:24.268', NULL, '466731fd5ad615714ab8508451319dae', 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 ('06FNR3FN8D0GVXPSPV9SY7PEY0', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Decide: workshop per-round scout critic (adversarial reviewer)', 'workshop-start''s IMPROVEMENTS.md (folded+deleted in T-1099/PR #175) proposed a candidate feature: an independent adversarial ''scout'' agent run between round-output and user-checkpoint. Apparently trialed once (docs/workshops/system-economic-specialization/scout-round2.md) but never documented in the skill. Product call: adopt formally into workshop-start SKILL.md, or drop the idea.', 'backlog', 'low', NULL, 'meta', NULL, '2026-07-13 15:17:26.468', '2026-07-13 15:17:26.468', NULL, 'f1978e6cb135aeb3b9da46902ee077e7', 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 ('06FNR3GM4RPPCJFH8VS3CDN6SG', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Body-ID naming standard: moon suffix -m{n} vs bare -{n}', 'Atlas body IDs split ~208 vs ~228 (of 436) between -m{n} and bare -{n} moon suffixes. T-1099/PR #175 documented both forms honestly in atlas/references/naming-brief.md rather than picking a winner. Decide the canonical form and whether to migrate existing IDs (StableId determinism implications — check Gauntlet fixtures before renaming anything).', 'backlog', 'low', NULL, 'content', NULL, '2026-07-13 15:17:34.374', '2026-07-13 15:17:34.374', NULL, '3a577d0966cf78e5aeed15c37a2d19c9', 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 ('06FNR3GYTN814QG6WWYNJH7QAR', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'renderer/README.md staleness: --headless claim + retired D-244 sprite path', 'Out of scope for T-1099/PR #175 (file not in the brief''s named list): renderer/README.md line ~66 claims --headless works for the sprite renderer (it does not — render.sh now hard-requires DISPLAY/xvfb-run), and its ''Adding a New Model'' step 6 still says to copy 64px variants to client/assets/sprites/, a path retired by D-244. Fix both.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-13 15:17:37.109', '2026-07-13 15:17:37.109', NULL, '0c57f07c9d0b3e1e8ab255d6fa5cc1d7', 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 ('06FN2TWG2HF2YHQFJFTSGTYS48', 'epic', '06FN2TV9HWGK5P7MJ9REHFKVFC', 'Settings & security hygiene (CLAUDE-CLEANUP §0-1)', '§0 security (key done) + §0.2 scheduled_tasks.lock gitignore; §1 settings.json/settings.local.json permission pruning, deny-list syntax normalization (never weaken deny rules), sqlite3 contradiction, blanket-allow review, promote stable allows into settings.json.', 'review', 'high', NULL, 'server', NULL, '2026-07-11 13:43:51.572', '2026-07-13 15:17:49.938', NULL, 'bc0ba2bc2f8ccf55b9bf71abfb75c51b', 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 ('06FN2TX5V43575PG96F14AH0N0', 'epic', '06FN2TV9HWGK5P7MJ9REHFKVFC', 'Workflow-skill correctness (CLAUDE-CLEANUP §3-7)', '§3 whats-next pql-durability + --status footgun ports; §4 git-commit stale attribution trailer; §5 workshop-start (Task→Agent, write-req, SI role); §6 pr-review (sprint narrative, main-path guard, Write grant); §7 pr-process (dead /pr-push refs, orphan-check regex, stale watch-list, bash hygiene).', 'review', 'high', NULL, 'server', NULL, '2026-07-11 13:43:57.145', '2026-07-13 15:17:49.946', NULL, '59d9a61c9d7b633000b036eacf8eba8b', 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 ('06FN2TWSXH3XF64SYP4FS89P7G', 'epic', '06FN2TV9HWGK5P7MJ9REHFKVFC', 'Remove skills promoted to user scope (CLAUDE-CLEANUP §2)', 'Delete .claude/skills/{d2-diagram,frame0-wireframe,skill-create,clide} after verifying user-scope copies at ~/.claude/skills/ and no .pql-install.json marker; move any repo-specific tailoring delta into docs/ first.', 'review', 'medium', NULL, 'server', NULL, '2026-07-11 13:43:54.092', '2026-07-13 15:17:49.946', NULL, '5fa67e8de02dee9ba91afd5b5bd7ed2b', 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 ('06FN2TXFEDCYPSARF3DYRN9VAG', 'epic', '06FN2TV9HWGK5P7MJ9REHFKVFC', 'Asset & tooling-skill accuracy (CLAUDE-CLEANUP §8-14)', '§8 atlas (removed populate, body-ID naming); §9 glb-gen; §10 image-gen (indent crash both copies — real bug); §11 audio-gen; §12 sprite-gen; §13 bug-report; §14 ticket. Doc accuracy + a few genuine script bugs.', 'review', 'medium', NULL, 'server', NULL, '2026-07-11 13:43:59.603', '2026-07-13 15:17:49.946', NULL, '9ea395c77dd764174b5b649fdde64b72', 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 ('06FN2TXSWVJGQWV6Q2RF5EF35W', 'epic', '06FN2TV9HWGK5P7MJ9REHFKVFC', 'Agent roster refresh (CLAUDE-CLEANUP §15)', 'STANDBY flips (dudley/stig/justine/mellanie/oscar misrouting active work); DISCUSSION.md dead-pointer sweep (8 profiles + README); tool grants vs duties; routing disambiguation; araminta asset-gen section (wholly wrong); inigo half-integration; individual fixes (clerk, gore, burnelli-sheldon); briefings tooling/db/ticket sweep.', 'review', 'high', NULL, 'server', NULL, '2026-07-11 13:44:02.278', '2026-07-13 15:17:49.947', NULL, '2094742d4bb4cb22a697168100edac0f', 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 ('06FN2VTA380WZT33Q07TJES7Q8', 'task', '06FN2TXFEDCYPSARF3DYRN9VAG', 'Asset/tooling-skill fixes — atlas/glb-gen/image-gen/audio-gen/sprite-gen/bug-report/ticket (§8-14)', NULL, 'review', 'medium', NULL, 'server', NULL, '2026-07-11 13:47:55.802', '2026-07-13 15:17:49.947', NULL, '219786af9378828f078a02274a3f37b2', 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 ('06FN2VQEJ20NM1RS413DEXCN98', 'task', '06FN2TX5V43575PG96F14AH0N0', 'Workflow-skill fixes — whats-next/git-commit/workshop-start/pr-review/pr-process (§3-7)', NULL, 'review', 'high', NULL, 'server', NULL, '2026-07-11 13:47:32.368', '2026-07-13 15:17:49.947', NULL, 'b2d47fcc2448a7ceca02cb9b1857117b', 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 ('06FN2VXFRB6K4TDBEPVH0P8XHG', 'task', '06FN2TXSWVJGQWV6Q2RF5EF35W', 'Agent roster refresh — STANDBY flips, DISCUSSION.md sweep, routing/grants, araminta/inigo, briefings (§15)', NULL, 'review', 'high', NULL, 'server', NULL, '2026-07-11 13:48:21.826', '2026-07-13 15:17:49.948', NULL, 'b04ba9cf8016b0f4cf2a1e96b4f34301', 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 ('06FN2VQEJ20NM1RS413DEXCN98', 'task', '06FN2TX5V43575PG96F14AH0N0', 'Workflow-skill fixes — whats-next/git-commit/workshop-start/pr-review/pr-process (§3-7)', NULL, 'done', 'high', NULL, 'server', NULL, '2026-07-11 13:47:32.368', '2026-07-13 20:52:24.184', NULL, '85dd1a4bc5f8d5d4a594014ca69ecf01', 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 ('06FN2VTA380WZT33Q07TJES7Q8', 'task', '06FN2TXFEDCYPSARF3DYRN9VAG', 'Asset/tooling-skill fixes — atlas/glb-gen/image-gen/audio-gen/sprite-gen/bug-report/ticket (§8-14)', NULL, 'done', 'medium', NULL, 'server', NULL, '2026-07-11 13:47:55.802', '2026-07-13 20:52:24.191', NULL, '8b1f9ddd21e941380b61c0f713ed90a8', 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 ('06FN2TWSXH3XF64SYP4FS89P7G', 'epic', '06FN2TV9HWGK5P7MJ9REHFKVFC', 'Remove skills promoted to user scope (CLAUDE-CLEANUP §2)', 'Delete .claude/skills/{d2-diagram,frame0-wireframe,skill-create,clide} after verifying user-scope copies at ~/.claude/skills/ and no .pql-install.json marker; move any repo-specific tailoring delta into docs/ first.', 'done', 'medium', NULL, 'server', NULL, '2026-07-11 13:43:54.092', '2026-07-13 20:52:24.192', NULL, '111bf5534734af48859f4c73c20eb7b5', 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 ('06FN2VXFRB6K4TDBEPVH0P8XHG', 'task', '06FN2TXSWVJGQWV6Q2RF5EF35W', 'Agent roster refresh — STANDBY flips, DISCUSSION.md sweep, routing/grants, araminta/inigo, briefings (§15)', NULL, 'done', 'high', NULL, 'server', NULL, '2026-07-11 13:48:21.826', '2026-07-13 20:52:24.192', NULL, '5a051468c5943979afabfca0085abf89', 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 ('06FN2TWG2HF2YHQFJFTSGTYS48', 'epic', '06FN2TV9HWGK5P7MJ9REHFKVFC', 'Settings & security hygiene (CLAUDE-CLEANUP §0-1)', '§0 security (key done) + §0.2 scheduled_tasks.lock gitignore; §1 settings.json/settings.local.json permission pruning, deny-list syntax normalization (never weaken deny rules), sqlite3 contradiction, blanket-allow review, promote stable allows into settings.json.', 'done', 'high', NULL, 'server', NULL, '2026-07-11 13:43:51.572', '2026-07-13 20:52:24.192', NULL, '61b8549d44bdd8d302f3066e26ae6a70', 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 ('06FN2TXSWVJGQWV6Q2RF5EF35W', 'epic', '06FN2TV9HWGK5P7MJ9REHFKVFC', 'Agent roster refresh (CLAUDE-CLEANUP §15)', 'STANDBY flips (dudley/stig/justine/mellanie/oscar misrouting active work); DISCUSSION.md dead-pointer sweep (8 profiles + README); tool grants vs duties; routing disambiguation; araminta asset-gen section (wholly wrong); inigo half-integration; individual fixes (clerk, gore, burnelli-sheldon); briefings tooling/db/ticket sweep.', 'done', 'high', NULL, 'server', NULL, '2026-07-11 13:44:02.278', '2026-07-13 20:52:24.193', NULL, '39e747d4d0b1db8592907b8cdf78c848', 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 ('06FN2TX5V43575PG96F14AH0N0', 'epic', '06FN2TV9HWGK5P7MJ9REHFKVFC', 'Workflow-skill correctness (CLAUDE-CLEANUP §3-7)', '§3 whats-next pql-durability + --status footgun ports; §4 git-commit stale attribution trailer; §5 workshop-start (Task→Agent, write-req, SI role); §6 pr-review (sprint narrative, main-path guard, Write grant); §7 pr-process (dead /pr-push refs, orphan-check regex, stale watch-list, bash hygiene).', 'done', 'high', NULL, 'server', NULL, '2026-07-11 13:43:57.145', '2026-07-13 20:52:24.193', NULL, '6ab665eeecfcd3e883e269eb8fea5295', 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 ('06FN2TXFEDCYPSARF3DYRN9VAG', 'epic', '06FN2TV9HWGK5P7MJ9REHFKVFC', 'Asset & tooling-skill accuracy (CLAUDE-CLEANUP §8-14)', '§8 atlas (removed populate, body-ID naming); §9 glb-gen; §10 image-gen (indent crash both copies — real bug); §11 audio-gen; §12 sprite-gen; §13 bug-report; §14 ticket. Doc accuracy + a few genuine script bugs.', 'done', 'medium', NULL, 'server', NULL, '2026-07-11 13:43:59.603', '2026-07-13 20:52:24.193', NULL, '7632ec25c23fd0117a631739ce8e09fd', 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 ('06FN2TV9HWGK5P7MJ9REHFKVFC', 'initiative', '06FBPPMZNNEV052DBYYY3A897C', 'Claude estate cleanup — 2026-07-02 .claude review', 'Execution of CLAUDE-CLEANUP.md (repo-root brief, adversarially verified 2026-07-02): ~60 findings across settings, skills, tooling scripts, and the agent roster. Each finding re-verified against the current tree before edit (repo moved since 2026-07-02). Security item (GEMINI_API_KEY) done first — key invalidated by user + replaced with KEY_GOES_HERE placeholder in the untracked settings.local.json. Shipped on branch claude-estate-cleanup via /pr-process.', 'done', 'medium', NULL, 'server', NULL, '2026-07-11 13:43:41.711', '2026-07-13 20:52:30.115', NULL, '73d885d748f00c844c1318a2f7bff504', 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 ('06FNTXEM9NM5M5SX9WQAAA40SC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Design: coarse quarter-footprint Atlas layer (L4 skeleton on the planetary map)', 'Si audit of T-960 (2026-07-13): L4 quarter/block skeletons (T-957/T-976) auto-generate server-side into BodyWorldState.quarters, but carry full per-block zoning/street/tag detail — too fine for a planetary map, and unlike the district grid there is no existing coarse shape to reuse. DESIGN ticket (Araminta encoding + Tyre data shape) before any wiring: define a QuarterFootprintLayer (city outline + aggregate stats?) for AtlasLayerResponse and its overlay encoding per the D-226 amended (2026-07-13) overlay pattern. Wiring is a follow-up once the design is recorded.', 'backlog', 'medium', NULL, 'client', 'D-226', '2026-07-13 21:50:30.989', '2026-07-13 21:50:30.989', NULL, '99f810ab70d0f9d81eea341aadba9729', 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 ('06FNTXF0JB8E6PDQRASQ18E9ER', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire region climate stack into the cascade (CascadeLayer::Region + proxy exposure)', 'Si audit of T-960 (2026-07-13): region_profile.rs (T-1078 — RegionProfile, derive_regions_for_body, region_baseline_at_district) is implemented and unit-tested but never called from cascade.rs: no CascadeLayer::Region variant, nothing on BodyWorldState, nothing in the layer proxy. Prerequisite for any climate Atlas overlay (explicitly out of T-960 scope per 2026-07-13 ruling): add the cascade layer, populate BodyWorldState, expose via AtlasLayerResponse following the T-1046 district-grid precedent. The climate OVERLAY itself is a small follow-up once this serves data.', 'backlog', 'medium', NULL, 'server', 'D-243', '2026-07-13 21:50:34.130', '2026-07-13 21:50:34.130', NULL, 'a4acf7e00688756a60312d3c6ef4f4e3', 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 ('06FB0TNSRZ8BFVNWDH17CMH1YM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Per-layer Atlas map viewer (generation progress viewer)', 'Render each cascade layer as a MAP in the implant Atlas (D-191, the Phase 3 viewer — D-166 amendment): terrain → water → biome → settlements → political zones → quarter/block skeleton → tiles. This is the progress-viewer deliverable; NO in-world rendering. Grows as each layer lands.
---
Refinement (2026-05-23, /whats-next):
- DATA TRANSPORT (resolves Q-098): NO bake — baking derived layer data makes mods second-class. A server-side FILE STREAM PROXY: resolves a body source files across base + mod paths, runs the deterministic cascade on demand (run_cascade, LRU-cached per D-203, ~45ms), and streams serialized layer data to the Godot client over IPC. First-party and mod bodies use the identical path; no pre-baked derived artifacts. Protocol/stream spec designed with Tyre and recorded in a D-record resolving Q-098.
- UI: layer selector in the existing regional Atlas view; layers are ADDITIVE overlays (toggle heightmap + rivers + attractors + sub-biome together and composite), not one-at-a-time — matching the existing overlay-bar model (D-191).
- SCOPE NOW: only Layer 0 (heightmap) + Layer 1 (rivers/basins/attractors/sub-biome) exist; the viewer grows as layers land (D-166). Acceptance: Layer 0 + Layer 1 render as additive, toggleable overlays for a body, fed by the proxy.
---
Design (D-226, Araminta, 2026-05-24) — human-visual layer of the inspection harness. Extend OVERLAY_DEFS / AtlasOverlayBar with a generation overlay group (second row) + a left-side legend panel; do NOT build a separate viewer. Encoding: relief base; rivers as blue polylines (mouths = double-circle markers, confluences where in-degree >= 3); basin fills very translucent + thin boundary polylines; attractors as markers where SHAPE encodes the 7 AttractorTypes (circle/half-circle/diamond/down-tri/up-tri/hollow-circle/square) and COLOR encodes sub-biome (shape carries identity in headless/monochrome; color is additive); size scales with strength, skip strength < 0.15. Stable button ids gen_l0_heightmap / gen_l1_rivers / gen_l1_basins / gen_l1_attractors; start pending/locked, unlock+activate via notify_gen_layer_ready(id) as data arrives (grows as layers land, D-166). Consumes the live proxy (#969); the agent channel (#971) drives this same UI.
Remaining to close (Si sweep 2026-06-07): add the gen_l0_heightmap overlay to OVERLAY_DEFS + render path in atlas_viewer.gd / atlas_marker_overlay.gd — required by the D-226/T-960 acceptance criteria but currently absent. Layer-1 overlays (rivers/basins/attractors) + proxy wire + pending indicator are already implemented; this Layer-0 overlay is the sole remaining piece.
---
Refinement (2026-07-13, /whats-next Si audit + Jeroen rulings) — RESCOPE. The 2026-06-07 scope note is obsolete in both directions:
DONE ALREADY (record, don''t rebuild):
- L1 overlays (gen_l1_rivers/basins/attractors) + proxy wire + pending indicator: shipped.
- District-morphology overlay (gen_district "MRPH"): shipped under T-1046 via AtlasLayerResponse.district_grid — the wiring precedent for everything below.
- gen_l0_heightmap: DROPPED — relief already renders via the always-on terrain overlay (D-191 §7). D-226 amended (2026-07-13) accordingly.
IN SCOPE NOW (this ticket):
1. gen_l2_roads overlay — L2 road/rail graph (T-1038) is computed on every AnalyzeBody (gen_queue.rs runs up_to=RoadGraph) but never serialized: add RoadGraphLayer field to AtlasLayerResponse (layer_proxy.rs), protocol.gd decode, overlay rendering (edges colored by MaintenanceAuthority, junction markers).
2. gen_l3_settlements overlay — wire T-955''s computed CityPlacement positions through the proxy (new SettlementLayer field, exact T-1046 precedent) so cities render on generated bodies for the first time (Jeroen ruling: wire positions now; T-949 stays boundary-cleanup).
3. Left-side legend panel — D-226 item (3) deliverable, never built (shape/color key for attractor types + sub-biomes + the new overlays).
4. Doc-comment fix: layer_proxy.rs:31-34 claims cascade runs "through CascadeLayer::Settlement" — actually RoadGraph (gen_queue.rs:416); client''s up_to field is ignored by the server. One-line correction.
OUT OF SCOPE (explicitly, per 2026-07-13 rulings):
- L4 quarter footprints → design ticket T-1112 (needs coarse-shape design first).
- Region climate → prerequisite wiring ticket T-1113 (not in cascade at all yet).
- L5 chunk/tile fill → NEVER an Atlas map layer (D-191/D-226 amendments 2026-07-13; harness-verified, Phase-5 in-world).
- markers.json migration / Sol → T-949 / T-1073 (D-236: Sol excluded from deterministic runs, access capped at Oort gateway).
Overlay mechanism: follow the shipped always-toggleable pattern (D-226 as amended), NOT the old lock/notify spec.', 'backlog', 'high', NULL, 'client', 'D-191', '2026-05-22 17:42:34', '2026-07-13 21:53:23.042', NULL, 'c18ab622ab35c728633f8b1126ee8fa1', 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 ('06FB0TNSRW2ZSPJ3D9NEZ6JBW8', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas data delivery — replace client-side file reads with server API calls', 'The client currently reads markers.json and star_map_data.json directly from the filesystem. This bypasses the server layer entirely — the client should request all game data through IPC calls to the Rust backend.
Implementation can be simple: the server exposes a query/proxy endpoint that reads the JSON file or runs a DB query and returns the result. The client asks, the server responds. No complex protocol needed — just the boundary enforcement that the client never reads files or DB directly.
Affects: atlas app (reach, system, planet, regional screens), economics monitor. The server-side handler can start as a thin proxy (read file, return contents) and evolve into perception-filtered queries (D-012) later.
Re-parented under Phase 4 epic per cascade refocus 2026-06-12 — atlas data delivery underpins viewing cascade layers through the implant Atlas.
---
Refinement (2026-07-13, /whats-next Si audit + Jeroen rulings):
SCOPE: the ~2,398 generated (non-Sol) bodies + star_map_data.json only.
- SOL EXCLUDED (Jeroen): Sol stays out of all deterministic runs; player access is lore-capped at the Oort-cloud gateway station — see D-236 as amended 2026-07-13. Sol''s client-side legacy markers.json read (atlas_viewer.gd::_load_markers) STAYS as the documented D-223/D-236 exception until T-1073 (gated on Q-107). Do not repeat T-1073''s implement→break→revert cycle.
- CITATION FIX: the description''s "perception-filtered queries (D-012)" should read D-010 (information boundaries). D-012 is chunk-based map architecture — unrelated.
CURRENT READ INVENTORY (verified 2026-07-13):
1. atlas_viewer.gd::_load_markers (~436-467) — markers.json via FileAccess. For non-Sol bodies this is names-only data (D-223); replace with a names request over the bridge (server already reads atlas_city_names via CityContextReader). Positions come from T-960''s SettlementLayer work (separate ticket, same branch).
2. system_index.gd::get_sorted_systems — star_map_data.json, consumed by atlas_app.gd:150 (Reach screen) + overview_screen.gd:114 (economics monitor selector; its only direct read). star_map_data.json is a build-time bake (tooling/generate-star-map-data.py); the server has NO existing reader — a thin proxy (server reads the same JSON, returns it) is acceptable per the ticket.
3. OUT OF SCOPE (deliberate, D-225): reliefmap.png / wiki globe image loads stay client-side.
PROTOCOL PATTERN: structural disambiguation on the existing IPC stream (Inbound enum: Inputs vs AtlasRequest). Reach-level star-map data is not per-body/cascade-shaped — add NEW request/response types (e.g. StarMapRequest → StarMapResponse), do not shoehorn into AtlasLayerResponse. Per-body names ride the atlas path.', 'backlog', 'medium', NULL, 'client', NULL, '2026-05-03 14:14:23', '2026-07-13 21:53:24.830', NULL, 'f892a5bfa7ef8f48a0a94defb9745ef5', 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 ('06FB0TNSRYEJ0G4F0SBHG379YR', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Auto-pause sim on implant-fullscreen (inspection substrate)', 'D-226 layer 1 (substrate). Auto-pause the sim when the client enters a fullscreen implant app (the Atlas). Freeze the world-advancing tick phases (Movement/Simulation/Economy/Storyteller/Knowledge/TickAdvance) via run-conditions keyed to a pause command, but KEEP PreInput (gen-drain), Input (receive resume), Snapshot, PostSnapshot (bridge) alive so the UI keeps fetching data while frozen. Reuse the existing TickRate::Paused + the input.rs paused-allowlist seam; trigger from HudGroups::gameplay_occluded (D-170) -> a pause/resume command over the bridge. For static geography pause is a compute-saver; for dynamic state (economics, later) it is essential. Confirm exactly what TickRate::Paused currently gates (whole loop vs sim-advance) and gate at the phase level.
---
Refinement (2026-07-13, /whats-next Si audit + lead ruling):
CONFIRMED (the ticket''s own open question): TickRate::Paused today gates ONLY the clock — advance_tick (simulation/time.rs:100-110, in TickPhase::TickAdvance) no-ops when Paused. There are ZERO run_if/run-conditions anywhere in the server schedule; tick_phases.rs::configure() only chains ordering. input.rs''s paused-allowlist (input.rs:128,134-155) is an in-system dispatch filter, not a phase gate. So "gate at the phase level" is new plumbing: add .run_if(not paused) to the SystemSet configs for Movement/Simulation/Storyteller/Knowledge/TickAdvance in tick_phases.rs::configure().
ECONOMY SPLIT (must not miss): serve_econ_state_query — which services the paused-ALLOWED EconStateQuery — is registered in the same TickPhase::Economy set, .after(tick_economy_simulation) (economy_plugin.rs:27-35). A blanket set-level gate on Economy would break that paused guarantee. Gate tick_economy_simulation individually; leave serve_econ_state_query unconditioned (.after() ordering holds when the upstream system is skipped). All other allowlist entries verified to land on keep-alive phases.
BRIDGE: no new protocol needed — PlayerAction::Pause/Unpause work end-to-end (D-088 manual pause; input_mapper.gd:137-139 → sim_bridge.gd action_enum_to_wire:528-531 → receive_bridge_inputs → process_player_input). The ONLY missing piece is client wiring: connect HudGroups.gameplay_occluded (hud_groups.gd:33,63-88) to sending Pause/Unpause via sim_bridge.gd''s outbound queue. Today only implant/map (Atlas, default_mode=fullscreen) triggers occlusion.
GEN-DRAIN: confirmed still correct post-T-1046 — GenerationPlugin registers drain_generation_completions + serve_atlas_requests in TickPhase::PreInput (atlas/plugin.rs:52-62); cascade compute is on an independent Rayon pool, so generation keeps flowing while paused. PreInput/Input/Snapshot/PostSnapshot stay alive per the ticket.
MANUAL-vs-AUTO PAUSE (the one gap — lead ruling: Option A): track WHETHER THE AUTO-PAUSE CAUSED the pause (a flag distinct from TickRate itself, e.g. auto_pause_active). Auto-resume on implant-close fires Unpause ONLY if auto-pause was the trigger — a prior manual pause (or D-088 Half rate) survives implant close untouched. No previous-rate stack (that''s Option C, deferred).', 'backlog', 'high', NULL, 'server', 'D-226', '2026-05-24 09:22:33', '2026-07-13 21:53:25.085', NULL, '5fe338d15b93a3392aa9d300932eaef8', 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 ('06FBRCMEKT72S0Q6NE68JH7EXC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Sol markers → D-223 names-only, migrated together with the Atlas legacy-geometry read path', 'Split from T-1071 (2026-06-12): converting the 4 legacy geometry-bearing GJ-0 markers.json (GJ0d, GJ0d-1, GJ0e, GJ0f-2) to the D-223 names-only schema is SAFE for generation (import_economics.py:1452 skips GJ 0 explicitly; all authored coordinates/populations verified present name-by-name in tooling/planet-gen/sol_markers/*.json + sol_name_fixes.py FIXES) but REGRESSES the client: atlas_viewer.gd:433 still loads markers.json per the legacy D-191 §8 geometry schema, so Sol bodies lose their city/river/mountain Atlas overlays. The conversion was implemented, verified, and then deliberately reverted to preserve working overlays. Land both halves together: (1) point the Sol overlay read at the proper Sol home (sol_markers/ or a baked path — Q-107 consolidation may decide this), (2) convert the 4 files, (3) also fix the residual debt: sol_import.py still WRITES legacy-schema markers and an unconditional heightmap link in its own _write_index_md, and import_economics.py:1485 carries a now-misleading comment about preserved geometry markers (editing it stales the systems.db stamp — bundle with a regen).
D-236 amended 2026-07-13: player access lore-capped at the Oort-cloud gateway station; Sol permanently excluded from deterministic runs. T-949 (2026-07-13 scoping) explicitly excludes Sol and leaves the legacy markers read in place for this ticket to migrate once Q-107 resolves — Sol''s geometry needs a server-side AUTHORED home, never a cascade path.', 'backlog', 'low', NULL, 'client', NULL, '2026-06-12 14:18:10', '2026-07-13 21:53:26.940', NULL, 'c37ad072071989493bfd216ffbb66891', 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 ('06FB0TNSRZ8BFVNWDH17CMH1YM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Per-layer Atlas map viewer (generation progress viewer)', 'Render each cascade layer as a MAP in the implant Atlas (D-191, the Phase 3 viewer — D-166 amendment): terrain → water → biome → settlements → political zones → quarter/block skeleton → tiles. This is the progress-viewer deliverable; NO in-world rendering. Grows as each layer lands.
---
Refinement (2026-05-23, /whats-next):
- DATA TRANSPORT (resolves Q-098): NO bake — baking derived layer data makes mods second-class. A server-side FILE STREAM PROXY: resolves a body source files across base + mod paths, runs the deterministic cascade on demand (run_cascade, LRU-cached per D-203, ~45ms), and streams serialized layer data to the Godot client over IPC. First-party and mod bodies use the identical path; no pre-baked derived artifacts. Protocol/stream spec designed with Tyre and recorded in a D-record resolving Q-098.
- UI: layer selector in the existing regional Atlas view; layers are ADDITIVE overlays (toggle heightmap + rivers + attractors + sub-biome together and composite), not one-at-a-time — matching the existing overlay-bar model (D-191).
- SCOPE NOW: only Layer 0 (heightmap) + Layer 1 (rivers/basins/attractors/sub-biome) exist; the viewer grows as layers land (D-166). Acceptance: Layer 0 + Layer 1 render as additive, toggleable overlays for a body, fed by the proxy.
---
Design (D-226, Araminta, 2026-05-24) — human-visual layer of the inspection harness. Extend OVERLAY_DEFS / AtlasOverlayBar with a generation overlay group (second row) + a left-side legend panel; do NOT build a separate viewer. Encoding: relief base; rivers as blue polylines (mouths = double-circle markers, confluences where in-degree >= 3); basin fills very translucent + thin boundary polylines; attractors as markers where SHAPE encodes the 7 AttractorTypes (circle/half-circle/diamond/down-tri/up-tri/hollow-circle/square) and COLOR encodes sub-biome (shape carries identity in headless/monochrome; color is additive); size scales with strength, skip strength < 0.15. Stable button ids gen_l0_heightmap / gen_l1_rivers / gen_l1_basins / gen_l1_attractors; start pending/locked, unlock+activate via notify_gen_layer_ready(id) as data arrives (grows as layers land, D-166). Consumes the live proxy (#969); the agent channel (#971) drives this same UI.
Remaining to close (Si sweep 2026-06-07): add the gen_l0_heightmap overlay to OVERLAY_DEFS + render path in atlas_viewer.gd / atlas_marker_overlay.gd — required by the D-226/T-960 acceptance criteria but currently absent. Layer-1 overlays (rivers/basins/attractors) + proxy wire + pending indicator are already implemented; this Layer-0 overlay is the sole remaining piece.
---
Refinement (2026-07-13, /whats-next Si audit + Jeroen rulings) — RESCOPE. The 2026-06-07 scope note is obsolete in both directions:
DONE ALREADY (record, don''t rebuild):
- L1 overlays (gen_l1_rivers/basins/attractors) + proxy wire + pending indicator: shipped.
- District-morphology overlay (gen_district "MRPH"): shipped under T-1046 via AtlasLayerResponse.district_grid — the wiring precedent for everything below.
- gen_l0_heightmap: DROPPED — relief already renders via the always-on terrain overlay (D-191 §7). D-226 amended (2026-07-13) accordingly.
IN SCOPE NOW (this ticket):
1. gen_l2_roads overlay — L2 road/rail graph (T-1038) is computed on every AnalyzeBody (gen_queue.rs runs up_to=RoadGraph) but never serialized: add RoadGraphLayer field to AtlasLayerResponse (layer_proxy.rs), protocol.gd decode, overlay rendering (edges colored by MaintenanceAuthority, junction markers).
2. gen_l3_settlements overlay — wire T-955''s computed CityPlacement positions through the proxy (new SettlementLayer field, exact T-1046 precedent) so cities render on generated bodies for the first time (Jeroen ruling: wire positions now; T-949 stays boundary-cleanup).
3. Left-side legend panel — D-226 item (3) deliverable, never built (shape/color key for attractor types + sub-biomes + the new overlays).
4. Doc-comment fix: layer_proxy.rs:31-34 claims cascade runs "through CascadeLayer::Settlement" — actually RoadGraph (gen_queue.rs:416); client''s up_to field is ignored by the server. One-line correction.
OUT OF SCOPE (explicitly, per 2026-07-13 rulings):
- L4 quarter footprints → design ticket T-1112 (needs coarse-shape design first).
- Region climate → prerequisite wiring ticket T-1113 (not in cascade at all yet).
- L5 chunk/tile fill → NEVER an Atlas map layer (D-191/D-226 amendments 2026-07-13; harness-verified, Phase-5 in-world).
- markers.json migration / Sol → T-949 / T-1073 (D-236: Sol excluded from deterministic runs, access capped at Oort gateway).
Overlay mechanism: follow the shipped always-toggleable pattern (D-226 as amended), NOT the old lock/notify spec.', 'in_progress', 'high', NULL, 'client', 'D-191', '2026-05-22 17:42:34', '2026-07-13 21:54:06.361', NULL, '956b2a24c68b83bb2d4e9399f5c90478', 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 ('06FB0TNSRYEJ0G4F0SBHG379YR', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Auto-pause sim on implant-fullscreen (inspection substrate)', 'D-226 layer 1 (substrate). Auto-pause the sim when the client enters a fullscreen implant app (the Atlas). Freeze the world-advancing tick phases (Movement/Simulation/Economy/Storyteller/Knowledge/TickAdvance) via run-conditions keyed to a pause command, but KEEP PreInput (gen-drain), Input (receive resume), Snapshot, PostSnapshot (bridge) alive so the UI keeps fetching data while frozen. Reuse the existing TickRate::Paused + the input.rs paused-allowlist seam; trigger from HudGroups::gameplay_occluded (D-170) -> a pause/resume command over the bridge. For static geography pause is a compute-saver; for dynamic state (economics, later) it is essential. Confirm exactly what TickRate::Paused currently gates (whole loop vs sim-advance) and gate at the phase level.
---
Refinement (2026-07-13, /whats-next Si audit + lead ruling):
CONFIRMED (the ticket''s own open question): TickRate::Paused today gates ONLY the clock — advance_tick (simulation/time.rs:100-110, in TickPhase::TickAdvance) no-ops when Paused. There are ZERO run_if/run-conditions anywhere in the server schedule; tick_phases.rs::configure() only chains ordering. input.rs''s paused-allowlist (input.rs:128,134-155) is an in-system dispatch filter, not a phase gate. So "gate at the phase level" is new plumbing: add .run_if(not paused) to the SystemSet configs for Movement/Simulation/Storyteller/Knowledge/TickAdvance in tick_phases.rs::configure().
ECONOMY SPLIT (must not miss): serve_econ_state_query — which services the paused-ALLOWED EconStateQuery — is registered in the same TickPhase::Economy set, .after(tick_economy_simulation) (economy_plugin.rs:27-35). A blanket set-level gate on Economy would break that paused guarantee. Gate tick_economy_simulation individually; leave serve_econ_state_query unconditioned (.after() ordering holds when the upstream system is skipped). All other allowlist entries verified to land on keep-alive phases.
BRIDGE: no new protocol needed — PlayerAction::Pause/Unpause work end-to-end (D-088 manual pause; input_mapper.gd:137-139 → sim_bridge.gd action_enum_to_wire:528-531 → receive_bridge_inputs → process_player_input). The ONLY missing piece is client wiring: connect HudGroups.gameplay_occluded (hud_groups.gd:33,63-88) to sending Pause/Unpause via sim_bridge.gd''s outbound queue. Today only implant/map (Atlas, default_mode=fullscreen) triggers occlusion.
GEN-DRAIN: confirmed still correct post-T-1046 — GenerationPlugin registers drain_generation_completions + serve_atlas_requests in TickPhase::PreInput (atlas/plugin.rs:52-62); cascade compute is on an independent Rayon pool, so generation keeps flowing while paused. PreInput/Input/Snapshot/PostSnapshot stay alive per the ticket.
MANUAL-vs-AUTO PAUSE (the one gap — lead ruling: Option A): track WHETHER THE AUTO-PAUSE CAUSED the pause (a flag distinct from TickRate itself, e.g. auto_pause_active). Auto-resume on implant-close fires Unpause ONLY if auto-pause was the trigger — a prior manual pause (or D-088 Half rate) survives implant close untouched. No previous-rate stack (that''s Option C, deferred).', 'in_progress', 'high', NULL, 'server', 'D-226', '2026-05-24 09:22:33', '2026-07-13 21:54:06.378', NULL, '7d7c2f702ed806428f939f2d0d0e04d3', 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 ('06FB0TNSRW2ZSPJ3D9NEZ6JBW8', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas data delivery — replace client-side file reads with server API calls', 'The client currently reads markers.json and star_map_data.json directly from the filesystem. This bypasses the server layer entirely — the client should request all game data through IPC calls to the Rust backend.
Implementation can be simple: the server exposes a query/proxy endpoint that reads the JSON file or runs a DB query and returns the result. The client asks, the server responds. No complex protocol needed — just the boundary enforcement that the client never reads files or DB directly.
Affects: atlas app (reach, system, planet, regional screens), economics monitor. The server-side handler can start as a thin proxy (read file, return contents) and evolve into perception-filtered queries (D-012) later.
Re-parented under Phase 4 epic per cascade refocus 2026-06-12 — atlas data delivery underpins viewing cascade layers through the implant Atlas.
---
Refinement (2026-07-13, /whats-next Si audit + Jeroen rulings):
SCOPE: the ~2,398 generated (non-Sol) bodies + star_map_data.json only.
- SOL EXCLUDED (Jeroen): Sol stays out of all deterministic runs; player access is lore-capped at the Oort-cloud gateway station — see D-236 as amended 2026-07-13. Sol''s client-side legacy markers.json read (atlas_viewer.gd::_load_markers) STAYS as the documented D-223/D-236 exception until T-1073 (gated on Q-107). Do not repeat T-1073''s implement→break→revert cycle.
- CITATION FIX: the description''s "perception-filtered queries (D-012)" should read D-010 (information boundaries). D-012 is chunk-based map architecture — unrelated.
CURRENT READ INVENTORY (verified 2026-07-13):
1. atlas_viewer.gd::_load_markers (~436-467) — markers.json via FileAccess. For non-Sol bodies this is names-only data (D-223); replace with a names request over the bridge (server already reads atlas_city_names via CityContextReader). Positions come from T-960''s SettlementLayer work (separate ticket, same branch).
2. system_index.gd::get_sorted_systems — star_map_data.json, consumed by atlas_app.gd:150 (Reach screen) + overview_screen.gd:114 (economics monitor selector; its only direct read). star_map_data.json is a build-time bake (tooling/generate-star-map-data.py); the server has NO existing reader — a thin proxy (server reads the same JSON, returns it) is acceptable per the ticket.
3. OUT OF SCOPE (deliberate, D-225): reliefmap.png / wiki globe image loads stay client-side.
PROTOCOL PATTERN: structural disambiguation on the existing IPC stream (Inbound enum: Inputs vs AtlasRequest). Reach-level star-map data is not per-body/cascade-shaped — add NEW request/response types (e.g. StarMapRequest → StarMapResponse), do not shoehorn into AtlasLayerResponse. Per-body names ride the atlas path.', 'in_progress', 'medium', NULL, 'client', NULL, '2026-05-03 14:14:23', '2026-07-13 21:54:06.379', NULL, 'd8effcc8e1310f4e9381698c28f2f8ea', 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 ('06FP1GTTEGD648NDDFCAYR6NR4', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Client gdlint debt — 39 pre-existing warnings (zero-warnings policy)', 'Full gdlint over client/scripts + client/ui + client/tests reports 39 pre-existing problems (class-definitions-order across sandbox/, input_mapper, atlas system_screen/marker_overlay; max-returns in _sub_biome_color; max-line-length atlas_app.gd:75; duplicated-load in test_free_camera; constant-name in test_greybox_store). Verified 2026-07-14 (atlas-layer-viewer batch): all pre-date the branch — identical findings on main. Zero-warnings policy says these should be fixed or suppressed with commented justification. Mechanical sweep, no behavior changes.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-14 13:14:03.252', '2026-07-14 13:14:03.252', NULL, '81f7495cbe4d2273019aab1dc39f73d1', 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 ('06FB0TNSRZ8BFVNWDH17CMH1YM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Per-layer Atlas map viewer (generation progress viewer)', 'Render each cascade layer as a MAP in the implant Atlas (D-191, the Phase 3 viewer — D-166 amendment): terrain → water → biome → settlements → political zones → quarter/block skeleton → tiles. This is the progress-viewer deliverable; NO in-world rendering. Grows as each layer lands.
---
Refinement (2026-05-23, /whats-next):
- DATA TRANSPORT (resolves Q-098): NO bake — baking derived layer data makes mods second-class. A server-side FILE STREAM PROXY: resolves a body source files across base + mod paths, runs the deterministic cascade on demand (run_cascade, LRU-cached per D-203, ~45ms), and streams serialized layer data to the Godot client over IPC. First-party and mod bodies use the identical path; no pre-baked derived artifacts. Protocol/stream spec designed with Tyre and recorded in a D-record resolving Q-098.
- UI: layer selector in the existing regional Atlas view; layers are ADDITIVE overlays (toggle heightmap + rivers + attractors + sub-biome together and composite), not one-at-a-time — matching the existing overlay-bar model (D-191).
- SCOPE NOW: only Layer 0 (heightmap) + Layer 1 (rivers/basins/attractors/sub-biome) exist; the viewer grows as layers land (D-166). Acceptance: Layer 0 + Layer 1 render as additive, toggleable overlays for a body, fed by the proxy.
---
Design (D-226, Araminta, 2026-05-24) — human-visual layer of the inspection harness. Extend OVERLAY_DEFS / AtlasOverlayBar with a generation overlay group (second row) + a left-side legend panel; do NOT build a separate viewer. Encoding: relief base; rivers as blue polylines (mouths = double-circle markers, confluences where in-degree >= 3); basin fills very translucent + thin boundary polylines; attractors as markers where SHAPE encodes the 7 AttractorTypes (circle/half-circle/diamond/down-tri/up-tri/hollow-circle/square) and COLOR encodes sub-biome (shape carries identity in headless/monochrome; color is additive); size scales with strength, skip strength < 0.15. Stable button ids gen_l0_heightmap / gen_l1_rivers / gen_l1_basins / gen_l1_attractors; start pending/locked, unlock+activate via notify_gen_layer_ready(id) as data arrives (grows as layers land, D-166). Consumes the live proxy (#969); the agent channel (#971) drives this same UI.
Remaining to close (Si sweep 2026-06-07): add the gen_l0_heightmap overlay to OVERLAY_DEFS + render path in atlas_viewer.gd / atlas_marker_overlay.gd — required by the D-226/T-960 acceptance criteria but currently absent. Layer-1 overlays (rivers/basins/attractors) + proxy wire + pending indicator are already implemented; this Layer-0 overlay is the sole remaining piece.
---
Refinement (2026-07-13, /whats-next Si audit + Jeroen rulings) — RESCOPE. The 2026-06-07 scope note is obsolete in both directions:
DONE ALREADY (record, don''t rebuild):
- L1 overlays (gen_l1_rivers/basins/attractors) + proxy wire + pending indicator: shipped.
- District-morphology overlay (gen_district "MRPH"): shipped under T-1046 via AtlasLayerResponse.district_grid — the wiring precedent for everything below.
- gen_l0_heightmap: DROPPED — relief already renders via the always-on terrain overlay (D-191 §7). D-226 amended (2026-07-13) accordingly.
IN SCOPE NOW (this ticket):
1. gen_l2_roads overlay — L2 road/rail graph (T-1038) is computed on every AnalyzeBody (gen_queue.rs runs up_to=RoadGraph) but never serialized: add RoadGraphLayer field to AtlasLayerResponse (layer_proxy.rs), protocol.gd decode, overlay rendering (edges colored by MaintenanceAuthority, junction markers).
2. gen_l3_settlements overlay — wire T-955''s computed CityPlacement positions through the proxy (new SettlementLayer field, exact T-1046 precedent) so cities render on generated bodies for the first time (Jeroen ruling: wire positions now; T-949 stays boundary-cleanup).
3. Left-side legend panel — D-226 item (3) deliverable, never built (shape/color key for attractor types + sub-biomes + the new overlays).
4. Doc-comment fix: layer_proxy.rs:31-34 claims cascade runs "through CascadeLayer::Settlement" — actually RoadGraph (gen_queue.rs:416); client''s up_to field is ignored by the server. One-line correction.
OUT OF SCOPE (explicitly, per 2026-07-13 rulings):
- L4 quarter footprints → design ticket T-1112 (needs coarse-shape design first).
- Region climate → prerequisite wiring ticket T-1113 (not in cascade at all yet).
- L5 chunk/tile fill → NEVER an Atlas map layer (D-191/D-226 amendments 2026-07-13; harness-verified, Phase-5 in-world).
- markers.json migration / Sol → T-949 / T-1073 (D-236: Sol excluded from deterministic runs, access capped at Oort gateway).
Overlay mechanism: follow the shipped always-toggleable pattern (D-226 as amended), NOT the old lock/notify spec.', 'review', 'high', NULL, 'client', 'D-191', '2026-05-22 17:42:34', '2026-07-14 14:36:24.074', NULL, '1be7212c31111c592dfb9f5aa7014578', 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 ('06FB0TNSRYEJ0G4F0SBHG379YR', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Auto-pause sim on implant-fullscreen (inspection substrate)', 'D-226 layer 1 (substrate). Auto-pause the sim when the client enters a fullscreen implant app (the Atlas). Freeze the world-advancing tick phases (Movement/Simulation/Economy/Storyteller/Knowledge/TickAdvance) via run-conditions keyed to a pause command, but KEEP PreInput (gen-drain), Input (receive resume), Snapshot, PostSnapshot (bridge) alive so the UI keeps fetching data while frozen. Reuse the existing TickRate::Paused + the input.rs paused-allowlist seam; trigger from HudGroups::gameplay_occluded (D-170) -> a pause/resume command over the bridge. For static geography pause is a compute-saver; for dynamic state (economics, later) it is essential. Confirm exactly what TickRate::Paused currently gates (whole loop vs sim-advance) and gate at the phase level.
---
Refinement (2026-07-13, /whats-next Si audit + lead ruling):
CONFIRMED (the ticket''s own open question): TickRate::Paused today gates ONLY the clock — advance_tick (simulation/time.rs:100-110, in TickPhase::TickAdvance) no-ops when Paused. There are ZERO run_if/run-conditions anywhere in the server schedule; tick_phases.rs::configure() only chains ordering. input.rs''s paused-allowlist (input.rs:128,134-155) is an in-system dispatch filter, not a phase gate. So "gate at the phase level" is new plumbing: add .run_if(not paused) to the SystemSet configs for Movement/Simulation/Storyteller/Knowledge/TickAdvance in tick_phases.rs::configure().
ECONOMY SPLIT (must not miss): serve_econ_state_query — which services the paused-ALLOWED EconStateQuery — is registered in the same TickPhase::Economy set, .after(tick_economy_simulation) (economy_plugin.rs:27-35). A blanket set-level gate on Economy would break that paused guarantee. Gate tick_economy_simulation individually; leave serve_econ_state_query unconditioned (.after() ordering holds when the upstream system is skipped). All other allowlist entries verified to land on keep-alive phases.
BRIDGE: no new protocol needed — PlayerAction::Pause/Unpause work end-to-end (D-088 manual pause; input_mapper.gd:137-139 → sim_bridge.gd action_enum_to_wire:528-531 → receive_bridge_inputs → process_player_input). The ONLY missing piece is client wiring: connect HudGroups.gameplay_occluded (hud_groups.gd:33,63-88) to sending Pause/Unpause via sim_bridge.gd''s outbound queue. Today only implant/map (Atlas, default_mode=fullscreen) triggers occlusion.
GEN-DRAIN: confirmed still correct post-T-1046 — GenerationPlugin registers drain_generation_completions + serve_atlas_requests in TickPhase::PreInput (atlas/plugin.rs:52-62); cascade compute is on an independent Rayon pool, so generation keeps flowing while paused. PreInput/Input/Snapshot/PostSnapshot stay alive per the ticket.
MANUAL-vs-AUTO PAUSE (the one gap — lead ruling: Option A): track WHETHER THE AUTO-PAUSE CAUSED the pause (a flag distinct from TickRate itself, e.g. auto_pause_active). Auto-resume on implant-close fires Unpause ONLY if auto-pause was the trigger — a prior manual pause (or D-088 Half rate) survives implant close untouched. No previous-rate stack (that''s Option C, deferred).', 'review', 'high', NULL, 'server', 'D-226', '2026-05-24 09:22:33', '2026-07-14 14:36:24.076', NULL, '2d011ca4ce4ba724166772120be881a8', 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 ('06FB0TNSRW2ZSPJ3D9NEZ6JBW8', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas data delivery — replace client-side file reads with server API calls', 'The client currently reads markers.json and star_map_data.json directly from the filesystem. This bypasses the server layer entirely — the client should request all game data through IPC calls to the Rust backend.
Implementation can be simple: the server exposes a query/proxy endpoint that reads the JSON file or runs a DB query and returns the result. The client asks, the server responds. No complex protocol needed — just the boundary enforcement that the client never reads files or DB directly.
Affects: atlas app (reach, system, planet, regional screens), economics monitor. The server-side handler can start as a thin proxy (read file, return contents) and evolve into perception-filtered queries (D-012) later.
Re-parented under Phase 4 epic per cascade refocus 2026-06-12 — atlas data delivery underpins viewing cascade layers through the implant Atlas.
---
Refinement (2026-07-13, /whats-next Si audit + Jeroen rulings):
SCOPE: the ~2,398 generated (non-Sol) bodies + star_map_data.json only.
- SOL EXCLUDED (Jeroen): Sol stays out of all deterministic runs; player access is lore-capped at the Oort-cloud gateway station — see D-236 as amended 2026-07-13. Sol''s client-side legacy markers.json read (atlas_viewer.gd::_load_markers) STAYS as the documented D-223/D-236 exception until T-1073 (gated on Q-107). Do not repeat T-1073''s implement→break→revert cycle.
- CITATION FIX: the description''s "perception-filtered queries (D-012)" should read D-010 (information boundaries). D-012 is chunk-based map architecture — unrelated.
CURRENT READ INVENTORY (verified 2026-07-13):
1. atlas_viewer.gd::_load_markers (~436-467) — markers.json via FileAccess. For non-Sol bodies this is names-only data (D-223); replace with a names request over the bridge (server already reads atlas_city_names via CityContextReader). Positions come from T-960''s SettlementLayer work (separate ticket, same branch).
2. system_index.gd::get_sorted_systems — star_map_data.json, consumed by atlas_app.gd:150 (Reach screen) + overview_screen.gd:114 (economics monitor selector; its only direct read). star_map_data.json is a build-time bake (tooling/generate-star-map-data.py); the server has NO existing reader — a thin proxy (server reads the same JSON, returns it) is acceptable per the ticket.
3. OUT OF SCOPE (deliberate, D-225): reliefmap.png / wiki globe image loads stay client-side.
PROTOCOL PATTERN: structural disambiguation on the existing IPC stream (Inbound enum: Inputs vs AtlasRequest). Reach-level star-map data is not per-body/cascade-shaped — add NEW request/response types (e.g. StarMapRequest → StarMapResponse), do not shoehorn into AtlasLayerResponse. Per-body names ride the atlas path.', 'review', 'medium', NULL, 'client', NULL, '2026-05-03 14:14:23', '2026-07-14 14:36:24.077', NULL, '44aa53dcae1945b971c673cf3124f7b1', 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 ('06FB0TNSRZ8BFVNWDH17CMH1YM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Per-layer Atlas map viewer (generation progress viewer)', 'Render each cascade layer as a MAP in the implant Atlas (D-191, the Phase 3 viewer — D-166 amendment): terrain → water → biome → settlements → political zones → quarter/block skeleton → tiles. This is the progress-viewer deliverable; NO in-world rendering. Grows as each layer lands.
---
Refinement (2026-05-23, /whats-next):
- DATA TRANSPORT (resolves Q-098): NO bake — baking derived layer data makes mods second-class. A server-side FILE STREAM PROXY: resolves a body source files across base + mod paths, runs the deterministic cascade on demand (run_cascade, LRU-cached per D-203, ~45ms), and streams serialized layer data to the Godot client over IPC. First-party and mod bodies use the identical path; no pre-baked derived artifacts. Protocol/stream spec designed with Tyre and recorded in a D-record resolving Q-098.
- UI: layer selector in the existing regional Atlas view; layers are ADDITIVE overlays (toggle heightmap + rivers + attractors + sub-biome together and composite), not one-at-a-time — matching the existing overlay-bar model (D-191).
- SCOPE NOW: only Layer 0 (heightmap) + Layer 1 (rivers/basins/attractors/sub-biome) exist; the viewer grows as layers land (D-166). Acceptance: Layer 0 + Layer 1 render as additive, toggleable overlays for a body, fed by the proxy.
---
Design (D-226, Araminta, 2026-05-24) — human-visual layer of the inspection harness. Extend OVERLAY_DEFS / AtlasOverlayBar with a generation overlay group (second row) + a left-side legend panel; do NOT build a separate viewer. Encoding: relief base; rivers as blue polylines (mouths = double-circle markers, confluences where in-degree >= 3); basin fills very translucent + thin boundary polylines; attractors as markers where SHAPE encodes the 7 AttractorTypes (circle/half-circle/diamond/down-tri/up-tri/hollow-circle/square) and COLOR encodes sub-biome (shape carries identity in headless/monochrome; color is additive); size scales with strength, skip strength < 0.15. Stable button ids gen_l0_heightmap / gen_l1_rivers / gen_l1_basins / gen_l1_attractors; start pending/locked, unlock+activate via notify_gen_layer_ready(id) as data arrives (grows as layers land, D-166). Consumes the live proxy (#969); the agent channel (#971) drives this same UI.
Remaining to close (Si sweep 2026-06-07): add the gen_l0_heightmap overlay to OVERLAY_DEFS + render path in atlas_viewer.gd / atlas_marker_overlay.gd — required by the D-226/T-960 acceptance criteria but currently absent. Layer-1 overlays (rivers/basins/attractors) + proxy wire + pending indicator are already implemented; this Layer-0 overlay is the sole remaining piece.
---
Refinement (2026-07-13, /whats-next Si audit + Jeroen rulings) — RESCOPE. The 2026-06-07 scope note is obsolete in both directions:
DONE ALREADY (record, don''t rebuild):
- L1 overlays (gen_l1_rivers/basins/attractors) + proxy wire + pending indicator: shipped.
- District-morphology overlay (gen_district "MRPH"): shipped under T-1046 via AtlasLayerResponse.district_grid — the wiring precedent for everything below.
- gen_l0_heightmap: DROPPED — relief already renders via the always-on terrain overlay (D-191 §7). D-226 amended (2026-07-13) accordingly.
IN SCOPE NOW (this ticket):
1. gen_l2_roads overlay — L2 road/rail graph (T-1038) is computed on every AnalyzeBody (gen_queue.rs runs up_to=RoadGraph) but never serialized: add RoadGraphLayer field to AtlasLayerResponse (layer_proxy.rs), protocol.gd decode, overlay rendering (edges colored by MaintenanceAuthority, junction markers).
2. gen_l3_settlements overlay — wire T-955''s computed CityPlacement positions through the proxy (new SettlementLayer field, exact T-1046 precedent) so cities render on generated bodies for the first time (Jeroen ruling: wire positions now; T-949 stays boundary-cleanup).
3. Left-side legend panel — D-226 item (3) deliverable, never built (shape/color key for attractor types + sub-biomes + the new overlays).
4. Doc-comment fix: layer_proxy.rs:31-34 claims cascade runs "through CascadeLayer::Settlement" — actually RoadGraph (gen_queue.rs:416); client''s up_to field is ignored by the server. One-line correction.
OUT OF SCOPE (explicitly, per 2026-07-13 rulings):
- L4 quarter footprints → design ticket T-1112 (needs coarse-shape design first).
- Region climate → prerequisite wiring ticket T-1113 (not in cascade at all yet).
- L5 chunk/tile fill → NEVER an Atlas map layer (D-191/D-226 amendments 2026-07-13; harness-verified, Phase-5 in-world).
- markers.json migration / Sol → T-949 / T-1073 (D-236: Sol excluded from deterministic runs, access capped at Oort gateway).
Overlay mechanism: follow the shipped always-toggleable pattern (D-226 as amended), NOT the old lock/notify spec.', 'done', 'high', NULL, 'client', 'D-191', '2026-05-22 17:42:34', '2026-07-14 16:03:22.067', NULL, '470c5af080778bdd00875b2e17b92dfc', 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 ('06FB0TNSRYEJ0G4F0SBHG379YR', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Auto-pause sim on implant-fullscreen (inspection substrate)', 'D-226 layer 1 (substrate). Auto-pause the sim when the client enters a fullscreen implant app (the Atlas). Freeze the world-advancing tick phases (Movement/Simulation/Economy/Storyteller/Knowledge/TickAdvance) via run-conditions keyed to a pause command, but KEEP PreInput (gen-drain), Input (receive resume), Snapshot, PostSnapshot (bridge) alive so the UI keeps fetching data while frozen. Reuse the existing TickRate::Paused + the input.rs paused-allowlist seam; trigger from HudGroups::gameplay_occluded (D-170) -> a pause/resume command over the bridge. For static geography pause is a compute-saver; for dynamic state (economics, later) it is essential. Confirm exactly what TickRate::Paused currently gates (whole loop vs sim-advance) and gate at the phase level.
---
Refinement (2026-07-13, /whats-next Si audit + lead ruling):
CONFIRMED (the ticket''s own open question): TickRate::Paused today gates ONLY the clock — advance_tick (simulation/time.rs:100-110, in TickPhase::TickAdvance) no-ops when Paused. There are ZERO run_if/run-conditions anywhere in the server schedule; tick_phases.rs::configure() only chains ordering. input.rs''s paused-allowlist (input.rs:128,134-155) is an in-system dispatch filter, not a phase gate. So "gate at the phase level" is new plumbing: add .run_if(not paused) to the SystemSet configs for Movement/Simulation/Storyteller/Knowledge/TickAdvance in tick_phases.rs::configure().
ECONOMY SPLIT (must not miss): serve_econ_state_query — which services the paused-ALLOWED EconStateQuery — is registered in the same TickPhase::Economy set, .after(tick_economy_simulation) (economy_plugin.rs:27-35). A blanket set-level gate on Economy would break that paused guarantee. Gate tick_economy_simulation individually; leave serve_econ_state_query unconditioned (.after() ordering holds when the upstream system is skipped). All other allowlist entries verified to land on keep-alive phases.
BRIDGE: no new protocol needed — PlayerAction::Pause/Unpause work end-to-end (D-088 manual pause; input_mapper.gd:137-139 → sim_bridge.gd action_enum_to_wire:528-531 → receive_bridge_inputs → process_player_input). The ONLY missing piece is client wiring: connect HudGroups.gameplay_occluded (hud_groups.gd:33,63-88) to sending Pause/Unpause via sim_bridge.gd''s outbound queue. Today only implant/map (Atlas, default_mode=fullscreen) triggers occlusion.
GEN-DRAIN: confirmed still correct post-T-1046 — GenerationPlugin registers drain_generation_completions + serve_atlas_requests in TickPhase::PreInput (atlas/plugin.rs:52-62); cascade compute is on an independent Rayon pool, so generation keeps flowing while paused. PreInput/Input/Snapshot/PostSnapshot stay alive per the ticket.
MANUAL-vs-AUTO PAUSE (the one gap — lead ruling: Option A): track WHETHER THE AUTO-PAUSE CAUSED the pause (a flag distinct from TickRate itself, e.g. auto_pause_active). Auto-resume on implant-close fires Unpause ONLY if auto-pause was the trigger — a prior manual pause (or D-088 Half rate) survives implant close untouched. No previous-rate stack (that''s Option C, deferred).', 'done', 'high', NULL, 'server', 'D-226', '2026-05-24 09:22:33', '2026-07-14 16:03:22.074', NULL, '255f751baf2f968ce8d282ee1da1354f', 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 ('06FB0TNSRW2ZSPJ3D9NEZ6JBW8', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas data delivery — replace client-side file reads with server API calls', 'The client currently reads markers.json and star_map_data.json directly from the filesystem. This bypasses the server layer entirely — the client should request all game data through IPC calls to the Rust backend.
Implementation can be simple: the server exposes a query/proxy endpoint that reads the JSON file or runs a DB query and returns the result. The client asks, the server responds. No complex protocol needed — just the boundary enforcement that the client never reads files or DB directly.
Affects: atlas app (reach, system, planet, regional screens), economics monitor. The server-side handler can start as a thin proxy (read file, return contents) and evolve into perception-filtered queries (D-012) later.
Re-parented under Phase 4 epic per cascade refocus 2026-06-12 — atlas data delivery underpins viewing cascade layers through the implant Atlas.
---
Refinement (2026-07-13, /whats-next Si audit + Jeroen rulings):
SCOPE: the ~2,398 generated (non-Sol) bodies + star_map_data.json only.
- SOL EXCLUDED (Jeroen): Sol stays out of all deterministic runs; player access is lore-capped at the Oort-cloud gateway station — see D-236 as amended 2026-07-13. Sol''s client-side legacy markers.json read (atlas_viewer.gd::_load_markers) STAYS as the documented D-223/D-236 exception until T-1073 (gated on Q-107). Do not repeat T-1073''s implement→break→revert cycle.
- CITATION FIX: the description''s "perception-filtered queries (D-012)" should read D-010 (information boundaries). D-012 is chunk-based map architecture — unrelated.
CURRENT READ INVENTORY (verified 2026-07-13):
1. atlas_viewer.gd::_load_markers (~436-467) — markers.json via FileAccess. For non-Sol bodies this is names-only data (D-223); replace with a names request over the bridge (server already reads atlas_city_names via CityContextReader). Positions come from T-960''s SettlementLayer work (separate ticket, same branch).
2. system_index.gd::get_sorted_systems — star_map_data.json, consumed by atlas_app.gd:150 (Reach screen) + overview_screen.gd:114 (economics monitor selector; its only direct read). star_map_data.json is a build-time bake (tooling/generate-star-map-data.py); the server has NO existing reader — a thin proxy (server reads the same JSON, returns it) is acceptable per the ticket.
3. OUT OF SCOPE (deliberate, D-225): reliefmap.png / wiki globe image loads stay client-side.
PROTOCOL PATTERN: structural disambiguation on the existing IPC stream (Inbound enum: Inputs vs AtlasRequest). Reach-level star-map data is not per-body/cascade-shaped — add NEW request/response types (e.g. StarMapRequest → StarMapResponse), do not shoehorn into AtlasLayerResponse. Per-body names ride the atlas path.', 'done', 'medium', NULL, 'client', NULL, '2026-05-03 14:14:23', '2026-07-14 16:03:22.075', NULL, '2bc4ee40e5c80f41fd705d1d3081995c', 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 ('06FCDG73NW7SNCA5Y8JN905QMR', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Corp HQ settlement model — remove HQs from the city pool; specialization-keyed standalone-vs-tenant; install-baked', 'Per D-242. Corp HQs are not cities and must leave atlas_city_names.
1. Remove the D-207/D-223 reserved=1 corp-HQ cross-reference (populate_atlas_city_names_corps, tooling/economy-db/economy_import/atlas.py) — it inserts one atlas_city_names row per corp HQ, creating duplicate co-named cities (e.g. 10 Groombridge rows on GJ380c).
2. Author a source-canonical specialization -> HQ-placement map ({CityTenant, Standalone}). corp_type is degenerate (corporation/combine only); key on corporations.specialization (and/or supply_chain_role). Install-baked.
3. CityTenant HQs: a corp->city link, no new settlement (many corps may tenant one city).
4. Standalone HQs (extraction/heavy-industry/shipyards): emit their own settlement (body + position) into the cascade settlement list. Requires resolving headquarters_body (today 0/165 set; only headquarters_system + a most-populated-body heuristic).
5. Cascade settlement input becomes: city pool + standalone-HQ settlements, merged.
Amends D-207, D-223. Blocks the road-graph hub refinement.
---
Refinement (2026-07-16, /whats-next Si review + gap resolution):
1. Specialization key-space (gap): corporations.specialization is unconstrained free text — 20/165 rows hand-seeded prose via the ORPHANED tooling/populate-corporations.sh (raw sqlite3 INSERTs, single commit e05444f70, not in the Makefile or generator_sources.py; nothing in import_economics writes the column). supply_chain_role is 100% NULL, never wired. RESOLUTION: author a corp_specialization controlled vocabulary + source-canonical TOML (mirror the D-237 wiki/economics/specialization_vocabulary.toml pattern), backfill all 165 corps, and key the {CityTenant, Standalone} placement map on it — all in this ticket. Retire/supersede the orphaned populate-corporations.sh as part of the cleanup (hand-seeded DB values violate the source-canonical rule).
2. headquarters_body resolution (gap): RESOLUTION — explicitly reuse the most-populated-body-in-system heuristic from populate_atlas_city_names_corps (tooling/economy-db/economy_import/atlas.py:229-233) at import/bake time to backfill headquarters_body for Standalone HQs. Deterministic, install-baked.
Si verification notes: all 30 duplicate-name groups in atlas_city_names are 100% reserved=1 rows (zero duplicates among reserved=0) — removal is cleanly scoped. corp_type confirmed degenerate (158 corporation / 7 combine). Consumption side has zero corp/Standalone/Tenant scaffolding: the merge point is greenfield, natural seam is read_body_settlements (server/src/atlas/city_context_reader.rs:268) feeding match_cities (server/src/atlas/attractor_matching.rs).', 'backlog', 'high', NULL, 'server', 'D-242', '2026-06-14 15:29:50', '2026-07-16 07:03:52.010', NULL, '03be4eea746c4a92aa89aae7b14635a1', 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 ('06FCDG73S4ET8KKTK62Z5KFEQM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Bake per-settlement population + settlement_class into the install dataset (only position stays seed-derived)', 'Per D-242. population + settlement_class are install-baked, not seed-derived.
Today: body-level population is baked (273/3240 bodies > 0), but per-city population is 0 and settlement_class NULL for all 463 atlas_city_names rows. D-223 deferred these ''to placement (T-955)'', but match_cities only READS them for tiering — nothing assigns them, so the pop-from-seed step was never built and all cities fall to synthetic placement.
Bake the per-settlement population spread + settlement_class into the source data (importer/source TOMLs). Only POSITION stays seed-derived (D-211). Drop the never-built seed-population idea.
Supersedes the D-223 deferred-pop/class note. Enables significance-ranked hub selection.
---
Refinement (2026-07-16, /whats-next Si review + gap resolution):
1. Population source (gap): RESOLUTION — pure importer derivation. import_economics computes the per-city population spread from the authored bodies.population on every regen-db run via a documented rank-size (Zipf-style) curve; no per-city population source file. Keep the curve constants named + documented in the importer — tuning is a code change by design. Only POSITION stays seed-derived (D-211). Amend D-242''s "carried down into the city/settlement source data" wording while implementing, to record the derivation rule (derivation-at-import, not hand-authoring).
2. settlement_class assignment (gap): RESOLUTION — default every pooled city to PopulationBudget; the D-196 thresholds (>=50k active / <5k ghost) decide state from the baked population. An authored NameLocked override list (small new source TOML) pins hero cities. EconomicTriggered and OrganicGrowth are simulation-time classes per D-196 — out of bake scope.
Si verification notes: 463 atlas_city_names rows, all population=0 / settlement_class NULL — claim exact. match_cities (server/src/atlas/attractor_matching.rs) only READS these fields for tiering; nothing assigns them. D-242/D-223/D-211/D-196 chain is consistent; no conflicting open Q-records. Importer entry point: populate_atlas_city_names (tooling/economy-db/economy_import/atlas.py:61-141).', 'backlog', 'high', NULL, 'server', 'D-242', '2026-06-14 15:29:50', '2026-07-16 07:03:59.490', NULL, '206469dde5fea12542027c54be683226', 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 ('06FCDG73NW7SNCA5Y8JN905QMR', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Corp HQ settlement model — remove HQs from the city pool; specialization-keyed standalone-vs-tenant; install-baked', 'Per D-242. Corp HQs are not cities and must leave atlas_city_names.
1. Remove the D-207/D-223 reserved=1 corp-HQ cross-reference (populate_atlas_city_names_corps, tooling/economy-db/economy_import/atlas.py) — it inserts one atlas_city_names row per corp HQ, creating duplicate co-named cities (e.g. 10 Groombridge rows on GJ380c).
2. Author a source-canonical specialization -> HQ-placement map ({CityTenant, Standalone}). corp_type is degenerate (corporation/combine only); key on corporations.specialization (and/or supply_chain_role). Install-baked.
3. CityTenant HQs: a corp->city link, no new settlement (many corps may tenant one city).
4. Standalone HQs (extraction/heavy-industry/shipyards): emit their own settlement (body + position) into the cascade settlement list. Requires resolving headquarters_body (today 0/165 set; only headquarters_system + a most-populated-body heuristic).
5. Cascade settlement input becomes: city pool + standalone-HQ settlements, merged.
Amends D-207, D-223. Blocks the road-graph hub refinement.
---
Refinement (2026-07-16, /whats-next Si review + gap resolution):
1. Specialization key-space (gap): corporations.specialization is unconstrained free text — 20/165 rows hand-seeded prose via the ORPHANED tooling/populate-corporations.sh (raw sqlite3 INSERTs, single commit e05444f70, not in the Makefile or generator_sources.py; nothing in import_economics writes the column). supply_chain_role is 100% NULL, never wired. RESOLUTION: author a corp_specialization controlled vocabulary + source-canonical TOML (mirror the D-237 wiki/economics/specialization_vocabulary.toml pattern), backfill all 165 corps, and key the {CityTenant, Standalone} placement map on it — all in this ticket. Retire/supersede the orphaned populate-corporations.sh as part of the cleanup (hand-seeded DB values violate the source-canonical rule).
2. headquarters_body resolution (gap): RESOLUTION — explicitly reuse the most-populated-body-in-system heuristic from populate_atlas_city_names_corps (tooling/economy-db/economy_import/atlas.py:229-233) at import/bake time to backfill headquarters_body for Standalone HQs. Deterministic, install-baked.
Si verification notes: all 30 duplicate-name groups in atlas_city_names are 100% reserved=1 rows (zero duplicates among reserved=0) — removal is cleanly scoped. corp_type confirmed degenerate (158 corporation / 7 combine). Consumption side has zero corp/Standalone/Tenant scaffolding: the merge point is greenfield, natural seam is read_body_settlements (server/src/atlas/city_context_reader.rs:268) feeding match_cities (server/src/atlas/attractor_matching.rs).', 'in_progress', 'high', NULL, 'server', 'D-242', '2026-06-14 15:29:50', '2026-07-16 07:04:05.817', NULL, 'a72fe7f08d9e3b0b3231d654d5693fc0', 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 ('06FCDG73S4ET8KKTK62Z5KFEQM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Bake per-settlement population + settlement_class into the install dataset (only position stays seed-derived)', 'Per D-242. population + settlement_class are install-baked, not seed-derived.
Today: body-level population is baked (273/3240 bodies > 0), but per-city population is 0 and settlement_class NULL for all 463 atlas_city_names rows. D-223 deferred these ''to placement (T-955)'', but match_cities only READS them for tiering — nothing assigns them, so the pop-from-seed step was never built and all cities fall to synthetic placement.
Bake the per-settlement population spread + settlement_class into the source data (importer/source TOMLs). Only POSITION stays seed-derived (D-211). Drop the never-built seed-population idea.
Supersedes the D-223 deferred-pop/class note. Enables significance-ranked hub selection.
---
Refinement (2026-07-16, /whats-next Si review + gap resolution):
1. Population source (gap): RESOLUTION — pure importer derivation. import_economics computes the per-city population spread from the authored bodies.population on every regen-db run via a documented rank-size (Zipf-style) curve; no per-city population source file. Keep the curve constants named + documented in the importer — tuning is a code change by design. Only POSITION stays seed-derived (D-211). Amend D-242''s "carried down into the city/settlement source data" wording while implementing, to record the derivation rule (derivation-at-import, not hand-authoring).
2. settlement_class assignment (gap): RESOLUTION — default every pooled city to PopulationBudget; the D-196 thresholds (>=50k active / <5k ghost) decide state from the baked population. An authored NameLocked override list (small new source TOML) pins hero cities. EconomicTriggered and OrganicGrowth are simulation-time classes per D-196 — out of bake scope.
Si verification notes: 463 atlas_city_names rows, all population=0 / settlement_class NULL — claim exact. match_cities (server/src/atlas/attractor_matching.rs) only READS these fields for tiering; nothing assigns them. D-242/D-223/D-211/D-196 chain is consistent; no conflicting open Q-records. Importer entry point: populate_atlas_city_names (tooling/economy-db/economy_import/atlas.py:61-141).', 'in_progress', 'high', NULL, 'server', 'D-242', '2026-06-14 15:29:50', '2026-07-16 07:04:05.825', NULL, '9c034b37c1de54fe0049e6c37769aa66', 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 ('06FCDG73NW7SNCA5Y8JN905QMR', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Corp HQ settlement model — remove HQs from the city pool; specialization-keyed standalone-vs-tenant; install-baked', 'Per D-242. Corp HQs are not cities and must leave atlas_city_names.
1. Remove the D-207/D-223 reserved=1 corp-HQ cross-reference (populate_atlas_city_names_corps, tooling/economy-db/economy_import/atlas.py) — it inserts one atlas_city_names row per corp HQ, creating duplicate co-named cities (e.g. 10 Groombridge rows on GJ380c).
2. Author a source-canonical specialization -> HQ-placement map ({CityTenant, Standalone}). corp_type is degenerate (corporation/combine only); key on corporations.specialization (and/or supply_chain_role). Install-baked.
3. CityTenant HQs: a corp->city link, no new settlement (many corps may tenant one city).
4. Standalone HQs (extraction/heavy-industry/shipyards): emit their own settlement (body + position) into the cascade settlement list. Requires resolving headquarters_body (today 0/165 set; only headquarters_system + a most-populated-body heuristic).
5. Cascade settlement input becomes: city pool + standalone-HQ settlements, merged.
Amends D-207, D-223. Blocks the road-graph hub refinement.
---
Refinement (2026-07-16, /whats-next Si review + gap resolution):
1. Specialization key-space (gap): corporations.specialization is unconstrained free text — 20/165 rows hand-seeded prose via the ORPHANED tooling/populate-corporations.sh (raw sqlite3 INSERTs, single commit e05444f70, not in the Makefile or generator_sources.py; nothing in import_economics writes the column). supply_chain_role is 100% NULL, never wired. RESOLUTION: author a corp_specialization controlled vocabulary + source-canonical TOML (mirror the D-237 wiki/economics/specialization_vocabulary.toml pattern), backfill all 165 corps, and key the {CityTenant, Standalone} placement map on it — all in this ticket. Retire/supersede the orphaned populate-corporations.sh as part of the cleanup (hand-seeded DB values violate the source-canonical rule).
2. headquarters_body resolution (gap): RESOLUTION — explicitly reuse the most-populated-body-in-system heuristic from populate_atlas_city_names_corps (tooling/economy-db/economy_import/atlas.py:229-233) at import/bake time to backfill headquarters_body for Standalone HQs. Deterministic, install-baked.
Si verification notes: all 30 duplicate-name groups in atlas_city_names are 100% reserved=1 rows (zero duplicates among reserved=0) — removal is cleanly scoped. corp_type confirmed degenerate (158 corporation / 7 combine). Consumption side has zero corp/Standalone/Tenant scaffolding: the merge point is greenfield, natural seam is read_body_settlements (server/src/atlas/city_context_reader.rs:268) feeding match_cities (server/src/atlas/attractor_matching.rs).', 'in_progress', 'high', 'dudley', 'server', 'D-242', '2026-06-14 15:29:50', '2026-07-16 07:33:41.104', NULL, 'b1c2de354b8fcf103f712363125647ef', 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 ('06FCDG73S4ET8KKTK62Z5KFEQM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Bake per-settlement population + settlement_class into the install dataset (only position stays seed-derived)', 'Per D-242. population + settlement_class are install-baked, not seed-derived.
Today: body-level population is baked (273/3240 bodies > 0), but per-city population is 0 and settlement_class NULL for all 463 atlas_city_names rows. D-223 deferred these ''to placement (T-955)'', but match_cities only READS them for tiering — nothing assigns them, so the pop-from-seed step was never built and all cities fall to synthetic placement.
Bake the per-settlement population spread + settlement_class into the source data (importer/source TOMLs). Only POSITION stays seed-derived (D-211). Drop the never-built seed-population idea.
Supersedes the D-223 deferred-pop/class note. Enables significance-ranked hub selection.
---
Refinement (2026-07-16, /whats-next Si review + gap resolution):
1. Population source (gap): RESOLUTION — pure importer derivation. import_economics computes the per-city population spread from the authored bodies.population on every regen-db run via a documented rank-size (Zipf-style) curve; no per-city population source file. Keep the curve constants named + documented in the importer — tuning is a code change by design. Only POSITION stays seed-derived (D-211). Amend D-242''s "carried down into the city/settlement source data" wording while implementing, to record the derivation rule (derivation-at-import, not hand-authoring).
2. settlement_class assignment (gap): RESOLUTION — default every pooled city to PopulationBudget; the D-196 thresholds (>=50k active / <5k ghost) decide state from the baked population. An authored NameLocked override list (small new source TOML) pins hero cities. EconomicTriggered and OrganicGrowth are simulation-time classes per D-196 — out of bake scope.
Si verification notes: 463 atlas_city_names rows, all population=0 / settlement_class NULL — claim exact. match_cities (server/src/atlas/attractor_matching.rs) only READS these fields for tiering; nothing assigns them. D-242/D-223/D-211/D-196 chain is consistent; no conflicting open Q-records. Importer entry point: populate_atlas_city_names (tooling/economy-db/economy_import/atlas.py:61-141).', 'in_progress', 'high', 'dudley', 'server', 'D-242', '2026-06-14 15:29:50', '2026-07-16 07:33:41.107', NULL, '8e4c430b5a1b6f997e9240de03aef474', 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 ('06FPM32KT5Z1JBP9BCN2KC53EG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wiki content: corp-placement coverage gaps — 10 pageless corps + GJ702B city names', 'Follow-up from T-1074 (D-242). Two wiki content gaps cap corp-HQ placement coverage: (1) 10 legacy DB-only corps (mvg, adams-ford, dsmc, 7 Cygni-B combines) have no wiki/corporations page, so no corp_specialization/hq_placement/headquarters_body — left NULL, non-fatal; they remain load-bearing for 2468 brand_products rows so rows were kept. Author pages (or record a deferral). (2) GJ 702B (Prometheus) has zero authored city names anywhere in the system, so prometheus-labs + kovalev-freight are logged unmatched CityTenant corps — the Reach''s longevity-monopoly hero system needs city names in its markers pool. Note: sova-station-works'' broken headquarters ref is NOT this ticket — it resolves under T-1054.', 'backlog', 'low', NULL, NULL, NULL, '2026-07-16 08:30:20.625', '2026-07-16 08:30:20.625', NULL, '97b7995fe551f248a4d28b00373c3c2e', 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 ('06FCDG73NW7SNCA5Y8JN905QMR', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Corp HQ settlement model — remove HQs from the city pool; specialization-keyed standalone-vs-tenant; install-baked', 'Per D-242. Corp HQs are not cities and must leave atlas_city_names.
1. Remove the D-207/D-223 reserved=1 corp-HQ cross-reference (populate_atlas_city_names_corps, tooling/economy-db/economy_import/atlas.py) — it inserts one atlas_city_names row per corp HQ, creating duplicate co-named cities (e.g. 10 Groombridge rows on GJ380c).
2. Author a source-canonical specialization -> HQ-placement map ({CityTenant, Standalone}). corp_type is degenerate (corporation/combine only); key on corporations.specialization (and/or supply_chain_role). Install-baked.
3. CityTenant HQs: a corp->city link, no new settlement (many corps may tenant one city).
4. Standalone HQs (extraction/heavy-industry/shipyards): emit their own settlement (body + position) into the cascade settlement list. Requires resolving headquarters_body (today 0/165 set; only headquarters_system + a most-populated-body heuristic).
5. Cascade settlement input becomes: city pool + standalone-HQ settlements, merged.
Amends D-207, D-223. Blocks the road-graph hub refinement.
---
Refinement (2026-07-16, /whats-next Si review + gap resolution):
1. Specialization key-space (gap): corporations.specialization is unconstrained free text — 20/165 rows hand-seeded prose via the ORPHANED tooling/populate-corporations.sh (raw sqlite3 INSERTs, single commit e05444f70, not in the Makefile or generator_sources.py; nothing in import_economics writes the column). supply_chain_role is 100% NULL, never wired. RESOLUTION: author a corp_specialization controlled vocabulary + source-canonical TOML (mirror the D-237 wiki/economics/specialization_vocabulary.toml pattern), backfill all 165 corps, and key the {CityTenant, Standalone} placement map on it — all in this ticket. Retire/supersede the orphaned populate-corporations.sh as part of the cleanup (hand-seeded DB values violate the source-canonical rule).
2. headquarters_body resolution (gap): RESOLUTION — explicitly reuse the most-populated-body-in-system heuristic from populate_atlas_city_names_corps (tooling/economy-db/economy_import/atlas.py:229-233) at import/bake time to backfill headquarters_body for Standalone HQs. Deterministic, install-baked.
Si verification notes: all 30 duplicate-name groups in atlas_city_names are 100% reserved=1 rows (zero duplicates among reserved=0) — removal is cleanly scoped. corp_type confirmed degenerate (158 corporation / 7 combine). Consumption side has zero corp/Standalone/Tenant scaffolding: the merge point is greenfield, natural seam is read_body_settlements (server/src/atlas/city_context_reader.rs:268) feeding match_cities (server/src/atlas/attractor_matching.rs).', 'review', 'high', 'dudley', 'server', 'D-242', '2026-06-14 15:29:50', '2026-07-16 09:49:28.595', NULL, '81320fcdc7ae3a4caedb4b32cb1d6822', 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 ('06FCDG73S4ET8KKTK62Z5KFEQM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Bake per-settlement population + settlement_class into the install dataset (only position stays seed-derived)', 'Per D-242. population + settlement_class are install-baked, not seed-derived.
Today: body-level population is baked (273/3240 bodies > 0), but per-city population is 0 and settlement_class NULL for all 463 atlas_city_names rows. D-223 deferred these ''to placement (T-955)'', but match_cities only READS them for tiering — nothing assigns them, so the pop-from-seed step was never built and all cities fall to synthetic placement.
Bake the per-settlement population spread + settlement_class into the source data (importer/source TOMLs). Only POSITION stays seed-derived (D-211). Drop the never-built seed-population idea.
Supersedes the D-223 deferred-pop/class note. Enables significance-ranked hub selection.
---
Refinement (2026-07-16, /whats-next Si review + gap resolution):
1. Population source (gap): RESOLUTION — pure importer derivation. import_economics computes the per-city population spread from the authored bodies.population on every regen-db run via a documented rank-size (Zipf-style) curve; no per-city population source file. Keep the curve constants named + documented in the importer — tuning is a code change by design. Only POSITION stays seed-derived (D-211). Amend D-242''s "carried down into the city/settlement source data" wording while implementing, to record the derivation rule (derivation-at-import, not hand-authoring).
2. settlement_class assignment (gap): RESOLUTION — default every pooled city to PopulationBudget; the D-196 thresholds (>=50k active / <5k ghost) decide state from the baked population. An authored NameLocked override list (small new source TOML) pins hero cities. EconomicTriggered and OrganicGrowth are simulation-time classes per D-196 — out of bake scope.
Si verification notes: 463 atlas_city_names rows, all population=0 / settlement_class NULL — claim exact. match_cities (server/src/atlas/attractor_matching.rs) only READS these fields for tiering; nothing assigns them. D-242/D-223/D-211/D-196 chain is consistent; no conflicting open Q-records. Importer entry point: populate_atlas_city_names (tooling/economy-db/economy_import/atlas.py:61-141).', 'review', 'high', 'dudley', 'server', 'D-242', '2026-06-14 15:29:50', '2026-07-16 09:49:28.604', NULL, 'f8adeb8ec7747d76448aca85bc5f6020', 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 ('06FPM32KT5Z1JBP9BCN2KC53EG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wiki content: corp-placement coverage gaps — 10 pageless corps + GJ702B city names', 'Follow-up from T-1074 (D-242). Two wiki content gaps cap corp-HQ placement coverage: (1) 10 legacy DB-only corps (mvg, adams-ford, dsmc, 7 Cygni-B combines) have no wiki/corporations page, so no corp_specialization/hq_placement/headquarters_body — left NULL, non-fatal; they remain load-bearing for 2468 brand_products rows so rows were kept. Author pages (or record a deferral). (2) GJ 702B (Prometheus) has zero authored city names anywhere in the system, so prometheus-labs + kovalev-freight are logged unmatched CityTenant corps — the Reach''s longevity-monopoly hero system needs city names in its markers pool. Note: sova-station-works'' broken headquarters ref is NOT this ticket — it resolves under T-1054.', 'backlog', 'low', NULL, 'copy', NULL, '2026-07-16 08:30:20.625', '2026-07-16 10:16:18.763', NULL, '9e9055326c3c23b9252f3890767230a5', 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 ('06FCDG73NW7SNCA5Y8JN905QMR', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Corp HQ settlement model — remove HQs from the city pool; specialization-keyed standalone-vs-tenant; install-baked', 'Per D-242. Corp HQs are not cities and must leave atlas_city_names.
1. Remove the D-207/D-223 reserved=1 corp-HQ cross-reference (populate_atlas_city_names_corps, tooling/economy-db/economy_import/atlas.py) — it inserts one atlas_city_names row per corp HQ, creating duplicate co-named cities (e.g. 10 Groombridge rows on GJ380c).
2. Author a source-canonical specialization -> HQ-placement map ({CityTenant, Standalone}). corp_type is degenerate (corporation/combine only); key on corporations.specialization (and/or supply_chain_role). Install-baked.
3. CityTenant HQs: a corp->city link, no new settlement (many corps may tenant one city).
4. Standalone HQs (extraction/heavy-industry/shipyards): emit their own settlement (body + position) into the cascade settlement list. Requires resolving headquarters_body (today 0/165 set; only headquarters_system + a most-populated-body heuristic).
5. Cascade settlement input becomes: city pool + standalone-HQ settlements, merged.
Amends D-207, D-223. Blocks the road-graph hub refinement.
---
Refinement (2026-07-16, /whats-next Si review + gap resolution):
1. Specialization key-space (gap): corporations.specialization is unconstrained free text — 20/165 rows hand-seeded prose via the ORPHANED tooling/populate-corporations.sh (raw sqlite3 INSERTs, single commit e05444f70, not in the Makefile or generator_sources.py; nothing in import_economics writes the column). supply_chain_role is 100% NULL, never wired. RESOLUTION: author a corp_specialization controlled vocabulary + source-canonical TOML (mirror the D-237 wiki/economics/specialization_vocabulary.toml pattern), backfill all 165 corps, and key the {CityTenant, Standalone} placement map on it — all in this ticket. Retire/supersede the orphaned populate-corporations.sh as part of the cleanup (hand-seeded DB values violate the source-canonical rule).
2. headquarters_body resolution (gap): RESOLUTION — explicitly reuse the most-populated-body-in-system heuristic from populate_atlas_city_names_corps (tooling/economy-db/economy_import/atlas.py:229-233) at import/bake time to backfill headquarters_body for Standalone HQs. Deterministic, install-baked.
Si verification notes: all 30 duplicate-name groups in atlas_city_names are 100% reserved=1 rows (zero duplicates among reserved=0) — removal is cleanly scoped. corp_type confirmed degenerate (158 corporation / 7 combine). Consumption side has zero corp/Standalone/Tenant scaffolding: the merge point is greenfield, natural seam is read_body_settlements (server/src/atlas/city_context_reader.rs:268) feeding match_cities (server/src/atlas/attractor_matching.rs).', 'done', 'high', 'dudley', 'server', 'D-242', '2026-06-14 15:29:50', '2026-07-16 11:08:50.978', NULL, '21ea17fd54d054f8b1034f388025e327', 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 ('06FCDG73S4ET8KKTK62Z5KFEQM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Bake per-settlement population + settlement_class into the install dataset (only position stays seed-derived)', 'Per D-242. population + settlement_class are install-baked, not seed-derived.
Today: body-level population is baked (273/3240 bodies > 0), but per-city population is 0 and settlement_class NULL for all 463 atlas_city_names rows. D-223 deferred these ''to placement (T-955)'', but match_cities only READS them for tiering — nothing assigns them, so the pop-from-seed step was never built and all cities fall to synthetic placement.
Bake the per-settlement population spread + settlement_class into the source data (importer/source TOMLs). Only POSITION stays seed-derived (D-211). Drop the never-built seed-population idea.
Supersedes the D-223 deferred-pop/class note. Enables significance-ranked hub selection.
---
Refinement (2026-07-16, /whats-next Si review + gap resolution):
1. Population source (gap): RESOLUTION — pure importer derivation. import_economics computes the per-city population spread from the authored bodies.population on every regen-db run via a documented rank-size (Zipf-style) curve; no per-city population source file. Keep the curve constants named + documented in the importer — tuning is a code change by design. Only POSITION stays seed-derived (D-211). Amend D-242''s "carried down into the city/settlement source data" wording while implementing, to record the derivation rule (derivation-at-import, not hand-authoring).
2. settlement_class assignment (gap): RESOLUTION — default every pooled city to PopulationBudget; the D-196 thresholds (>=50k active / <5k ghost) decide state from the baked population. An authored NameLocked override list (small new source TOML) pins hero cities. EconomicTriggered and OrganicGrowth are simulation-time classes per D-196 — out of bake scope.
Si verification notes: 463 atlas_city_names rows, all population=0 / settlement_class NULL — claim exact. match_cities (server/src/atlas/attractor_matching.rs) only READS these fields for tiering; nothing assigns them. D-242/D-223/D-211/D-196 chain is consistent; no conflicting open Q-records. Importer entry point: populate_atlas_city_names (tooling/economy-db/economy_import/atlas.py:61-141).', 'done', 'high', 'dudley', 'server', 'D-242', '2026-06-14 15:29:50', '2026-07-16 11:08:50.980', NULL, '308acb6c5b17ed676ed92d81f3dc160f', 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 ('06FCDG73V9R6N024QSFDEJ4R68', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Road-graph hub refinement — scaled-cap hubs + co-location collapse + hybrid minor attach + RailHeadFacing junction pass', 'Follow-on to T-1038 (road-graph core shipped). Hub-selection refinement, gated on the resolved settlement model (D-242).
Build on server/src/atlas/road_graph.rs:
1. Hub selection: ''real cities, scaled cap'' — wiki cities anchor the MST; cap scales with body size; standalone HQs/minor settlements are non-hub.
2. Co-location collapse: group same-named settlements (defensive; the D-242 corp-HQ fix removes most at source).
3. Hybrid minor-settlement attach: snap to nearest existing road edge if close, else spur to nearest hub (add edge-split-at-attach).
4. RailHeadFacing junction pass (T-1038 step 6, deferred): junction settlements (RoadGraph::high_connectivity_junctions already computed) get FoundingOrientation::RailHeadFacing. REQUIRES a D-213 amendment (adopted enum has no RailHeadFacing) + Layer-4 skeleton_gen handling.
Needs population ranking — blocked on the corp-HQ-model + population-bake tickets.
---
Refinement (2026-07-16, /whats-next Si review + gap resolution with Jeroen):
SCOPE ITEM 0 (added — placement-map correction, precedes hub work): corp_hq_placement.toml over-generalized D-242''s Standalone boundary (bulk_class != NonPhysical -> Standalone gave 95 Standalone / 60 CityTenant, incl. distilleries/wineries/leatherworks as standalone belt settlements). Jeroen''s ruling, faithful to D-242''s own examples: Standalone ONLY where the HQ is itself an industrial complex / company town — extraction/mining/quarrying, heavy vehicle + shipyard manufacturing, heavy energy (refinery/extraction complexes), gate/orbital infrastructure. Everything else CityTenant, explicitly including craft/consumer manufacturing (distilleries, breweries, wineries, ceramics, textiles, furniture), agriculture (estate offices sit in market towns), and high-tech fabs. Re-author per-value (judge each of the 31 value descriptions against this boundary), regen, and amend D-242 to state the boundary explicitly so it cannot be re-coarsened; record Jeroen''s "edge of settlement / clusters" color as CityTenant rendering detail for later phases, not a third placement class.
1. Hub selection (gap: no cap formula, no HQ-row identity): RESOLUTION — thread an is_standalone_hq flag through read_body_settlements/CityRecord (join corporations.headquarters_body + proper_name where hq_placement=''Standalone''; the city_context_reader.rs:1165-1170 test comment predicted exactly this threading). Hubs = top-cap by population among NON-HQ cities; cap scales with body size via a documented inline constant (existing workshop-tunable pattern in road_graph.rs), sanity-checked on 2-3 bodies of different sizes.
2. Co-location collapse (gap: zero live cases, no rule): RESOLUTION — ship trivial defensive: exact (body_id, name) match collapses to one node keeping lowest id, loud log warning when it ever fires.
3. Hybrid minor-settlement attach (gap: no snap threshold, no edge-split machinery): RESOLUTION — engineer''s judgment: design the edge-split representation + pick the snap threshold as a documented inline constant; extend the RoadEdge/RoadNode invariants and the ten existing unit tests accordingly.
4. RailHeadFacing (well-specified per Si): add the FoundingOrientation variant (consumers use if-let, no exhaustive-match break), assign via existing RoadGraph::high_connectivity_junctions (degree >= 3, matches D-213''s paula-round3 source), amend D-213, and make the skeleton_gen rotation call inline (Si''s suggested default: face the junction''s dominant incident-edge bearing).
Deferred (no preference given): standalone-HQ Zipf population shares stay as-is (e.g. Gate Corporation 909M on GJ251c); the item-0 re-tune shrinks standalone rows to genuine company-towns where a real share is defensible. Revisit if Atlas numbers read wrong.
Si verification notes: road_graph.rs has NO hub concept and NO edge-split machinery today (T-1038 treats every placement as a uniform MST node); RoadGraph::high_connectivity_junctions() exists (JUNCTION_DEGREE=3); FoundingOrientation consumers are founding_orientation() and coastal_edge() (if-let, safe); skeleton_gen has zero rail-facing rotation logic (new work, scoped); PR #177 data preconditions verified (population>0 + settlement_class on all rows; standalone HQs present as ordinary rows).', 'backlog', 'medium', NULL, 'server', 'D-242', '2026-06-14 15:29:50', '2026-07-16 13:34:35.724', NULL, '532facc26c7b83e10e29b101b608f82f', 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 ('06FCDG73V9R6N024QSFDEJ4R68', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Road-graph hub refinement — scaled-cap hubs + co-location collapse + hybrid minor attach + RailHeadFacing junction pass', 'Follow-on to T-1038 (road-graph core shipped). Hub-selection refinement, gated on the resolved settlement model (D-242).
Build on server/src/atlas/road_graph.rs:
1. Hub selection: ''real cities, scaled cap'' — wiki cities anchor the MST; cap scales with body size; standalone HQs/minor settlements are non-hub.
2. Co-location collapse: group same-named settlements (defensive; the D-242 corp-HQ fix removes most at source).
3. Hybrid minor-settlement attach: snap to nearest existing road edge if close, else spur to nearest hub (add edge-split-at-attach).
4. RailHeadFacing junction pass (T-1038 step 6, deferred): junction settlements (RoadGraph::high_connectivity_junctions already computed) get FoundingOrientation::RailHeadFacing. REQUIRES a D-213 amendment (adopted enum has no RailHeadFacing) + Layer-4 skeleton_gen handling.
Needs population ranking — blocked on the corp-HQ-model + population-bake tickets.
---
Refinement (2026-07-16, /whats-next Si review + gap resolution with Jeroen):
SCOPE ITEM 0 (added — placement-map correction, precedes hub work): corp_hq_placement.toml over-generalized D-242''s Standalone boundary (bulk_class != NonPhysical -> Standalone gave 95 Standalone / 60 CityTenant, incl. distilleries/wineries/leatherworks as standalone belt settlements). Jeroen''s ruling, faithful to D-242''s own examples: Standalone ONLY where the HQ is itself an industrial complex / company town — extraction/mining/quarrying, heavy vehicle + shipyard manufacturing, heavy energy (refinery/extraction complexes), gate/orbital infrastructure. Everything else CityTenant, explicitly including craft/consumer manufacturing (distilleries, breweries, wineries, ceramics, textiles, furniture), agriculture (estate offices sit in market towns), and high-tech fabs. Re-author per-value (judge each of the 31 value descriptions against this boundary), regen, and amend D-242 to state the boundary explicitly so it cannot be re-coarsened; record Jeroen''s "edge of settlement / clusters" color as CityTenant rendering detail for later phases, not a third placement class.
1. Hub selection (gap: no cap formula, no HQ-row identity): RESOLUTION — thread an is_standalone_hq flag through read_body_settlements/CityRecord (join corporations.headquarters_body + proper_name where hq_placement=''Standalone''; the city_context_reader.rs:1165-1170 test comment predicted exactly this threading). Hubs = top-cap by population among NON-HQ cities; cap scales with body size via a documented inline constant (existing workshop-tunable pattern in road_graph.rs), sanity-checked on 2-3 bodies of different sizes.
2. Co-location collapse (gap: zero live cases, no rule): RESOLUTION — ship trivial defensive: exact (body_id, name) match collapses to one node keeping lowest id, loud log warning when it ever fires.
3. Hybrid minor-settlement attach (gap: no snap threshold, no edge-split machinery): RESOLUTION — engineer''s judgment: design the edge-split representation + pick the snap threshold as a documented inline constant; extend the RoadEdge/RoadNode invariants and the ten existing unit tests accordingly.
4. RailHeadFacing (well-specified per Si): add the FoundingOrientation variant (consumers use if-let, no exhaustive-match break), assign via existing RoadGraph::high_connectivity_junctions (degree >= 3, matches D-213''s paula-round3 source), amend D-213, and make the skeleton_gen rotation call inline (Si''s suggested default: face the junction''s dominant incident-edge bearing).
Deferred (no preference given): standalone-HQ Zipf population shares stay as-is (e.g. Gate Corporation 909M on GJ251c); the item-0 re-tune shrinks standalone rows to genuine company-towns where a real share is defensible. Revisit if Atlas numbers read wrong.
Si verification notes: road_graph.rs has NO hub concept and NO edge-split machinery today (T-1038 treats every placement as a uniform MST node); RoadGraph::high_connectivity_junctions() exists (JUNCTION_DEGREE=3); FoundingOrientation consumers are founding_orientation() and coastal_edge() (if-let, safe); skeleton_gen has zero rail-facing rotation logic (new work, scoped); PR #177 data preconditions verified (population>0 + settlement_class on all rows; standalone HQs present as ordinary rows).', 'in_progress', 'medium', NULL, 'server', 'D-242', '2026-06-14 15:29:50', '2026-07-16 13:34:42.064', NULL, '0a0a8e3b1ef8ae909a9e1fd756529e8a', 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 ('06FCDG73V9R6N024QSFDEJ4R68', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Road-graph hub refinement — scaled-cap hubs + co-location collapse + hybrid minor attach + RailHeadFacing junction pass', 'Follow-on to T-1038 (road-graph core shipped). Hub-selection refinement, gated on the resolved settlement model (D-242).
Build on server/src/atlas/road_graph.rs:
1. Hub selection: ''real cities, scaled cap'' — wiki cities anchor the MST; cap scales with body size; standalone HQs/minor settlements are non-hub.
2. Co-location collapse: group same-named settlements (defensive; the D-242 corp-HQ fix removes most at source).
3. Hybrid minor-settlement attach: snap to nearest existing road edge if close, else spur to nearest hub (add edge-split-at-attach).
4. RailHeadFacing junction pass (T-1038 step 6, deferred): junction settlements (RoadGraph::high_connectivity_junctions already computed) get FoundingOrientation::RailHeadFacing. REQUIRES a D-213 amendment (adopted enum has no RailHeadFacing) + Layer-4 skeleton_gen handling.
Needs population ranking — blocked on the corp-HQ-model + population-bake tickets.
---
Refinement (2026-07-16, /whats-next Si review + gap resolution with Jeroen):
SCOPE ITEM 0 (added — placement-map correction, precedes hub work): corp_hq_placement.toml over-generalized D-242''s Standalone boundary (bulk_class != NonPhysical -> Standalone gave 95 Standalone / 60 CityTenant, incl. distilleries/wineries/leatherworks as standalone belt settlements). Jeroen''s ruling, faithful to D-242''s own examples: Standalone ONLY where the HQ is itself an industrial complex / company town — extraction/mining/quarrying, heavy vehicle + shipyard manufacturing, heavy energy (refinery/extraction complexes), gate/orbital infrastructure. Everything else CityTenant, explicitly including craft/consumer manufacturing (distilleries, breweries, wineries, ceramics, textiles, furniture), agriculture (estate offices sit in market towns), and high-tech fabs. Re-author per-value (judge each of the 31 value descriptions against this boundary), regen, and amend D-242 to state the boundary explicitly so it cannot be re-coarsened; record Jeroen''s "edge of settlement / clusters" color as CityTenant rendering detail for later phases, not a third placement class.
1. Hub selection (gap: no cap formula, no HQ-row identity): RESOLUTION — thread an is_standalone_hq flag through read_body_settlements/CityRecord (join corporations.headquarters_body + proper_name where hq_placement=''Standalone''; the city_context_reader.rs:1165-1170 test comment predicted exactly this threading). Hubs = top-cap by population among NON-HQ cities; cap scales with body size via a documented inline constant (existing workshop-tunable pattern in road_graph.rs), sanity-checked on 2-3 bodies of different sizes.
2. Co-location collapse (gap: zero live cases, no rule): RESOLUTION — ship trivial defensive: exact (body_id, name) match collapses to one node keeping lowest id, loud log warning when it ever fires.
3. Hybrid minor-settlement attach (gap: no snap threshold, no edge-split machinery): RESOLUTION — engineer''s judgment: design the edge-split representation + pick the snap threshold as a documented inline constant; extend the RoadEdge/RoadNode invariants and the ten existing unit tests accordingly.
4. RailHeadFacing (well-specified per Si): add the FoundingOrientation variant (consumers use if-let, no exhaustive-match break), assign via existing RoadGraph::high_connectivity_junctions (degree >= 3, matches D-213''s paula-round3 source), amend D-213, and make the skeleton_gen rotation call inline (Si''s suggested default: face the junction''s dominant incident-edge bearing).
Deferred (no preference given): standalone-HQ Zipf population shares stay as-is (e.g. Gate Corporation 909M on GJ251c); the item-0 re-tune shrinks standalone rows to genuine company-towns where a real share is defensible. Revisit if Atlas numbers read wrong.
Si verification notes: road_graph.rs has NO hub concept and NO edge-split machinery today (T-1038 treats every placement as a uniform MST node); RoadGraph::high_connectivity_junctions() exists (JUNCTION_DEGREE=3); FoundingOrientation consumers are founding_orientation() and coastal_edge() (if-let, safe); skeleton_gen has zero rail-facing rotation logic (new work, scoped); PR #177 data preconditions verified (population>0 + settlement_class on all rows; standalone HQs present as ordinary rows).', 'in_progress', 'medium', 'dudley', 'server', 'D-242', '2026-06-14 15:29:50', '2026-07-16 13:34:49.724', NULL, '976f4eba75344d6e3f0ab9ba6393a5df', 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 ('06FPPM0KP850FN6WQ5PDEX9ZS4', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Road graph: 0 routable edges on water-heavy bodies — placements land in water-majority routing cells, every A* pair drops', 'Found during T-1076 (road-hub refinement) verification: several real bodies (GJ251c, GJ380c) produce 0 routable road edges because settlement placements land in water-majority routing cells, so every A* pair is dropped. PRE-EXISTING behavior (T-1038''s core had the same drop path) but now more visible with hub/spur semantics and on the Atlas road overlay. Candidate fixes per Dudley: coastal-cell routing relaxation (allow routing through coast-adjacent cells at a cost penalty) or a placement nudge (settlements snap out of water-majority cells at L3). Affects the Atlas planet-browsing experience directly: water-heavy bodies show settlements but no roads.', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-16 14:23:57.874', '2026-07-16 14:23:57.874', NULL, '91a60e09275dd3bd232078fbe8bbb8b3', 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 ('06FCDG73V9R6N024QSFDEJ4R68', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Road-graph hub refinement — scaled-cap hubs + co-location collapse + hybrid minor attach + RailHeadFacing junction pass', 'Follow-on to T-1038 (road-graph core shipped). Hub-selection refinement, gated on the resolved settlement model (D-242).
Build on server/src/atlas/road_graph.rs:
1. Hub selection: ''real cities, scaled cap'' — wiki cities anchor the MST; cap scales with body size; standalone HQs/minor settlements are non-hub.
2. Co-location collapse: group same-named settlements (defensive; the D-242 corp-HQ fix removes most at source).
3. Hybrid minor-settlement attach: snap to nearest existing road edge if close, else spur to nearest hub (add edge-split-at-attach).
4. RailHeadFacing junction pass (T-1038 step 6, deferred): junction settlements (RoadGraph::high_connectivity_junctions already computed) get FoundingOrientation::RailHeadFacing. REQUIRES a D-213 amendment (adopted enum has no RailHeadFacing) + Layer-4 skeleton_gen handling.
Needs population ranking — blocked on the corp-HQ-model + population-bake tickets.
---
Refinement (2026-07-16, /whats-next Si review + gap resolution with Jeroen):
SCOPE ITEM 0 (added — placement-map correction, precedes hub work): corp_hq_placement.toml over-generalized D-242''s Standalone boundary (bulk_class != NonPhysical -> Standalone gave 95 Standalone / 60 CityTenant, incl. distilleries/wineries/leatherworks as standalone belt settlements). Jeroen''s ruling, faithful to D-242''s own examples: Standalone ONLY where the HQ is itself an industrial complex / company town — extraction/mining/quarrying, heavy vehicle + shipyard manufacturing, heavy energy (refinery/extraction complexes), gate/orbital infrastructure. Everything else CityTenant, explicitly including craft/consumer manufacturing (distilleries, breweries, wineries, ceramics, textiles, furniture), agriculture (estate offices sit in market towns), and high-tech fabs. Re-author per-value (judge each of the 31 value descriptions against this boundary), regen, and amend D-242 to state the boundary explicitly so it cannot be re-coarsened; record Jeroen''s "edge of settlement / clusters" color as CityTenant rendering detail for later phases, not a third placement class.
1. Hub selection (gap: no cap formula, no HQ-row identity): RESOLUTION — thread an is_standalone_hq flag through read_body_settlements/CityRecord (join corporations.headquarters_body + proper_name where hq_placement=''Standalone''; the city_context_reader.rs:1165-1170 test comment predicted exactly this threading). Hubs = top-cap by population among NON-HQ cities; cap scales with body size via a documented inline constant (existing workshop-tunable pattern in road_graph.rs), sanity-checked on 2-3 bodies of different sizes.
2. Co-location collapse (gap: zero live cases, no rule): RESOLUTION — ship trivial defensive: exact (body_id, name) match collapses to one node keeping lowest id, loud log warning when it ever fires.
3. Hybrid minor-settlement attach (gap: no snap threshold, no edge-split machinery): RESOLUTION — engineer''s judgment: design the edge-split representation + pick the snap threshold as a documented inline constant; extend the RoadEdge/RoadNode invariants and the ten existing unit tests accordingly.
4. RailHeadFacing (well-specified per Si): add the FoundingOrientation variant (consumers use if-let, no exhaustive-match break), assign via existing RoadGraph::high_connectivity_junctions (degree >= 3, matches D-213''s paula-round3 source), amend D-213, and make the skeleton_gen rotation call inline (Si''s suggested default: face the junction''s dominant incident-edge bearing).
Deferred (no preference given): standalone-HQ Zipf population shares stay as-is (e.g. Gate Corporation 909M on GJ251c); the item-0 re-tune shrinks standalone rows to genuine company-towns where a real share is defensible. Revisit if Atlas numbers read wrong.
Si verification notes: road_graph.rs has NO hub concept and NO edge-split machinery today (T-1038 treats every placement as a uniform MST node); RoadGraph::high_connectivity_junctions() exists (JUNCTION_DEGREE=3); FoundingOrientation consumers are founding_orientation() and coastal_edge() (if-let, safe); skeleton_gen has zero rail-facing rotation logic (new work, scoped); PR #177 data preconditions verified (population>0 + settlement_class on all rows; standalone HQs present as ordinary rows).', 'review', 'medium', 'dudley', 'server', 'D-242', '2026-06-14 15:29:50', '2026-07-16 14:25:25.189', NULL, '6489dd0ff1f3803b055b0911e9c3b62e', 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 ('06FPPYPF4PCTW99YMKXPA60Y40', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'heavy_works vocabulary value — split civilian heavy industry off military_industrial', 'PR #178 re-review (Tyre). The M1 re-tag parked three CIVILIAN heavy-works corps (sova-station-works habitat modules, stalownia-kowalski ore-processing machinery, westphalia-heavy-works Compact heavy equipment) on military_industrial — placement-class correct, but that value''s standalone_economic_role=''military'' is NOT inert: it feeds the D-211 CompatibilityMatrix military row (attractor_matching.rs:113, placement scoring — LIVE today) and political_archetype -> Military -> FortifiedPerimeter garrison geometry (attractor_matching.rs:659/686 — latent; masks to Pioneer today only because GJ896A/GJ601A lack system_factions data). Fix per the D-242 amendment note: add a heavy_works vocabulary value (D-237 pattern; anchor heavy_equipment, BulkSolid, Specialist, standalone_economic_role=manufacturing, Standalone placement), re-tag the three corps, regen. NEVER re-coarsen general_industrial (recorded non-regressive rule). Also consider whether sede-chemical-works (currently fuel_production, wrong commodity fusion_fuel vs chemical feedstock) belongs on a chemical/processing value in the same pass — Miri flagged it as the loosest fit.', 'backlog', 'medium', NULL, 'server', 'D-242', '2026-07-16 15:10:38.373', '2026-07-16 15:10:38.373', NULL, '7ddcf9c15c2a93b3b0543fb69d4d3804', 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 ('06FCDG73V9R6N024QSFDEJ4R68', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Road-graph hub refinement — scaled-cap hubs + co-location collapse + hybrid minor attach + RailHeadFacing junction pass', 'Follow-on to T-1038 (road-graph core shipped). Hub-selection refinement, gated on the resolved settlement model (D-242).
Build on server/src/atlas/road_graph.rs:
1. Hub selection: ''real cities, scaled cap'' — wiki cities anchor the MST; cap scales with body size; standalone HQs/minor settlements are non-hub.
2. Co-location collapse: group same-named settlements (defensive; the D-242 corp-HQ fix removes most at source).
3. Hybrid minor-settlement attach: snap to nearest existing road edge if close, else spur to nearest hub (add edge-split-at-attach).
4. RailHeadFacing junction pass (T-1038 step 6, deferred): junction settlements (RoadGraph::high_connectivity_junctions already computed) get FoundingOrientation::RailHeadFacing. REQUIRES a D-213 amendment (adopted enum has no RailHeadFacing) + Layer-4 skeleton_gen handling.
Needs population ranking — blocked on the corp-HQ-model + population-bake tickets.
---
Refinement (2026-07-16, /whats-next Si review + gap resolution with Jeroen):
SCOPE ITEM 0 (added — placement-map correction, precedes hub work): corp_hq_placement.toml over-generalized D-242''s Standalone boundary (bulk_class != NonPhysical -> Standalone gave 95 Standalone / 60 CityTenant, incl. distilleries/wineries/leatherworks as standalone belt settlements). Jeroen''s ruling, faithful to D-242''s own examples: Standalone ONLY where the HQ is itself an industrial complex / company town — extraction/mining/quarrying, heavy vehicle + shipyard manufacturing, heavy energy (refinery/extraction complexes), gate/orbital infrastructure. Everything else CityTenant, explicitly including craft/consumer manufacturing (distilleries, breweries, wineries, ceramics, textiles, furniture), agriculture (estate offices sit in market towns), and high-tech fabs. Re-author per-value (judge each of the 31 value descriptions against this boundary), regen, and amend D-242 to state the boundary explicitly so it cannot be re-coarsened; record Jeroen''s "edge of settlement / clusters" color as CityTenant rendering detail for later phases, not a third placement class.
1. Hub selection (gap: no cap formula, no HQ-row identity): RESOLUTION — thread an is_standalone_hq flag through read_body_settlements/CityRecord (join corporations.headquarters_body + proper_name where hq_placement=''Standalone''; the city_context_reader.rs:1165-1170 test comment predicted exactly this threading). Hubs = top-cap by population among NON-HQ cities; cap scales with body size via a documented inline constant (existing workshop-tunable pattern in road_graph.rs), sanity-checked on 2-3 bodies of different sizes.
2. Co-location collapse (gap: zero live cases, no rule): RESOLUTION — ship trivial defensive: exact (body_id, name) match collapses to one node keeping lowest id, loud log warning when it ever fires.
3. Hybrid minor-settlement attach (gap: no snap threshold, no edge-split machinery): RESOLUTION — engineer''s judgment: design the edge-split representation + pick the snap threshold as a documented inline constant; extend the RoadEdge/RoadNode invariants and the ten existing unit tests accordingly.
4. RailHeadFacing (well-specified per Si): add the FoundingOrientation variant (consumers use if-let, no exhaustive-match break), assign via existing RoadGraph::high_connectivity_junctions (degree >= 3, matches D-213''s paula-round3 source), amend D-213, and make the skeleton_gen rotation call inline (Si''s suggested default: face the junction''s dominant incident-edge bearing).
Deferred (no preference given): standalone-HQ Zipf population shares stay as-is (e.g. Gate Corporation 909M on GJ251c); the item-0 re-tune shrinks standalone rows to genuine company-towns where a real share is defensible. Revisit if Atlas numbers read wrong.
Si verification notes: road_graph.rs has NO hub concept and NO edge-split machinery today (T-1038 treats every placement as a uniform MST node); RoadGraph::high_connectivity_junctions() exists (JUNCTION_DEGREE=3); FoundingOrientation consumers are founding_orientation() and coastal_edge() (if-let, safe); skeleton_gen has zero rail-facing rotation logic (new work, scoped); PR #177 data preconditions verified (population>0 + settlement_class on all rows; standalone HQs present as ordinary rows).', 'done', 'medium', 'dudley', 'server', 'D-242', '2026-06-14 15:29:50', '2026-07-16 15:15:16.460', NULL, 'fa5cf116e084387017b5d6a0d280c768', 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 ('06FNTXF0JB8E6PDQRASQ18E9ER', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire region climate stack into the cascade (CascadeLayer::Region + proxy exposure)', 'Si audit of T-960 (2026-07-13): region_profile.rs (T-1078 — RegionProfile, derive_regions_for_body, region_baseline_at_district) is implemented and unit-tested but never called from cascade.rs: no CascadeLayer::Region variant, nothing on BodyWorldState, nothing in the layer proxy. Prerequisite for any climate Atlas overlay (explicitly out of T-960 scope per 2026-07-13 ruling): add the cascade layer, populate BodyWorldState, expose via AtlasLayerResponse following the T-1046 district-grid precedent. The climate OVERLAY itself is a small follow-up once this serves data.
---
Refinement (2026-07-16, /whats-next Si review): READY, no gaps.
Key implementation facts (Si-verified against post-PR#178 main):
- region_profile.rs (T-1078) exposes exactly what''s needed: RegionProfile{pos, clock: RegionClock{season, weather, mean_temp_c}, latitude_deg, moisture_q}, derive_regions_for_body(...) -> BTreeMap<RegionPos, RegionProfile>, region_baseline_at_district(...). Deterministic, unit-tested.
- CRITICAL: district_profile.rs::derive_all_districts (~1434-1509) ALREADY builds the region cache internally (incl. neighbours for edge-fuzz) and discards it — this ticket is retention, not new derivation. Chosen shape: option (a) — independent CascadeLayer::Region step in run_cascade_from_heightmap (one-layer-one-concern, mirrors existing layers; accept the cheap double-derive).
- CascadeLayer Ord today: Heightmap < Topography < Settlement < DistrictProfile < RoadGraph. Region slots as its own step.
- BodyWorldState: add pub regions: BTreeMap<RegionPos, RegionProfile> per the districts precedent (same determinism rationale, same empty-until-run convention). Footprint trivial (~195×98 max).
- layer_proxy.rs doc comment (76-79) already earmarks T-1113 as one of the two final layer fields. Mirror build_district_grid: RegionGridLayer + build_region_grid(state) -> Option<...>, called from handle_atlas_request cache-hit branch. Encoding: dense row-major grid per the T-1046 precedent (implementer''s low-stakes call on exact fields/quantization).
- Client wire-decode IS in scope (one line): protocol.gd::atlas_response_from_raw raw.get("region_grid") passthrough, matching district_grid. The VISUAL overlay (atlas_viewer/marker_overlay/legend) is explicitly OUT of scope — small follow-up per D-226 amendment (a) incremental-overlay pattern.
- Edge-fuzz (D-243 §4) is consumer-side sampling behavior already implemented in region_baseline_at_district — not this ticket''s concern.
- Q-105 resolved into D-243; RegionClock tick-phase callbacks stay deferred — only the mean-state fields are served.', 'backlog', 'medium', NULL, 'server', 'D-243', '2026-07-13 21:50:34.130', '2026-07-16 16:28:52.947', NULL, 'b1ac72a5909879e07725f150340fb5cf', 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 ('06FNTXEM9NM5M5SX9WQAAA40SC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Design: coarse quarter-footprint Atlas layer (L4 skeleton on the planetary map)', 'Si audit of T-960 (2026-07-13): L4 quarter/block skeletons (T-957/T-976) auto-generate server-side into BodyWorldState.quarters, but carry full per-block zoning/street/tag detail — too fine for a planetary map, and unlike the district grid there is no existing coarse shape to reuse. DESIGN ticket (Araminta encoding + Tyre data shape) before any wiring: define a QuarterFootprintLayer (city outline + aggregate stats?) for AtlasLayerResponse and its overlay encoding per the D-226 amended (2026-07-13) overlay pattern. Wiring is a follow-up once the design is recorded.
---
Refinement (2026-07-16, /whats-next Si review): READY, one scoping note made explicit.
- DELIVERABLE FORMAT (was unpinned): record the design as a **D-226 amendment** — the established mechanism (the 2026-07-13 overlay pattern itself was recorded that way); no docs/design Atlas-overlay doc precedent exists (T-1046 shipped design-in-ticket+code-comments). The design pass ends at the recorded amendment; the wiring follow-up ticket is filed by the lead afterwards, not by the design pass.
- BINDING CONSTRAINT: D-226 amendment (d) is a hard ceiling — planetary Atlas tops out at settlement/quarter-skeleton granularity with AT MOST aggregate stats. No per-block zoning/street/tag detail at planetary scale. City outline + aggregate stats only.
- DATA SOURCE: BodyWorldState.quarters stores QuarterWorldState{skeleton: QuarterSkeleton, block_tags: BTreeMap<(u8,u8), Vec<BuildingPropertyTag>>}; QuarterSkeleton carries [[BlockSkeleton;4];4] with zoning/district_type/density_pct/landmark + reservations/corridors. All aggregatable (density avg, dominant zoning/district-type mix, landmark count, corridor count) without inventing new simulation truth — same derivation class as build_district_grid/build_settlement_layer.
- PRECEDENT TO IMITATE (five touch points, each with a working example): server AtlasLayerResponse field + build_quarter_footprint_layer() mirroring build_district_grid (keyed off non-empty state.quarters); client OVERLAY_DEFS gen_* id (atlas_viewer.gd), _draw_gen_* fn (atlas_marker_overlay.gd, imitate _draw_gen_district ~185-188), GENERATION_LEGEND entry (atlas_legend_panel.gd). These are named in the amendment for the wiring follow-up — not implemented in this ticket.
- PARTICIPANT SPLIT: Tyre owns the aggregate-stats data shape (fields, structure, D-010 integer discipline); Araminta owns the overlay encoding (outline vs filled block, color axis per the shape-vs-color convention in atlas_legend_panel.gd) — the same division D-226 originally used. Two coordinated agents, not a workshop round.
- pql metadata quirk noted: the ticket''s decision_ref D-226 resolves to a wrong title in show-summary; `pql decisions read D-226` returns the correct record.', 'backlog', 'medium', NULL, 'client', 'D-226', '2026-07-13 21:50:30.989', '2026-07-16 16:29:00.676', NULL, 'e7e2c4c9c632f28a8dea8230d56d8e9e', 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 ('06FNTXEM9NM5M5SX9WQAAA40SC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Design: coarse quarter-footprint Atlas layer (L4 skeleton on the planetary map)', 'Si audit of T-960 (2026-07-13): L4 quarter/block skeletons (T-957/T-976) auto-generate server-side into BodyWorldState.quarters, but carry full per-block zoning/street/tag detail — too fine for a planetary map, and unlike the district grid there is no existing coarse shape to reuse. DESIGN ticket (Araminta encoding + Tyre data shape) before any wiring: define a QuarterFootprintLayer (city outline + aggregate stats?) for AtlasLayerResponse and its overlay encoding per the D-226 amended (2026-07-13) overlay pattern. Wiring is a follow-up once the design is recorded.
---
Refinement (2026-07-16, /whats-next Si review): READY, one scoping note made explicit.
- DELIVERABLE FORMAT (was unpinned): record the design as a **D-226 amendment** — the established mechanism (the 2026-07-13 overlay pattern itself was recorded that way); no docs/design Atlas-overlay doc precedent exists (T-1046 shipped design-in-ticket+code-comments). The design pass ends at the recorded amendment; the wiring follow-up ticket is filed by the lead afterwards, not by the design pass.
- BINDING CONSTRAINT: D-226 amendment (d) is a hard ceiling — planetary Atlas tops out at settlement/quarter-skeleton granularity with AT MOST aggregate stats. No per-block zoning/street/tag detail at planetary scale. City outline + aggregate stats only.
- DATA SOURCE: BodyWorldState.quarters stores QuarterWorldState{skeleton: QuarterSkeleton, block_tags: BTreeMap<(u8,u8), Vec<BuildingPropertyTag>>}; QuarterSkeleton carries [[BlockSkeleton;4];4] with zoning/district_type/density_pct/landmark + reservations/corridors. All aggregatable (density avg, dominant zoning/district-type mix, landmark count, corridor count) without inventing new simulation truth — same derivation class as build_district_grid/build_settlement_layer.
- PRECEDENT TO IMITATE (five touch points, each with a working example): server AtlasLayerResponse field + build_quarter_footprint_layer() mirroring build_district_grid (keyed off non-empty state.quarters); client OVERLAY_DEFS gen_* id (atlas_viewer.gd), _draw_gen_* fn (atlas_marker_overlay.gd, imitate _draw_gen_district ~185-188), GENERATION_LEGEND entry (atlas_legend_panel.gd). These are named in the amendment for the wiring follow-up — not implemented in this ticket.
- PARTICIPANT SPLIT: Tyre owns the aggregate-stats data shape (fields, structure, D-010 integer discipline); Araminta owns the overlay encoding (outline vs filled block, color axis per the shape-vs-color convention in atlas_legend_panel.gd) — the same division D-226 originally used. Two coordinated agents, not a workshop round.
- pql metadata quirk noted: the ticket''s decision_ref D-226 resolves to a wrong title in show-summary; `pql decisions read D-226` returns the correct record.', 'in_progress', 'medium', NULL, 'client', 'D-226', '2026-07-13 21:50:30.989', '2026-07-16 16:29:07.852', NULL, '80c50822d552dfbf0c8452a9e5e2df99', 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 ('06FNTXF0JB8E6PDQRASQ18E9ER', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire region climate stack into the cascade (CascadeLayer::Region + proxy exposure)', 'Si audit of T-960 (2026-07-13): region_profile.rs (T-1078 — RegionProfile, derive_regions_for_body, region_baseline_at_district) is implemented and unit-tested but never called from cascade.rs: no CascadeLayer::Region variant, nothing on BodyWorldState, nothing in the layer proxy. Prerequisite for any climate Atlas overlay (explicitly out of T-960 scope per 2026-07-13 ruling): add the cascade layer, populate BodyWorldState, expose via AtlasLayerResponse following the T-1046 district-grid precedent. The climate OVERLAY itself is a small follow-up once this serves data.
---
Refinement (2026-07-16, /whats-next Si review): READY, no gaps.
Key implementation facts (Si-verified against post-PR#178 main):
- region_profile.rs (T-1078) exposes exactly what''s needed: RegionProfile{pos, clock: RegionClock{season, weather, mean_temp_c}, latitude_deg, moisture_q}, derive_regions_for_body(...) -> BTreeMap<RegionPos, RegionProfile>, region_baseline_at_district(...). Deterministic, unit-tested.
- CRITICAL: district_profile.rs::derive_all_districts (~1434-1509) ALREADY builds the region cache internally (incl. neighbours for edge-fuzz) and discards it — this ticket is retention, not new derivation. Chosen shape: option (a) — independent CascadeLayer::Region step in run_cascade_from_heightmap (one-layer-one-concern, mirrors existing layers; accept the cheap double-derive).
- CascadeLayer Ord today: Heightmap < Topography < Settlement < DistrictProfile < RoadGraph. Region slots as its own step.
- BodyWorldState: add pub regions: BTreeMap<RegionPos, RegionProfile> per the districts precedent (same determinism rationale, same empty-until-run convention). Footprint trivial (~195×98 max).
- layer_proxy.rs doc comment (76-79) already earmarks T-1113 as one of the two final layer fields. Mirror build_district_grid: RegionGridLayer + build_region_grid(state) -> Option<...>, called from handle_atlas_request cache-hit branch. Encoding: dense row-major grid per the T-1046 precedent (implementer''s low-stakes call on exact fields/quantization).
- Client wire-decode IS in scope (one line): protocol.gd::atlas_response_from_raw raw.get("region_grid") passthrough, matching district_grid. The VISUAL overlay (atlas_viewer/marker_overlay/legend) is explicitly OUT of scope — small follow-up per D-226 amendment (a) incremental-overlay pattern.
- Edge-fuzz (D-243 §4) is consumer-side sampling behavior already implemented in region_baseline_at_district — not this ticket''s concern.
- Q-105 resolved into D-243; RegionClock tick-phase callbacks stay deferred — only the mean-state fields are served.', 'in_progress', 'medium', NULL, 'server', 'D-243', '2026-07-13 21:50:34.130', '2026-07-16 16:29:07.859', NULL, '6051dd64719856227b74735692044209', 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 ('06FNTXF0JB8E6PDQRASQ18E9ER', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire region climate stack into the cascade (CascadeLayer::Region + proxy exposure)', 'Si audit of T-960 (2026-07-13): region_profile.rs (T-1078 — RegionProfile, derive_regions_for_body, region_baseline_at_district) is implemented and unit-tested but never called from cascade.rs: no CascadeLayer::Region variant, nothing on BodyWorldState, nothing in the layer proxy. Prerequisite for any climate Atlas overlay (explicitly out of T-960 scope per 2026-07-13 ruling): add the cascade layer, populate BodyWorldState, expose via AtlasLayerResponse following the T-1046 district-grid precedent. The climate OVERLAY itself is a small follow-up once this serves data.
---
Refinement (2026-07-16, /whats-next Si review): READY, no gaps.
Key implementation facts (Si-verified against post-PR#178 main):
- region_profile.rs (T-1078) exposes exactly what''s needed: RegionProfile{pos, clock: RegionClock{season, weather, mean_temp_c}, latitude_deg, moisture_q}, derive_regions_for_body(...) -> BTreeMap<RegionPos, RegionProfile>, region_baseline_at_district(...). Deterministic, unit-tested.
- CRITICAL: district_profile.rs::derive_all_districts (~1434-1509) ALREADY builds the region cache internally (incl. neighbours for edge-fuzz) and discards it — this ticket is retention, not new derivation. Chosen shape: option (a) — independent CascadeLayer::Region step in run_cascade_from_heightmap (one-layer-one-concern, mirrors existing layers; accept the cheap double-derive).
- CascadeLayer Ord today: Heightmap < Topography < Settlement < DistrictProfile < RoadGraph. Region slots as its own step.
- BodyWorldState: add pub regions: BTreeMap<RegionPos, RegionProfile> per the districts precedent (same determinism rationale, same empty-until-run convention). Footprint trivial (~195×98 max).
- layer_proxy.rs doc comment (76-79) already earmarks T-1113 as one of the two final layer fields. Mirror build_district_grid: RegionGridLayer + build_region_grid(state) -> Option<...>, called from handle_atlas_request cache-hit branch. Encoding: dense row-major grid per the T-1046 precedent (implementer''s low-stakes call on exact fields/quantization).
- Client wire-decode IS in scope (one line): protocol.gd::atlas_response_from_raw raw.get("region_grid") passthrough, matching district_grid. The VISUAL overlay (atlas_viewer/marker_overlay/legend) is explicitly OUT of scope — small follow-up per D-226 amendment (a) incremental-overlay pattern.
- Edge-fuzz (D-243 §4) is consumer-side sampling behavior already implemented in region_baseline_at_district — not this ticket''s concern.
- Q-105 resolved into D-243; RegionClock tick-phase callbacks stay deferred — only the mean-state fields are served.', 'in_progress', 'medium', 'dudley', 'server', 'D-243', '2026-07-13 21:50:34.130', '2026-07-16 16:29:14.520', NULL, 'ca5d25a0104dea15c652446bc7b2e59d', 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 ('06FNTXEM9NM5M5SX9WQAAA40SC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Design: coarse quarter-footprint Atlas layer (L4 skeleton on the planetary map)', 'Si audit of T-960 (2026-07-13): L4 quarter/block skeletons (T-957/T-976) auto-generate server-side into BodyWorldState.quarters, but carry full per-block zoning/street/tag detail — too fine for a planetary map, and unlike the district grid there is no existing coarse shape to reuse. DESIGN ticket (Araminta encoding + Tyre data shape) before any wiring: define a QuarterFootprintLayer (city outline + aggregate stats?) for AtlasLayerResponse and its overlay encoding per the D-226 amended (2026-07-13) overlay pattern. Wiring is a follow-up once the design is recorded.
---
Refinement (2026-07-16, /whats-next Si review): READY, one scoping note made explicit.
- DELIVERABLE FORMAT (was unpinned): record the design as a **D-226 amendment** — the established mechanism (the 2026-07-13 overlay pattern itself was recorded that way); no docs/design Atlas-overlay doc precedent exists (T-1046 shipped design-in-ticket+code-comments). The design pass ends at the recorded amendment; the wiring follow-up ticket is filed by the lead afterwards, not by the design pass.
- BINDING CONSTRAINT: D-226 amendment (d) is a hard ceiling — planetary Atlas tops out at settlement/quarter-skeleton granularity with AT MOST aggregate stats. No per-block zoning/street/tag detail at planetary scale. City outline + aggregate stats only.
- DATA SOURCE: BodyWorldState.quarters stores QuarterWorldState{skeleton: QuarterSkeleton, block_tags: BTreeMap<(u8,u8), Vec<BuildingPropertyTag>>}; QuarterSkeleton carries [[BlockSkeleton;4];4] with zoning/district_type/density_pct/landmark + reservations/corridors. All aggregatable (density avg, dominant zoning/district-type mix, landmark count, corridor count) without inventing new simulation truth — same derivation class as build_district_grid/build_settlement_layer.
- PRECEDENT TO IMITATE (five touch points, each with a working example): server AtlasLayerResponse field + build_quarter_footprint_layer() mirroring build_district_grid (keyed off non-empty state.quarters); client OVERLAY_DEFS gen_* id (atlas_viewer.gd), _draw_gen_* fn (atlas_marker_overlay.gd, imitate _draw_gen_district ~185-188), GENERATION_LEGEND entry (atlas_legend_panel.gd). These are named in the amendment for the wiring follow-up — not implemented in this ticket.
- PARTICIPANT SPLIT: Tyre owns the aggregate-stats data shape (fields, structure, D-010 integer discipline); Araminta owns the overlay encoding (outline vs filled block, color axis per the shape-vs-color convention in atlas_legend_panel.gd) — the same division D-226 originally used. Two coordinated agents, not a workshop round.
- pql metadata quirk noted: the ticket''s decision_ref D-226 resolves to a wrong title in show-summary; `pql decisions read D-226` returns the correct record.', 'in_progress', 'medium', 'tyre', 'client', 'D-226', '2026-07-13 21:50:30.989', '2026-07-16 16:29:22.162', NULL, 'c875a58d0f49225f768dd2e7efbee3e8', 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 ('06FNTXEM9NM5M5SX9WQAAA40SC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Design: coarse quarter-footprint Atlas layer (L4 skeleton on the planetary map)', 'Si audit of T-960 (2026-07-13): L4 quarter/block skeletons (T-957/T-976) auto-generate server-side into BodyWorldState.quarters, but carry full per-block zoning/street/tag detail — too fine for a planetary map, and unlike the district grid there is no existing coarse shape to reuse. DESIGN ticket (Araminta encoding + Tyre data shape) before any wiring: define a QuarterFootprintLayer (city outline + aggregate stats?) for AtlasLayerResponse and its overlay encoding per the D-226 amended (2026-07-13) overlay pattern. Wiring is a follow-up once the design is recorded.
---
Refinement (2026-07-16, /whats-next Si review): READY, one scoping note made explicit.
- DELIVERABLE FORMAT (was unpinned): record the design as a **D-226 amendment** — the established mechanism (the 2026-07-13 overlay pattern itself was recorded that way); no docs/design Atlas-overlay doc precedent exists (T-1046 shipped design-in-ticket+code-comments). The design pass ends at the recorded amendment; the wiring follow-up ticket is filed by the lead afterwards, not by the design pass.
- BINDING CONSTRAINT: D-226 amendment (d) is a hard ceiling — planetary Atlas tops out at settlement/quarter-skeleton granularity with AT MOST aggregate stats. No per-block zoning/street/tag detail at planetary scale. City outline + aggregate stats only.
- DATA SOURCE: BodyWorldState.quarters stores QuarterWorldState{skeleton: QuarterSkeleton, block_tags: BTreeMap<(u8,u8), Vec<BuildingPropertyTag>>}; QuarterSkeleton carries [[BlockSkeleton;4];4] with zoning/district_type/density_pct/landmark + reservations/corridors. All aggregatable (density avg, dominant zoning/district-type mix, landmark count, corridor count) without inventing new simulation truth — same derivation class as build_district_grid/build_settlement_layer.
- PRECEDENT TO IMITATE (five touch points, each with a working example): server AtlasLayerResponse field + build_quarter_footprint_layer() mirroring build_district_grid (keyed off non-empty state.quarters); client OVERLAY_DEFS gen_* id (atlas_viewer.gd), _draw_gen_* fn (atlas_marker_overlay.gd, imitate _draw_gen_district ~185-188), GENERATION_LEGEND entry (atlas_legend_panel.gd). These are named in the amendment for the wiring follow-up — not implemented in this ticket.
- PARTICIPANT SPLIT: Tyre owns the aggregate-stats data shape (fields, structure, D-010 integer discipline); Araminta owns the overlay encoding (outline vs filled block, color axis per the shape-vs-color convention in atlas_legend_panel.gd) — the same division D-226 originally used. Two coordinated agents, not a workshop round.
- pql metadata quirk noted: the ticket''s decision_ref D-226 resolves to a wrong title in show-summary; `pql decisions read D-226` returns the correct record.', 'review', 'medium', 'tyre', 'client', 'D-226', '2026-07-13 21:50:30.989', '2026-07-16 17:08:34.002', NULL, '7090cccaa4a8519967f005e59e106c30', 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 ('06FNTXF0JB8E6PDQRASQ18E9ER', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire region climate stack into the cascade (CascadeLayer::Region + proxy exposure)', 'Si audit of T-960 (2026-07-13): region_profile.rs (T-1078 — RegionProfile, derive_regions_for_body, region_baseline_at_district) is implemented and unit-tested but never called from cascade.rs: no CascadeLayer::Region variant, nothing on BodyWorldState, nothing in the layer proxy. Prerequisite for any climate Atlas overlay (explicitly out of T-960 scope per 2026-07-13 ruling): add the cascade layer, populate BodyWorldState, expose via AtlasLayerResponse following the T-1046 district-grid precedent. The climate OVERLAY itself is a small follow-up once this serves data.
---
Refinement (2026-07-16, /whats-next Si review): READY, no gaps.
Key implementation facts (Si-verified against post-PR#178 main):
- region_profile.rs (T-1078) exposes exactly what''s needed: RegionProfile{pos, clock: RegionClock{season, weather, mean_temp_c}, latitude_deg, moisture_q}, derive_regions_for_body(...) -> BTreeMap<RegionPos, RegionProfile>, region_baseline_at_district(...). Deterministic, unit-tested.
- CRITICAL: district_profile.rs::derive_all_districts (~1434-1509) ALREADY builds the region cache internally (incl. neighbours for edge-fuzz) and discards it — this ticket is retention, not new derivation. Chosen shape: option (a) — independent CascadeLayer::Region step in run_cascade_from_heightmap (one-layer-one-concern, mirrors existing layers; accept the cheap double-derive).
- CascadeLayer Ord today: Heightmap < Topography < Settlement < DistrictProfile < RoadGraph. Region slots as its own step.
- BodyWorldState: add pub regions: BTreeMap<RegionPos, RegionProfile> per the districts precedent (same determinism rationale, same empty-until-run convention). Footprint trivial (~195×98 max).
- layer_proxy.rs doc comment (76-79) already earmarks T-1113 as one of the two final layer fields. Mirror build_district_grid: RegionGridLayer + build_region_grid(state) -> Option<...>, called from handle_atlas_request cache-hit branch. Encoding: dense row-major grid per the T-1046 precedent (implementer''s low-stakes call on exact fields/quantization).
- Client wire-decode IS in scope (one line): protocol.gd::atlas_response_from_raw raw.get("region_grid") passthrough, matching district_grid. The VISUAL overlay (atlas_viewer/marker_overlay/legend) is explicitly OUT of scope — small follow-up per D-226 amendment (a) incremental-overlay pattern.
- Edge-fuzz (D-243 §4) is consumer-side sampling behavior already implemented in region_baseline_at_district — not this ticket''s concern.
- Q-105 resolved into D-243; RegionClock tick-phase callbacks stay deferred — only the mean-state fields are served.', 'review', 'medium', 'dudley', 'server', 'D-243', '2026-07-13 21:50:34.130', '2026-07-16 17:08:34.005', NULL, '3e46d1a1deedd537dea1d2d214b26c37', 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 ('06FNTXEM9NM5M5SX9WQAAA40SC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Design: coarse quarter-footprint Atlas layer (L4 skeleton on the planetary map)', 'Si audit of T-960 (2026-07-13): L4 quarter/block skeletons (T-957/T-976) auto-generate server-side into BodyWorldState.quarters, but carry full per-block zoning/street/tag detail — too fine for a planetary map, and unlike the district grid there is no existing coarse shape to reuse. DESIGN ticket (Araminta encoding + Tyre data shape) before any wiring: define a QuarterFootprintLayer (city outline + aggregate stats?) for AtlasLayerResponse and its overlay encoding per the D-226 amended (2026-07-13) overlay pattern. Wiring is a follow-up once the design is recorded.
---
Refinement (2026-07-16, /whats-next Si review): READY, one scoping note made explicit.
- DELIVERABLE FORMAT (was unpinned): record the design as a **D-226 amendment** — the established mechanism (the 2026-07-13 overlay pattern itself was recorded that way); no docs/design Atlas-overlay doc precedent exists (T-1046 shipped design-in-ticket+code-comments). The design pass ends at the recorded amendment; the wiring follow-up ticket is filed by the lead afterwards, not by the design pass.
- BINDING CONSTRAINT: D-226 amendment (d) is a hard ceiling — planetary Atlas tops out at settlement/quarter-skeleton granularity with AT MOST aggregate stats. No per-block zoning/street/tag detail at planetary scale. City outline + aggregate stats only.
- DATA SOURCE: BodyWorldState.quarters stores QuarterWorldState{skeleton: QuarterSkeleton, block_tags: BTreeMap<(u8,u8), Vec<BuildingPropertyTag>>}; QuarterSkeleton carries [[BlockSkeleton;4];4] with zoning/district_type/density_pct/landmark + reservations/corridors. All aggregatable (density avg, dominant zoning/district-type mix, landmark count, corridor count) without inventing new simulation truth — same derivation class as build_district_grid/build_settlement_layer.
- PRECEDENT TO IMITATE (five touch points, each with a working example): server AtlasLayerResponse field + build_quarter_footprint_layer() mirroring build_district_grid (keyed off non-empty state.quarters); client OVERLAY_DEFS gen_* id (atlas_viewer.gd), _draw_gen_* fn (atlas_marker_overlay.gd, imitate _draw_gen_district ~185-188), GENERATION_LEGEND entry (atlas_legend_panel.gd). These are named in the amendment for the wiring follow-up — not implemented in this ticket.
- PARTICIPANT SPLIT: Tyre owns the aggregate-stats data shape (fields, structure, D-010 integer discipline); Araminta owns the overlay encoding (outline vs filled block, color axis per the shape-vs-color convention in atlas_legend_panel.gd) — the same division D-226 originally used. Two coordinated agents, not a workshop round.
- pql metadata quirk noted: the ticket''s decision_ref D-226 resolves to a wrong title in show-summary; `pql decisions read D-226` returns the correct record.', 'done', 'medium', 'tyre', 'client', 'D-226', '2026-07-13 21:50:30.989', '2026-07-16 17:24:54.843', NULL, 'fe5dfefb93177b0fc3f85e78a69490aa', 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 ('06FNTXF0JB8E6PDQRASQ18E9ER', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire region climate stack into the cascade (CascadeLayer::Region + proxy exposure)', 'Si audit of T-960 (2026-07-13): region_profile.rs (T-1078 — RegionProfile, derive_regions_for_body, region_baseline_at_district) is implemented and unit-tested but never called from cascade.rs: no CascadeLayer::Region variant, nothing on BodyWorldState, nothing in the layer proxy. Prerequisite for any climate Atlas overlay (explicitly out of T-960 scope per 2026-07-13 ruling): add the cascade layer, populate BodyWorldState, expose via AtlasLayerResponse following the T-1046 district-grid precedent. The climate OVERLAY itself is a small follow-up once this serves data.
---
Refinement (2026-07-16, /whats-next Si review): READY, no gaps.
Key implementation facts (Si-verified against post-PR#178 main):
- region_profile.rs (T-1078) exposes exactly what''s needed: RegionProfile{pos, clock: RegionClock{season, weather, mean_temp_c}, latitude_deg, moisture_q}, derive_regions_for_body(...) -> BTreeMap<RegionPos, RegionProfile>, region_baseline_at_district(...). Deterministic, unit-tested.
- CRITICAL: district_profile.rs::derive_all_districts (~1434-1509) ALREADY builds the region cache internally (incl. neighbours for edge-fuzz) and discards it — this ticket is retention, not new derivation. Chosen shape: option (a) — independent CascadeLayer::Region step in run_cascade_from_heightmap (one-layer-one-concern, mirrors existing layers; accept the cheap double-derive).
- CascadeLayer Ord today: Heightmap < Topography < Settlement < DistrictProfile < RoadGraph. Region slots as its own step.
- BodyWorldState: add pub regions: BTreeMap<RegionPos, RegionProfile> per the districts precedent (same determinism rationale, same empty-until-run convention). Footprint trivial (~195×98 max).
- layer_proxy.rs doc comment (76-79) already earmarks T-1113 as one of the two final layer fields. Mirror build_district_grid: RegionGridLayer + build_region_grid(state) -> Option<...>, called from handle_atlas_request cache-hit branch. Encoding: dense row-major grid per the T-1046 precedent (implementer''s low-stakes call on exact fields/quantization).
- Client wire-decode IS in scope (one line): protocol.gd::atlas_response_from_raw raw.get("region_grid") passthrough, matching district_grid. The VISUAL overlay (atlas_viewer/marker_overlay/legend) is explicitly OUT of scope — small follow-up per D-226 amendment (a) incremental-overlay pattern.
- Edge-fuzz (D-243 §4) is consumer-side sampling behavior already implemented in region_baseline_at_district — not this ticket''s concern.
- Q-105 resolved into D-243; RegionClock tick-phase callbacks stay deferred — only the mean-state fields are served.', 'done', 'medium', 'dudley', 'server', 'D-243', '2026-07-13 21:50:34.130', '2026-07-16 17:24:54.846', NULL, '695184b0c9a6834a67955695fc129658', 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 ('06FPQXFFHXEEDQEM7PGXAH3534', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas climate overlay — render region_grid (temp/season/weather/moisture) in the viewer', 'Follow-up to T-1113 (merged PR #179): the server now serves RegionGridLayer (dense row-major; season/weather repr(u8) discriminants, mean_temp deci-degC i16 with i16::MIN airless sentinel, moisture_q u8) through AtlasLayerResponse.region_grid, and protocol.gd passes it through. Build the client overlay per the D-226 incremental-overlay pattern: OVERLAY_DEFS gen_* id (atlas_viewer.gd), _draw_gen_* renderer (atlas_marker_overlay.gd — a region-grid fill analogous to _draw_gen_district but at region granularity), GENERATION_LEGEND entry (atlas_legend_panel.gd). Pick ONE visual channel (mean temp ramp is the obvious first; season/weather as tooltip or variant overlays). Also add a populated region_grid wire fixture (all three committed fixtures carry nil — noted in PR #179).', 'backlog', 'medium', NULL, 'client', 'D-226', '2026-07-16 17:25:07.599', '2026-07-16 17:25:07.599', NULL, 'ade35c5892731c5c656f4a09c7d625ac', 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 ('06FPQXHDSMSVD3VADV38NG9STW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire QuarterFootprintLayer per the D-226 T-1112 amendment (six touch points)', 'Implements the design recorded in the D-226 amendment (2026-07-16, T-1112, merged PR #179). Six touch points, each named in the amendment SS4: (1) ZoningType gains PartialOrd/Ord derives mirroring DistrictType''s T-994 precedent (enables the dominant_zoning declaration-order tie-break); (2) AtlasLayerResponse.quarter_footprints field + build_quarter_footprint_layer() mirroring build_district_grid, keyed off non-empty state.quarters, aggregates keyed by city_id (join via the same deterministic QuarterId derivation the dispatch uses); (3) protocol.gd passthrough; (4) OVERLAY_DEFS gen_* id (atlas_viewer.gd); (5) _draw_gen_* scaled-glyph renderer anchored on the L3 settlement dot — shape=dominant district type, color=density ramp on the settlement-gold family, zoom-gated at SETTLEMENT_LABEL_MIN_ZOOM (atlas_marker_overlay.gd); (6) GENERATION_LEGEND entry (atlas_legend_panel.gd). Five aggregate fields: density_avg_pct u8, dominant_district_type, dominant_zoning, landmark_count u8, corridor_count u8; landmark/corridor tooltip-only per the D-226(d) ceiling.', 'backlog', 'medium', NULL, 'server', 'D-226', '2026-07-16 17:25:23.533', '2026-07-16 17:25:23.533', NULL, 'b6b8cd20b3fe7d1229c917674d46b0cc', 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 ('06FPQXK8D6YVWVC2TB2ZR25K6G', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Atlas screenshot capture matrix + golden smoke (visual_capture extension)', 'Jeroen''s ask (2026-07-16): in-game Atlas screenshots at several zoom levels across a varied body sample, treated as goldens for smoke tests. Recon (this session) mapped the path — reuse, don''t rebuild: extend client/tests/visual_capture.gd with an atlas-matrix mode (one Godot boot looping body x zoom x overlay-set), scenarios modeled on visual_scenarios.gd atlas_gen_open (currently GJ71c-hardcoded — parameterize); server lifecycle per tests/run-visual (--test-mode --port 0, parse LISTENING, SR_LIVE=1 — Atlas data is 100% systems.db regardless of world); waits on SimBridge.atlas_layers_received/city_names_received (status Ready) not fixed delays; capture via the existing _capture() (frame_post_draw -> save_png); xvfb-run + --rendering-driver opengl3, NEVER --headless (blank PNGs under dummy driver). Promote a curated subset into client/tests/golden/visual/ + tests/visual.json (existing tooling/visual-diff tolerance machinery, make visual-update). Known warts: no public zoom/pan setter on AtlasViewer (reach into _view_zoom/_view_offset + _apply_transform per existing convention, or add a small set_view() API); Sol uses the legacy markers path (exclude); async races need per-shot wait/timeout. Body sample suggestion: GJ380c, GJ144e, GJ338Bd, GJ820Bc, GJ251c (honest T-1116 water/no-roads exhibit), + a frozen body and a moon.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-16 17:25:38.537', '2026-07-16 17:25:38.537', NULL, '1cfe732a65ed0be3d7f4724c2ec2389c', 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 ('06FPQXK8D6YVWVC2TB2ZR25K6G', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Atlas screenshot capture matrix + golden smoke (visual_capture extension)', 'Jeroen''s ask (2026-07-16): in-game Atlas screenshots at several zoom levels across a varied body sample, treated as goldens for smoke tests. Recon (this session) mapped the path — reuse, don''t rebuild: extend client/tests/visual_capture.gd with an atlas-matrix mode (one Godot boot looping body x zoom x overlay-set), scenarios modeled on visual_scenarios.gd atlas_gen_open (currently GJ71c-hardcoded — parameterize); server lifecycle per tests/run-visual (--test-mode --port 0, parse LISTENING, SR_LIVE=1 — Atlas data is 100% systems.db regardless of world); waits on SimBridge.atlas_layers_received/city_names_received (status Ready) not fixed delays; capture via the existing _capture() (frame_post_draw -> save_png); xvfb-run + --rendering-driver opengl3, NEVER --headless (blank PNGs under dummy driver). Promote a curated subset into client/tests/golden/visual/ + tests/visual.json (existing tooling/visual-diff tolerance machinery, make visual-update). Known warts: no public zoom/pan setter on AtlasViewer (reach into _view_zoom/_view_offset + _apply_transform per existing convention, or add a small set_view() API); Sol uses the legacy markers path (exclude); async races need per-shot wait/timeout. Body sample suggestion: GJ380c, GJ144e, GJ338Bd, GJ820Bc, GJ251c (honest T-1116 water/no-roads exhibit), + a frozen body and a moon.', 'in_progress', 'medium', NULL, 'client', NULL, '2026-07-16 17:25:38.537', '2026-07-16 17:30:59.850', NULL, '928251ffd5e5a560ae63e5dc0eb0f14c', 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 ('06FPQXK8D6YVWVC2TB2ZR25K6G', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Atlas screenshot capture matrix + golden smoke (visual_capture extension)', 'Jeroen''s ask (2026-07-16): in-game Atlas screenshots at several zoom levels across a varied body sample, treated as goldens for smoke tests. Recon (this session) mapped the path — reuse, don''t rebuild: extend client/tests/visual_capture.gd with an atlas-matrix mode (one Godot boot looping body x zoom x overlay-set), scenarios modeled on visual_scenarios.gd atlas_gen_open (currently GJ71c-hardcoded — parameterize); server lifecycle per tests/run-visual (--test-mode --port 0, parse LISTENING, SR_LIVE=1 — Atlas data is 100% systems.db regardless of world); waits on SimBridge.atlas_layers_received/city_names_received (status Ready) not fixed delays; capture via the existing _capture() (frame_post_draw -> save_png); xvfb-run + --rendering-driver opengl3, NEVER --headless (blank PNGs under dummy driver). Promote a curated subset into client/tests/golden/visual/ + tests/visual.json (existing tooling/visual-diff tolerance machinery, make visual-update). Known warts: no public zoom/pan setter on AtlasViewer (reach into _view_zoom/_view_offset + _apply_transform per existing convention, or add a small set_view() API); Sol uses the legacy markers path (exclude); async races need per-shot wait/timeout. Body sample suggestion: GJ380c, GJ144e, GJ338Bd, GJ820Bc, GJ251c (honest T-1116 water/no-roads exhibit), + a frozen body and a moon.', 'in_progress', 'medium', 'stig', 'client', NULL, '2026-07-16 17:25:38.537', '2026-07-16 17:31:07.362', NULL, '49c742f3db16af2adc379a3ff145acdc', 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 ('06FPRCCX8DGJJSMQZNM25VDJQ4', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Visual golden baseline drift — all 19 pre-existing goldens fail 12-14% on this box (font/AA/GPU)', 'Found during T-1120 (2026-07-16): a full tests/run-visual pass shows every one of the 19 pre-existing golden scenarios failing at 12-14% pixel diff (fog_debug 44.7%) on this box — whole-region mismatches (vision cone, HUD corners, text) consistent with font/AA/GPU-driver rendering drift between the machine that produced the committed goldens and this Mesa/AMD stack, NOT a real regression. Also: fog_live_hub stalls for minutes in the visible-window (no xvfb) fallback. The new T-1120 atlas goldens were captured AND verified on this box (12/12 exact match) so they are self-consistent here. Investigation: cross-machine golden portability — options include per-machine baselines, higher tolerance + masked regions, font/AA pinning, xvfb install for consistent rasterization, or declaring one canonical capture box. Decide + record; regenerate the 19 goldens on the canonical box once decided.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-16 18:30:18.692', '2026-07-16 18:30:18.692', NULL, '1453787231b0fe267b765fd4f55e6db2', 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 ('06FPQXK8D6YVWVC2TB2ZR25K6G', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Atlas screenshot capture matrix + golden smoke (visual_capture extension)', 'Jeroen''s ask (2026-07-16): in-game Atlas screenshots at several zoom levels across a varied body sample, treated as goldens for smoke tests. Recon (this session) mapped the path — reuse, don''t rebuild: extend client/tests/visual_capture.gd with an atlas-matrix mode (one Godot boot looping body x zoom x overlay-set), scenarios modeled on visual_scenarios.gd atlas_gen_open (currently GJ71c-hardcoded — parameterize); server lifecycle per tests/run-visual (--test-mode --port 0, parse LISTENING, SR_LIVE=1 — Atlas data is 100% systems.db regardless of world); waits on SimBridge.atlas_layers_received/city_names_received (status Ready) not fixed delays; capture via the existing _capture() (frame_post_draw -> save_png); xvfb-run + --rendering-driver opengl3, NEVER --headless (blank PNGs under dummy driver). Promote a curated subset into client/tests/golden/visual/ + tests/visual.json (existing tooling/visual-diff tolerance machinery, make visual-update). Known warts: no public zoom/pan setter on AtlasViewer (reach into _view_zoom/_view_offset + _apply_transform per existing convention, or add a small set_view() API); Sol uses the legacy markers path (exclude); async races need per-shot wait/timeout. Body sample suggestion: GJ380c, GJ144e, GJ338Bd, GJ820Bc, GJ251c (honest T-1116 water/no-roads exhibit), + a frozen body and a moon.', 'review', 'medium', 'stig', 'client', NULL, '2026-07-16 17:25:38.537', '2026-07-16 18:36:30.141', NULL, '4785da2d375c52d86af946406a1031f4', 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 ('06FPRTBJWG6Z7B8H5S115E2K74', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Shared live-server GdUnit helper — dedupe spawn/handshake/send-receive across roundtrip suites', 'PR #180 root-cause fallout (2026-07-16): test_input_roundtrip.gd and test_sprint2_proof.gd copy-paste the entire live-server harness (spawn + port retry + handshake + _send_and_receive) — which is why the T-1068 stance-cooldown pacing fix (COOLDOWN_TICKS post-ack spacing) existed in sprint2_proof but never reached input_roundtrip, producing today''s gate flake (MoveEast silently discarded by the D-053 Walk cooldown under load; root-caused with server trace logs). Extract a shared GdUnit helper (e.g. client/tests/helpers/live_server_harness.gd) carrying: port-retry spawn (server binds AFTER ~250ms init — death-during-connect retries on a fresh port), handshake+startup exchange, drain-then-send-then-wait with wall-clock deadline, and D-053 cooldown pacing. Port both suites onto it; any future live-roundtrip suite starts there. All three hardening rounds from today (port collision, wall-clock deadline, cooldown pacing) must survive the extraction with their comments intact.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-16 19:31:17.860', '2026-07-16 19:31:17.860', NULL, '441fe4bbf5e7ce46cce39ded40c01116', 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 ('06FPQXK8D6YVWVC2TB2ZR25K6G', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Atlas screenshot capture matrix + golden smoke (visual_capture extension)', 'Jeroen''s ask (2026-07-16): in-game Atlas screenshots at several zoom levels across a varied body sample, treated as goldens for smoke tests. Recon (this session) mapped the path — reuse, don''t rebuild: extend client/tests/visual_capture.gd with an atlas-matrix mode (one Godot boot looping body x zoom x overlay-set), scenarios modeled on visual_scenarios.gd atlas_gen_open (currently GJ71c-hardcoded — parameterize); server lifecycle per tests/run-visual (--test-mode --port 0, parse LISTENING, SR_LIVE=1 — Atlas data is 100% systems.db regardless of world); waits on SimBridge.atlas_layers_received/city_names_received (status Ready) not fixed delays; capture via the existing _capture() (frame_post_draw -> save_png); xvfb-run + --rendering-driver opengl3, NEVER --headless (blank PNGs under dummy driver). Promote a curated subset into client/tests/golden/visual/ + tests/visual.json (existing tooling/visual-diff tolerance machinery, make visual-update). Known warts: no public zoom/pan setter on AtlasViewer (reach into _view_zoom/_view_offset + _apply_transform per existing convention, or add a small set_view() API); Sol uses the legacy markers path (exclude); async races need per-shot wait/timeout. Body sample suggestion: GJ380c, GJ144e, GJ338Bd, GJ820Bc, GJ251c (honest T-1116 water/no-roads exhibit), + a frozen body and a moon.', 'done', 'medium', 'stig', 'client', NULL, '2026-07-16 17:25:38.537', '2026-07-16 19:43:56.313', NULL, 'b73fb765676495e74735564b36e0d8db', 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 ('06FPRZX1PJ55V7462N56ME2QZ0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'District-window map renderer — zoom-ladder PNGs from derive_district (aliveness_probe --render)', 'Jeroen (2026-07-16, reviewing the T-1120 screenshots): all Atlas captures are planetary-level; the orbit-to-jet-plane bridge (district-resolution deterministic biome maps) has never been SEEN. Recon verified the fast path: extend server/src/bin/aliveness_probe.rs with --render <dir> --window <N>: after the existing cascade_for_body + anchor-district computation (lines 34-113), loop an NxN district window around the anchor calling derive_district(seed, body_id, params, ta, (dx,dy), climate) PER CELL (district_profile.rs:1256 — the pure on-demand 2km carrier; do NOT index bws.districts, that is the coarse gcpr=8 eager grid) and write PNGs via the png crate (pattern at heightmap.rs:184-193): morphology zones (transliterate the 17-entry MORPHOLOGY_COLORS palette from atlas_marker_overlay.gd:65-83 into a Rust [[u8;3];17]), plus elev_q / temperature_c / moisture_q / vegetation_class panels. Deliverable: zoom-ladder image sets (e.g. 256x256 and 64x64 district windows) for 2-3 bodies anchored on settlements, demonstrating the deterministic orbital->district bridge. Quarter/block rendering is OUT of scope (no windowable pure function at that tier).', 'backlog', 'high', NULL, 'server', 'D-243', '2026-07-16 19:55:31.636', '2026-07-16 19:55:31.636', NULL, 'd83709a31f2ff298e8dd6e77f3b71b42', 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 ('06FPRZZ0P42NHC1QR5S3737EV4', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Design: Atlas regional map view — windowed district-resolution biome/climate screen (D-226)', 'Jeroen (2026-07-16): the Atlas needs the jet-plane altitude — a windowed district-resolution (2km-cell) map screen bridging the planetary canvas and the (never-mapped, D-226(d)) chunk/voxel world. Today the viewer is one whole-body texture with client-side pan/zoom; AtlasLayerRequest is {body_id, up_to} whole-body only — no rect/window/LOD machinery exists anywhere (recon-verified). Design pass (Tyre data shape + Araminta encoding, the T-1112 pattern): (1) windowed request protocol — rect/center+radius params on a new or extended layer request, served by calling derive_district per cell (district_profile.rs:1256, pure/on-demand — NOT the coarse eager grid); (2) which fields ship at district resolution (morphology, elev_q, temperature, moisture, vegetation — wire-integer discipline per D-010); (3) the screen: entry from a planetary-map click/settlement, second canvas vs zoom-threshold LOD swap, overlay/legend reuse; (4) budget: window size caps, caching, re-request-on-pan. Record as a D-226 amendment. District resolution is ABOVE the D-226(d) quarter-skeleton floor — in remit. Wiring is a follow-up ticket once the design is recorded. Natural batch-mates: T-1118 (climate overlay) + T-1119 (quarter glyphs). T-1123 (server-side window renderer) is the data-side proof and shares the per-cell derive pattern.', 'backlog', 'high', NULL, 'client', 'D-226', '2026-07-16 19:55:47.761', '2026-07-16 19:55:47.761', NULL, '5905b9c55f52b02ef75c079bae812df1', 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 ('06FPRZX1PJ55V7462N56ME2QZ0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'District-window map renderer — zoom-ladder PNGs from derive_district (aliveness_probe --render)', 'Jeroen (2026-07-16, reviewing the T-1120 screenshots): all Atlas captures are planetary-level; the orbit-to-jet-plane bridge (district-resolution deterministic biome maps) has never been SEEN. Recon verified the fast path: extend server/src/bin/aliveness_probe.rs with --render <dir> --window <N>: after the existing cascade_for_body + anchor-district computation (lines 34-113), loop an NxN district window around the anchor calling derive_district(seed, body_id, params, ta, (dx,dy), climate) PER CELL (district_profile.rs:1256 — the pure on-demand 2km carrier; do NOT index bws.districts, that is the coarse gcpr=8 eager grid) and write PNGs via the png crate (pattern at heightmap.rs:184-193): morphology zones (transliterate the 17-entry MORPHOLOGY_COLORS palette from atlas_marker_overlay.gd:65-83 into a Rust [[u8;3];17]), plus elev_q / temperature_c / moisture_q / vegetation_class panels. Deliverable: zoom-ladder image sets (e.g. 256x256 and 64x64 district windows) for 2-3 bodies anchored on settlements, demonstrating the deterministic orbital->district bridge. Quarter/block rendering is OUT of scope (no windowable pure function at that tier).', 'in_progress', 'high', NULL, 'server', 'D-243', '2026-07-16 19:55:31.636', '2026-07-16 19:55:54.662', NULL, '3713c77be183aef75c2e86e8b2e20997', 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 ('06FPRZX1PJ55V7462N56ME2QZ0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'District-window map renderer — zoom-ladder PNGs from derive_district (aliveness_probe --render)', 'Jeroen (2026-07-16, reviewing the T-1120 screenshots): all Atlas captures are planetary-level; the orbit-to-jet-plane bridge (district-resolution deterministic biome maps) has never been SEEN. Recon verified the fast path: extend server/src/bin/aliveness_probe.rs with --render <dir> --window <N>: after the existing cascade_for_body + anchor-district computation (lines 34-113), loop an NxN district window around the anchor calling derive_district(seed, body_id, params, ta, (dx,dy), climate) PER CELL (district_profile.rs:1256 — the pure on-demand 2km carrier; do NOT index bws.districts, that is the coarse gcpr=8 eager grid) and write PNGs via the png crate (pattern at heightmap.rs:184-193): morphology zones (transliterate the 17-entry MORPHOLOGY_COLORS palette from atlas_marker_overlay.gd:65-83 into a Rust [[u8;3];17]), plus elev_q / temperature_c / moisture_q / vegetation_class panels. Deliverable: zoom-ladder image sets (e.g. 256x256 and 64x64 district windows) for 2-3 bodies anchored on settlements, demonstrating the deterministic orbital->district bridge. Quarter/block rendering is OUT of scope (no windowable pure function at that tier).', 'in_progress', 'high', 'dudley', 'server', 'D-243', '2026-07-16 19:55:31.636', '2026-07-16 19:56:02.144', NULL, '962a3df16d9dc62be308e3d1065de065', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'D-227 invented detail does not feed district classification — the orbit-to-jet-plane bridge ends at ~500km', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64.', 'backlog', 'high', NULL, 'server', 'D-227', '2026-07-16 20:25:20.427', '2026-07-16 20:25:20.427', NULL, '5fa1dc1136f4a9373ab4676a21debcc6', 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 ('06FPS6RRBF9HHAED5QKTKQCY1G', 'bug', '06FGSJNP8YM537DJCC82ZD6544', 'derive_vegetation is ocean-blind — district maps paint Forest across open ocean', 'T-1123 zoom-ladder finding: derive_vegetation(temp, moisture, elev, near_water) has no ocean/water-mask input — GJ380c w256 renders solid Forest across ~250km of OpenOcean districts (see .cache/screenshots/district-windows gj380c_w256_veg.png vs _morphology.png). Voxel-level water generators mask this in-world, but any district-resolution Atlas view (T-1124) will show forested oceans. Fix: vegetation classification should return a None/Marine class when the district''s morphology/ocean_fraction says water (reuse ocean_fraction_q already computed in derive_district).', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-16 20:25:31.484', '2026-07-16 20:25:31.484', NULL, 'cc2649149c561804e790193ff77a5d08', 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 ('06FPS6TF9XDZPKZY8DHG1T0E2R', 'task', '06FGSJNP8YM537DJCC82ZD6544', 'Frozen bodies read temperate-green on district morphology maps — render glaciation_grade', 'T-1123 zoom-ladder finding: GJ244Ad (frozen planet_class, temp panel pegged at the -60C clamp floor, vegetation solid Barren) still renders temperate-green morphology — the 17-zone MorphologyZone vocabulary (T-1027, FROZEN per its own contract) has no ice/glacial zone, and glaciation_grade is a separate DistrictProfile field nothing renders. Do NOT reopen the frozen vocab by default: the likely fix is rendering glaciation_grade as a modifier/overlay on the morphology color (ice-tinting affected zones) in both the T-1123 probe renderer and the future T-1124 view — or, if the team decides an ice zone belongs in the vocabulary after all, that is a deliberate amendment to the freeze decision, not a drive-by. Images: .cache/screenshots/district-windows gj244ad_w256_morphology.png vs _temp.png.', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-16 20:25:45.551', '2026-07-16 20:25:45.551', NULL, '9e0446592b8a98b0db2641cb4b03192b', 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 ('06FPRZX1PJ55V7462N56ME2QZ0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'District-window map renderer — zoom-ladder PNGs from derive_district (aliveness_probe --render)', 'Jeroen (2026-07-16, reviewing the T-1120 screenshots): all Atlas captures are planetary-level; the orbit-to-jet-plane bridge (district-resolution deterministic biome maps) has never been SEEN. Recon verified the fast path: extend server/src/bin/aliveness_probe.rs with --render <dir> --window <N>: after the existing cascade_for_body + anchor-district computation (lines 34-113), loop an NxN district window around the anchor calling derive_district(seed, body_id, params, ta, (dx,dy), climate) PER CELL (district_profile.rs:1256 — the pure on-demand 2km carrier; do NOT index bws.districts, that is the coarse gcpr=8 eager grid) and write PNGs via the png crate (pattern at heightmap.rs:184-193): morphology zones (transliterate the 17-entry MORPHOLOGY_COLORS palette from atlas_marker_overlay.gd:65-83 into a Rust [[u8;3];17]), plus elev_q / temperature_c / moisture_q / vegetation_class panels. Deliverable: zoom-ladder image sets (e.g. 256x256 and 64x64 district windows) for 2-3 bodies anchored on settlements, demonstrating the deterministic orbital->district bridge. Quarter/block rendering is OUT of scope (no windowable pure function at that tier).', 'review', 'high', 'dudley', 'server', 'D-243', '2026-07-16 19:55:31.636', '2026-07-16 20:31:20.622', NULL, '80d25588e64088d49f8e6dfac7dc380f', 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 ('06FPRZX1PJ55V7462N56ME2QZ0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'District-window map renderer — zoom-ladder PNGs from derive_district (aliveness_probe --render)', 'Jeroen (2026-07-16, reviewing the T-1120 screenshots): all Atlas captures are planetary-level; the orbit-to-jet-plane bridge (district-resolution deterministic biome maps) has never been SEEN. Recon verified the fast path: extend server/src/bin/aliveness_probe.rs with --render <dir> --window <N>: after the existing cascade_for_body + anchor-district computation (lines 34-113), loop an NxN district window around the anchor calling derive_district(seed, body_id, params, ta, (dx,dy), climate) PER CELL (district_profile.rs:1256 — the pure on-demand 2km carrier; do NOT index bws.districts, that is the coarse gcpr=8 eager grid) and write PNGs via the png crate (pattern at heightmap.rs:184-193): morphology zones (transliterate the 17-entry MORPHOLOGY_COLORS palette from atlas_marker_overlay.gd:65-83 into a Rust [[u8;3];17]), plus elev_q / temperature_c / moisture_q / vegetation_class panels. Deliverable: zoom-ladder image sets (e.g. 256x256 and 64x64 district windows) for 2-3 bodies anchored on settlements, demonstrating the deterministic orbital->district bridge. Quarter/block rendering is OUT of scope (no windowable pure function at that tier).', 'done', 'high', 'dudley', 'server', 'D-243', '2026-07-16 19:55:31.636', '2026-07-16 20:36:46.488', NULL, 'c2a534d0423666a0d0c965c3a4d4a1a7', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'D-227 invented detail does not feed district classification — the orbit-to-jet-plane bridge ends at ~500km', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64.
Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010.', 'backlog', 'high', NULL, 'server', 'D-227', '2026-07-16 20:25:20.427', '2026-07-17 05:29:10.685', NULL, '7483d1c081ba0a6d4fc688e3783b9402', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'D-227 invented detail does not feed district classification — the orbit-to-jet-plane bridge ends at ~500km', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64.
Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010.
Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data.', 'backlog', 'high', NULL, 'server', 'D-227', '2026-07-16 20:25:20.427', '2026-07-17 05:30:58.591', NULL, 'a21b8705688641ff805676bebb8a26d4', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'D-227 invented detail does not feed district classification — the orbit-to-jet-plane bridge ends at ~500km', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64.
Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010.
Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data.
Jeroen, further (2026-07-17): ''more ocean is more wet and rainy, for instance'' — hydrosphere/ocean coverage as a tier-1 driver chain: large hydrosphere -> wetter, rainier climate -> HIGHER EROSION -> smoother, more mature coastlines (and more dendritic/eroded relief); small hydrosphere / thin atmosphere -> dry -> sharp, young, uneroded coasts. Erosion-proneness therefore need not be a new authored field — it is derivable from hydrosphere + atmosphere + precipitation, compounding with the tectonic/glaciation drivers. Note for implementation: hydrosphere already feeds moisture derivation (T-1034 fixed the vocab fall-through), so part of this chain exists — the new link is wet/rainy feeding COASTLINE/relief character, not just biome wetness.', 'backlog', 'high', NULL, 'server', 'D-227', '2026-07-16 20:25:20.427', '2026-07-17 05:32:00.842', NULL, 'a06a30fb996bd8f7b584dcd2efd9cd5d', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'D-227 invented detail does not feed district classification — the orbit-to-jet-plane bridge ends at ~500km', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64.
Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010.
Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data.
Jeroen, further (2026-07-17): ''more ocean is more wet and rainy, for instance'' — hydrosphere/ocean coverage as a tier-1 driver chain: large hydrosphere -> wetter, rainier climate -> HIGHER EROSION -> smoother, more mature coastlines (and more dendritic/eroded relief); small hydrosphere / thin atmosphere -> dry -> sharp, young, uneroded coasts. Erosion-proneness therefore need not be a new authored field — it is derivable from hydrosphere + atmosphere + precipitation, compounding with the tectonic/glaciation drivers. Note for implementation: hydrosphere already feeds moisture derivation (T-1034 fixed the vocab fall-through), so part of this chain exists — the new link is wet/rainy feeding COASTLINE/relief character, not just biome wetness.
---
Gap resolution (2026-07-17, Si review + Jeroen''s rulings):
1. TILT/D-240: DROPPED — no axial tilt input; D-240 stays intact. latitude_deg alone carries the position-tier polar effects (glaciation_grade is already latitude/temperature-derived). Tier-1 envelope CONFIRMED as what exists: planet_class + hydrosphere + atmosphere + body_radius_km (BodyParams, body_params_reader.rs:14-20). No tectonic-activity or erosion field exists — tectonic_class derives from planet_class (derive_tectonic_class always falls to its planet_class branch in production); erosion-proneness is DERIVED from hydrosphere + atmosphere + precipitation per Jeroen''s chain. Do not hunt for richer body signals; they are not there.
2. CIRCULARITY: ACCEPT STALE INPUT — glaciation_grade reads the UNWARPED raw-bilinear ocean_fraction_q (one-step-stale, deterministic, coarse ~156km-pixel value anyway). Document on the call site. tectonic_class is cheap to hoist to the top of derive_district (needs only BodyParams).
3. SCOPE: BOTH PATHS — the invention must feed derive_district (on-demand: probe + future T-1124 view) AND the production derive_district_profile (district_profile.rs:1062-1134, the batch path BodyWorldState stores — currently has NO scatter at all; the paths silently diverged). Implementer may unify the shared classification core if cleaner than duplicating — his call, reviewers verify. Believability goldens (server/tests/golden/believability.json) WILL regenerate — expected and acknowledged via UPDATE_GOLDEN=1; report the metric deltas (contrast/coherence blocks) in the PR as evidence of improvement, not just churn.
Si verification facts for the implementer:
- Direct reusable precedents: climate_edge_warp (region_profile.rs:477-513) — warp-the-sampling with a distinct hash path, exactly the pattern for ocean_fraction_q; domain_warp.rs (T-1026, stateless (seed,body,pos)->offset); detail_scatter.rs voxel_mosaic (T-1084) proves slope-INDEPENDENT position-keyed scatter is established. Longitude variation comes free via position-keyed noise on world-metre coords — no explicit longitude plumbing needed.
- Fix shape (from the ticket + rulings): warp the ocean-mask sampling position (invented bays/capes), slope-independent scatter floor whose AMPLITUDE/CHARACTER varies by the two driver tiers (body envelope: planet_class/hydrosphere/atmosphere/radius -> personality incl. erosion-smoothness; position: latitude, glaciation_grade, tectonic_class, climate bands -> fjordy vs smooth vs sharp), plus seeded heterogeneity so same-coast stretches differ. All deterministic seed+body+position (D-227/D-010); continuous across district/region boundaries (D-243 edge-fuzz discipline — character never steps on a line).
- Acceptance: (a) before/after zoom ladders (aliveness_probe --render, same bodies/windows as T-1123: GJ380c/GJ820Bc/GJ244Ad at w256/w64/w16) reviewed by Jeroen — the primary artifact; (b) believability metrics moved not regressed; new coastline-legibility criterion work belongs to open Q-123, not this ticket. (c) determinism byte-compare as in T-1123.
- Performance: no per-district budget record exists; do the D-246-style check — verify derive_district cost stays same order (~8us was the T-1123 measurement) and the believability/derivation harness budgets stay green.', 'backlog', 'high', NULL, 'server', 'D-227', '2026-07-16 20:25:20.427', '2026-07-17 05:37:57.401', NULL, '538b97e5e51823c4f9599fdb4bc7e5ad', 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 ('06FPS6RRBF9HHAED5QKTKQCY1G', 'bug', '06FGSJNP8YM537DJCC82ZD6544', 'derive_vegetation is ocean-blind — district maps paint Forest across open ocean', 'T-1123 zoom-ladder finding: derive_vegetation(temp, moisture, elev, near_water) has no ocean/water-mask input — GJ380c w256 renders solid Forest across ~250km of OpenOcean districts (see .cache/screenshots/district-windows gj380c_w256_veg.png vs _morphology.png). Voxel-level water generators mask this in-world, but any district-resolution Atlas view (T-1124) will show forested oceans. Fix: vegetation classification should return a None/Marine class when the district''s morphology/ocean_fraction says water (reuse ocean_fraction_q already computed in derive_district).
Gap resolution (2026-07-17, Si review + Jeroen): (1) GATE: morphology-derived — pass the already-computed morphology water verdict (OpenOcean/Lake) into the vegetation call; vegetation and morphology can NEVER disagree by construction; no fourth threshold is born. (2) VARIANT: Marine, appended at discriminant 6 (after RiparianThicket=5); update the load-bearing vegetation_class_discriminants_pinned test; document that Ord position is non-semantic (the density-ladder doc does not extend to Marine). (3) SEQUENCING: blocked by T-1125 (edge added) — classify against the warped/invented coastline, not the smooth one. Si facts: the ticket phrase near_water is a TRAP — derive_vegetation''s 4th param is near_perennial_water (riparian/river proximity), unrelated to ocean; do not reuse it. voxel.rs consumers are wildcard-safe (ocean districts route to the WaterBody generator upstream per D-239/T-1082) — no changes needed there. aliveness_probe.rs vegetation_rgb is an EXHAUSTIVE match and will fail to compile until it gets a Marine color — extend the palette deliberately (ocean blue distinct from morphology water tones). D-239 gets an implementation-addendum note recording the Marine variant (its §8 climate-vegetation law predates a water class).', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-16 20:25:31.484', '2026-07-17 05:38:10.917', NULL, 'd5ab3fc6645ce75ffc423aaec391d033', 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 ('06FPS6TF9XDZPKZY8DHG1T0E2R', 'task', '06FGSJNP8YM537DJCC82ZD6544', 'Frozen bodies read temperate-green on district morphology maps — render glaciation_grade', 'T-1123 zoom-ladder finding: GJ244Ad (frozen planet_class, temp panel pegged at the -60C clamp floor, vegetation solid Barren) still renders temperate-green morphology — the 17-zone MorphologyZone vocabulary (T-1027, FROZEN per its own contract) has no ice/glacial zone, and glaciation_grade is a separate DistrictProfile field nothing renders. Do NOT reopen the frozen vocab by default: the likely fix is rendering glaciation_grade as a modifier/overlay on the morphology color (ice-tinting affected zones) in both the T-1123 probe renderer and the future T-1124 view — or, if the team decides an ice zone belongs in the vocabulary after all, that is a deliberate amendment to the freeze decision, not a drive-by. Images: .cache/screenshots/district-windows gj244ad_w256_morphology.png vs _temp.png.
Refinement (2026-07-17, Si review): READY. Upstream verified HEALTHY — glaciation derivation (derive_glaciation_grade_from_climate, district_profile.rs:339-366) is correctly wired and even gates the Fjord family; GJ244Ad moisture clears the gate floor. This is purely a rendering gap: aliveness_probe render_window_panels computes the full DistrictProfile and discards .glaciation_grade (line ~458 reads only morphology_zone). SCOPE RULING (lead): probe-only THIS ticket — per-pixel ice tint blended on the morphology panel keyed on glaciation_grade (+ optionally a 6th dedicated glaciation panel; PANEL_NAMES/bufs are array-driven, cheap). The wire+client half (glaciation: Vec<u8> parallel array inside DistrictGridLayer + build_district_grid + protocol.gd + _draw_gen_district — governance-UNCONSTRAINED per the D-226 growth-ceiling reading: the cap governs new top-level Option layers, not per-cell fields inside a shipped layer; elev_q precedent) is DEFERRED to T-1124''s design pass, which owns the which-fields-ship-at-district-resolution question. Freeze contract confirmed: D-239 SS6 — tint is classifier-tuning territory, no MorphologyZone vocab reopen; an ice zone would need its own explicit D-amendment (not pursued).', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-16 20:25:45.551', '2026-07-17 05:38:24.161', NULL, '43a14c30a1bf3855a9ca6c6d076e0d30', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'D-227 invented detail does not feed district classification — the orbit-to-jet-plane bridge ends at ~500km', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64.
Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010.
Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data.
Jeroen, further (2026-07-17): ''more ocean is more wet and rainy, for instance'' — hydrosphere/ocean coverage as a tier-1 driver chain: large hydrosphere -> wetter, rainier climate -> HIGHER EROSION -> smoother, more mature coastlines (and more dendritic/eroded relief); small hydrosphere / thin atmosphere -> dry -> sharp, young, uneroded coasts. Erosion-proneness therefore need not be a new authored field — it is derivable from hydrosphere + atmosphere + precipitation, compounding with the tectonic/glaciation drivers. Note for implementation: hydrosphere already feeds moisture derivation (T-1034 fixed the vocab fall-through), so part of this chain exists — the new link is wet/rainy feeding COASTLINE/relief character, not just biome wetness.
---
Gap resolution (2026-07-17, Si review + Jeroen''s rulings):
1. TILT/D-240: DROPPED — no axial tilt input; D-240 stays intact. latitude_deg alone carries the position-tier polar effects (glaciation_grade is already latitude/temperature-derived). Tier-1 envelope CONFIRMED as what exists: planet_class + hydrosphere + atmosphere + body_radius_km (BodyParams, body_params_reader.rs:14-20). No tectonic-activity or erosion field exists — tectonic_class derives from planet_class (derive_tectonic_class always falls to its planet_class branch in production); erosion-proneness is DERIVED from hydrosphere + atmosphere + precipitation per Jeroen''s chain. Do not hunt for richer body signals; they are not there.
2. CIRCULARITY: ACCEPT STALE INPUT — glaciation_grade reads the UNWARPED raw-bilinear ocean_fraction_q (one-step-stale, deterministic, coarse ~156km-pixel value anyway). Document on the call site. tectonic_class is cheap to hoist to the top of derive_district (needs only BodyParams).
3. SCOPE: BOTH PATHS — the invention must feed derive_district (on-demand: probe + future T-1124 view) AND the production derive_district_profile (district_profile.rs:1062-1134, the batch path BodyWorldState stores — currently has NO scatter at all; the paths silently diverged). Implementer may unify the shared classification core if cleaner than duplicating — his call, reviewers verify. Believability goldens (server/tests/golden/believability.json) WILL regenerate — expected and acknowledged via UPDATE_GOLDEN=1; report the metric deltas (contrast/coherence blocks) in the PR as evidence of improvement, not just churn.
Si verification facts for the implementer:
- Direct reusable precedents: climate_edge_warp (region_profile.rs:477-513) — warp-the-sampling with a distinct hash path, exactly the pattern for ocean_fraction_q; domain_warp.rs (T-1026, stateless (seed,body,pos)->offset); detail_scatter.rs voxel_mosaic (T-1084) proves slope-INDEPENDENT position-keyed scatter is established. Longitude variation comes free via position-keyed noise on world-metre coords — no explicit longitude plumbing needed.
- Fix shape (from the ticket + rulings): warp the ocean-mask sampling position (invented bays/capes), slope-independent scatter floor whose AMPLITUDE/CHARACTER varies by the two driver tiers (body envelope: planet_class/hydrosphere/atmosphere/radius -> personality incl. erosion-smoothness; position: latitude, glaciation_grade, tectonic_class, climate bands -> fjordy vs smooth vs sharp), plus seeded heterogeneity so same-coast stretches differ. All deterministic seed+body+position (D-227/D-010); continuous across district/region boundaries (D-243 edge-fuzz discipline — character never steps on a line).
- Acceptance: (a) before/after zoom ladders (aliveness_probe --render, same bodies/windows as T-1123: GJ380c/GJ820Bc/GJ244Ad at w256/w64/w16) reviewed by Jeroen — the primary artifact; (b) believability metrics moved not regressed; new coastline-legibility criterion work belongs to open Q-123, not this ticket. (c) determinism byte-compare as in T-1123.
- Performance: no per-district budget record exists; do the D-246-style check — verify derive_district cost stays same order (~8us was the T-1123 measurement) and the believability/derivation harness budgets stay green.', 'in_progress', 'high', NULL, 'server', 'D-227', '2026-07-16 20:25:20.427', '2026-07-17 05:38:38.565', NULL, '764115aba836c846aba43a62b813ed36', 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 ('06FPS6RRBF9HHAED5QKTKQCY1G', 'bug', '06FGSJNP8YM537DJCC82ZD6544', 'derive_vegetation is ocean-blind — district maps paint Forest across open ocean', 'T-1123 zoom-ladder finding: derive_vegetation(temp, moisture, elev, near_water) has no ocean/water-mask input — GJ380c w256 renders solid Forest across ~250km of OpenOcean districts (see .cache/screenshots/district-windows gj380c_w256_veg.png vs _morphology.png). Voxel-level water generators mask this in-world, but any district-resolution Atlas view (T-1124) will show forested oceans. Fix: vegetation classification should return a None/Marine class when the district''s morphology/ocean_fraction says water (reuse ocean_fraction_q already computed in derive_district).
Gap resolution (2026-07-17, Si review + Jeroen): (1) GATE: morphology-derived — pass the already-computed morphology water verdict (OpenOcean/Lake) into the vegetation call; vegetation and morphology can NEVER disagree by construction; no fourth threshold is born. (2) VARIANT: Marine, appended at discriminant 6 (after RiparianThicket=5); update the load-bearing vegetation_class_discriminants_pinned test; document that Ord position is non-semantic (the density-ladder doc does not extend to Marine). (3) SEQUENCING: blocked by T-1125 (edge added) — classify against the warped/invented coastline, not the smooth one. Si facts: the ticket phrase near_water is a TRAP — derive_vegetation''s 4th param is near_perennial_water (riparian/river proximity), unrelated to ocean; do not reuse it. voxel.rs consumers are wildcard-safe (ocean districts route to the WaterBody generator upstream per D-239/T-1082) — no changes needed there. aliveness_probe.rs vegetation_rgb is an EXHAUSTIVE match and will fail to compile until it gets a Marine color — extend the palette deliberately (ocean blue distinct from morphology water tones). D-239 gets an implementation-addendum note recording the Marine variant (its §8 climate-vegetation law predates a water class).', 'in_progress', 'medium', NULL, 'server', NULL, '2026-07-16 20:25:31.484', '2026-07-17 05:38:38.573', NULL, '8f3b6d7cab85fb02ef8e134f8a6fe6e7', 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 ('06FPS6TF9XDZPKZY8DHG1T0E2R', 'task', '06FGSJNP8YM537DJCC82ZD6544', 'Frozen bodies read temperate-green on district morphology maps — render glaciation_grade', 'T-1123 zoom-ladder finding: GJ244Ad (frozen planet_class, temp panel pegged at the -60C clamp floor, vegetation solid Barren) still renders temperate-green morphology — the 17-zone MorphologyZone vocabulary (T-1027, FROZEN per its own contract) has no ice/glacial zone, and glaciation_grade is a separate DistrictProfile field nothing renders. Do NOT reopen the frozen vocab by default: the likely fix is rendering glaciation_grade as a modifier/overlay on the morphology color (ice-tinting affected zones) in both the T-1123 probe renderer and the future T-1124 view — or, if the team decides an ice zone belongs in the vocabulary after all, that is a deliberate amendment to the freeze decision, not a drive-by. Images: .cache/screenshots/district-windows gj244ad_w256_morphology.png vs _temp.png.
Refinement (2026-07-17, Si review): READY. Upstream verified HEALTHY — glaciation derivation (derive_glaciation_grade_from_climate, district_profile.rs:339-366) is correctly wired and even gates the Fjord family; GJ244Ad moisture clears the gate floor. This is purely a rendering gap: aliveness_probe render_window_panels computes the full DistrictProfile and discards .glaciation_grade (line ~458 reads only morphology_zone). SCOPE RULING (lead): probe-only THIS ticket — per-pixel ice tint blended on the morphology panel keyed on glaciation_grade (+ optionally a 6th dedicated glaciation panel; PANEL_NAMES/bufs are array-driven, cheap). The wire+client half (glaciation: Vec<u8> parallel array inside DistrictGridLayer + build_district_grid + protocol.gd + _draw_gen_district — governance-UNCONSTRAINED per the D-226 growth-ceiling reading: the cap governs new top-level Option layers, not per-cell fields inside a shipped layer; elev_q precedent) is DEFERRED to T-1124''s design pass, which owns the which-fields-ship-at-district-resolution question. Freeze contract confirmed: D-239 SS6 — tint is classifier-tuning territory, no MorphologyZone vocab reopen; an ice zone would need its own explicit D-amendment (not pursued).', 'in_progress', 'medium', NULL, 'server', NULL, '2026-07-16 20:25:45.551', '2026-07-17 05:38:38.574', NULL, 'd096e14e3dc8723f6b2b8521316980c4', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'D-227 invented detail does not feed district classification — the orbit-to-jet-plane bridge ends at ~500km', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64.
Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010.
Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data.
Jeroen, further (2026-07-17): ''more ocean is more wet and rainy, for instance'' — hydrosphere/ocean coverage as a tier-1 driver chain: large hydrosphere -> wetter, rainier climate -> HIGHER EROSION -> smoother, more mature coastlines (and more dendritic/eroded relief); small hydrosphere / thin atmosphere -> dry -> sharp, young, uneroded coasts. Erosion-proneness therefore need not be a new authored field — it is derivable from hydrosphere + atmosphere + precipitation, compounding with the tectonic/glaciation drivers. Note for implementation: hydrosphere already feeds moisture derivation (T-1034 fixed the vocab fall-through), so part of this chain exists — the new link is wet/rainy feeding COASTLINE/relief character, not just biome wetness.
---
Gap resolution (2026-07-17, Si review + Jeroen''s rulings):
1. TILT/D-240: DROPPED — no axial tilt input; D-240 stays intact. latitude_deg alone carries the position-tier polar effects (glaciation_grade is already latitude/temperature-derived). Tier-1 envelope CONFIRMED as what exists: planet_class + hydrosphere + atmosphere + body_radius_km (BodyParams, body_params_reader.rs:14-20). No tectonic-activity or erosion field exists — tectonic_class derives from planet_class (derive_tectonic_class always falls to its planet_class branch in production); erosion-proneness is DERIVED from hydrosphere + atmosphere + precipitation per Jeroen''s chain. Do not hunt for richer body signals; they are not there.
2. CIRCULARITY: ACCEPT STALE INPUT — glaciation_grade reads the UNWARPED raw-bilinear ocean_fraction_q (one-step-stale, deterministic, coarse ~156km-pixel value anyway). Document on the call site. tectonic_class is cheap to hoist to the top of derive_district (needs only BodyParams).
3. SCOPE: BOTH PATHS — the invention must feed derive_district (on-demand: probe + future T-1124 view) AND the production derive_district_profile (district_profile.rs:1062-1134, the batch path BodyWorldState stores — currently has NO scatter at all; the paths silently diverged). Implementer may unify the shared classification core if cleaner than duplicating — his call, reviewers verify. Believability goldens (server/tests/golden/believability.json) WILL regenerate — expected and acknowledged via UPDATE_GOLDEN=1; report the metric deltas (contrast/coherence blocks) in the PR as evidence of improvement, not just churn.
Si verification facts for the implementer:
- Direct reusable precedents: climate_edge_warp (region_profile.rs:477-513) — warp-the-sampling with a distinct hash path, exactly the pattern for ocean_fraction_q; domain_warp.rs (T-1026, stateless (seed,body,pos)->offset); detail_scatter.rs voxel_mosaic (T-1084) proves slope-INDEPENDENT position-keyed scatter is established. Longitude variation comes free via position-keyed noise on world-metre coords — no explicit longitude plumbing needed.
- Fix shape (from the ticket + rulings): warp the ocean-mask sampling position (invented bays/capes), slope-independent scatter floor whose AMPLITUDE/CHARACTER varies by the two driver tiers (body envelope: planet_class/hydrosphere/atmosphere/radius -> personality incl. erosion-smoothness; position: latitude, glaciation_grade, tectonic_class, climate bands -> fjordy vs smooth vs sharp), plus seeded heterogeneity so same-coast stretches differ. All deterministic seed+body+position (D-227/D-010); continuous across district/region boundaries (D-243 edge-fuzz discipline — character never steps on a line).
- Acceptance: (a) before/after zoom ladders (aliveness_probe --render, same bodies/windows as T-1123: GJ380c/GJ820Bc/GJ244Ad at w256/w64/w16) reviewed by Jeroen — the primary artifact; (b) believability metrics moved not regressed; new coastline-legibility criterion work belongs to open Q-123, not this ticket. (c) determinism byte-compare as in T-1123.
- Performance: no per-district budget record exists; do the D-246-style check — verify derive_district cost stays same order (~8us was the T-1123 measurement) and the believability/derivation harness budgets stay green.', 'in_progress', 'high', 'dudley', 'server', 'D-227', '2026-07-16 20:25:20.427', '2026-07-17 05:38:51.394', NULL, 'd46e951ec6051bced263c73b6ba8c615', 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 ('06FPS6RRBF9HHAED5QKTKQCY1G', 'bug', '06FGSJNP8YM537DJCC82ZD6544', 'derive_vegetation is ocean-blind — district maps paint Forest across open ocean', 'T-1123 zoom-ladder finding: derive_vegetation(temp, moisture, elev, near_water) has no ocean/water-mask input — GJ380c w256 renders solid Forest across ~250km of OpenOcean districts (see .cache/screenshots/district-windows gj380c_w256_veg.png vs _morphology.png). Voxel-level water generators mask this in-world, but any district-resolution Atlas view (T-1124) will show forested oceans. Fix: vegetation classification should return a None/Marine class when the district''s morphology/ocean_fraction says water (reuse ocean_fraction_q already computed in derive_district).
Gap resolution (2026-07-17, Si review + Jeroen): (1) GATE: morphology-derived — pass the already-computed morphology water verdict (OpenOcean/Lake) into the vegetation call; vegetation and morphology can NEVER disagree by construction; no fourth threshold is born. (2) VARIANT: Marine, appended at discriminant 6 (after RiparianThicket=5); update the load-bearing vegetation_class_discriminants_pinned test; document that Ord position is non-semantic (the density-ladder doc does not extend to Marine). (3) SEQUENCING: blocked by T-1125 (edge added) — classify against the warped/invented coastline, not the smooth one. Si facts: the ticket phrase near_water is a TRAP — derive_vegetation''s 4th param is near_perennial_water (riparian/river proximity), unrelated to ocean; do not reuse it. voxel.rs consumers are wildcard-safe (ocean districts route to the WaterBody generator upstream per D-239/T-1082) — no changes needed there. aliveness_probe.rs vegetation_rgb is an EXHAUSTIVE match and will fail to compile until it gets a Marine color — extend the palette deliberately (ocean blue distinct from morphology water tones). D-239 gets an implementation-addendum note recording the Marine variant (its §8 climate-vegetation law predates a water class).', 'in_progress', 'medium', 'dudley', 'server', NULL, '2026-07-16 20:25:31.484', '2026-07-17 05:38:51.406', NULL, '1b963315c1bd8f52f24d11610f511ca0', 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 ('06FPS6TF9XDZPKZY8DHG1T0E2R', 'task', '06FGSJNP8YM537DJCC82ZD6544', 'Frozen bodies read temperate-green on district morphology maps — render glaciation_grade', 'T-1123 zoom-ladder finding: GJ244Ad (frozen planet_class, temp panel pegged at the -60C clamp floor, vegetation solid Barren) still renders temperate-green morphology — the 17-zone MorphologyZone vocabulary (T-1027, FROZEN per its own contract) has no ice/glacial zone, and glaciation_grade is a separate DistrictProfile field nothing renders. Do NOT reopen the frozen vocab by default: the likely fix is rendering glaciation_grade as a modifier/overlay on the morphology color (ice-tinting affected zones) in both the T-1123 probe renderer and the future T-1124 view — or, if the team decides an ice zone belongs in the vocabulary after all, that is a deliberate amendment to the freeze decision, not a drive-by. Images: .cache/screenshots/district-windows gj244ad_w256_morphology.png vs _temp.png.
Refinement (2026-07-17, Si review): READY. Upstream verified HEALTHY — glaciation derivation (derive_glaciation_grade_from_climate, district_profile.rs:339-366) is correctly wired and even gates the Fjord family; GJ244Ad moisture clears the gate floor. This is purely a rendering gap: aliveness_probe render_window_panels computes the full DistrictProfile and discards .glaciation_grade (line ~458 reads only morphology_zone). SCOPE RULING (lead): probe-only THIS ticket — per-pixel ice tint blended on the morphology panel keyed on glaciation_grade (+ optionally a 6th dedicated glaciation panel; PANEL_NAMES/bufs are array-driven, cheap). The wire+client half (glaciation: Vec<u8> parallel array inside DistrictGridLayer + build_district_grid + protocol.gd + _draw_gen_district — governance-UNCONSTRAINED per the D-226 growth-ceiling reading: the cap governs new top-level Option layers, not per-cell fields inside a shipped layer; elev_q precedent) is DEFERRED to T-1124''s design pass, which owns the which-fields-ship-at-district-resolution question. Freeze contract confirmed: D-239 SS6 — tint is classifier-tuning territory, no MorphologyZone vocab reopen; an ice zone would need its own explicit D-amendment (not pursued).', 'in_progress', 'medium', 'dudley', 'server', NULL, '2026-07-16 20:25:45.551', '2026-07-17 05:38:51.406', NULL, '56b6a078fc17ed79dc85768ab857dd44', 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 ('06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'epic', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Standalone Atlas companion app — make atlas (read-only reader, future trading)', 'Jeroen (2026-07-17): the Atlas should run as a standalone desktop application/reader via make atlas, against the RUNNING game server or by starting its own (with a save/load interaction seam). Read-only for now; may later support market trading against the running game. Three stated purposes: (1) lightweight app to scan ALL database data (dev data-inspection surface over systems/bodies/corps/settlements/etc.); (2) remains available as a LEGITIMATE player-facing pattern post-release — a diegetic second-monitor companion; (3) attach-or-spawn with save/load interaction (saves are Phase 5+ — v1 designs the seam, offers world-seed selection). Read-only enforcement implies a reader/spectator connection class on the bridge that never spawns a character or sends inputs — likely the first true multi-connection work (Oscar''s remit). Design pass first (D-254 claimed for the record); implementation tickets follow the recorded design.', 'backlog', 'high', NULL, NULL, NULL, '2026-07-17 05:47:53.088', '2026-07-17 05:47:53.088', NULL, '15e4ee84818115da35db352275277b10', 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 ('06FPX7HZY1CK3VSTRXP1NP9VYW', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Design pass: D-254 companion-app architecture — connection model, reader protocol, app shell, data browser, save/load seam', 'Produce and record D-254 (id claimed). Participants: Tyre (architecture lead, writes the record) + Oscar (protocol/connection model — his activation). Must pin: (1) CONNECTION MODEL — attach to the running game server vs spawn-own (--test-mode? which world?) incl. discovery (port/SR_ADDR), and the multi-connection reality of the bridge today (recon: almost certainly single-connection; a second reader connection is new protocol work); (2) READER CONNECTION CLASS — handshake variant that never spawns a character and cannot send inputs (read-only enforced SERVER-side, not client courtesy — future market trading would widen this deliberately per-verb, design the enum now); (3) APP SHELL — how make atlas launches the existing implant Atlas UI standalone (second main scene vs feature flag vs export preset; keep the implant aesthetic — the diegetic second-monitor pattern is a product goal); (4) DATA BROWSER — Jeroen wants to scan ALL database data: define the browse surface (entity list/detail screens from implant components: systems, bodies, corps, settlements, markets) and its data path (existing wire requests vs a generic read API vs local systems.db read for static data + wire for dynamic — mind the D-010 information-boundary rules for what a reader may see); (5) SAVE/LOAD SEAM — saves are Phase 5+; design the interaction now (attach = inherits the running world; spawn = pick seed today, pick save later), record the hook; (6) FUTURE TRADING note — what changes when the app gains write verbs (auth? per-verb allowlist on the reader class?). Deliverable: D-254 record written in governance/decisions/architecture.md + a proposed implementation ticket tree under T-1128. Wiring is NOT this ticket.', 'backlog', 'high', NULL, 'server', NULL, '2026-07-17 05:48:12.656', '2026-07-17 05:48:12.656', NULL, '293a6ca42749bbad299da7e217a8fcc6', 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 ('06FPX7HZY1CK3VSTRXP1NP9VYW', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Design pass: D-254 companion-app architecture — connection model, reader protocol, app shell, data browser, save/load seam', 'Produce and record D-254 (id claimed). Participants: Tyre (architecture lead, writes the record) + Oscar (protocol/connection model — his activation). Must pin: (1) CONNECTION MODEL — attach to the running game server vs spawn-own (--test-mode? which world?) incl. discovery (port/SR_ADDR), and the multi-connection reality of the bridge today (recon: almost certainly single-connection; a second reader connection is new protocol work); (2) READER CONNECTION CLASS — handshake variant that never spawns a character and cannot send inputs (read-only enforced SERVER-side, not client courtesy — future market trading would widen this deliberately per-verb, design the enum now); (3) APP SHELL — how make atlas launches the existing implant Atlas UI standalone (second main scene vs feature flag vs export preset; keep the implant aesthetic — the diegetic second-monitor pattern is a product goal); (4) DATA BROWSER — Jeroen wants to scan ALL database data: define the browse surface (entity list/detail screens from implant components: systems, bodies, corps, settlements, markets) and its data path (existing wire requests vs a generic read API vs local systems.db read for static data + wire for dynamic — mind the D-010 information-boundary rules for what a reader may see); (5) SAVE/LOAD SEAM — saves are Phase 5+; design the interaction now (attach = inherits the running world; spawn = pick seed today, pick save later), record the hook; (6) FUTURE TRADING note — what changes when the app gains write verbs (auth? per-verb allowlist on the reader class?). Deliverable: D-254 record written in governance/decisions/architecture.md + a proposed implementation ticket tree under T-1128. Wiring is NOT this ticket.', 'in_progress', 'high', NULL, 'server', NULL, '2026-07-17 05:48:12.656', '2026-07-17 05:48:21.364', NULL, '42d787f064722c83d45529642ecc597c', 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 ('06FPXAS26J3BS7ZJVRFT5JDXP8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Reader connection: multi-connection bridge + ConnectionRole gate', 'D-254 SS1/SS2 (record on the atlas-companion-app branch; ref attaches after merge+sync). THE GATING TICKET — everything else in T-1128 needs a server that can hold a second connection without hanging it (today: main.rs single blocking accept at ~line 120; a second client hangs silently forever). Scope: ConnectionRole field on StartupMessage (Player | Reader, serde default Player for back-compat, enum shaped for future TradingReader); TcpListener blocking single-accept -> non-blocking per-tick accept loop; BridgeResource becomes 0-1 Player + 0-N Reader collection (NOT general N-player — D-009 scope stays separate); Reader never enters the character-spawn path and receives NO ObserverSnapshot (structural, not filtered); per-connection response tagging for atlas/starmap/citynames request-response buffers; Player-only whole-server-shutdown-on-disconnect (Reader disconnects must never affect the player session); permitted-message matrix enforcement server-side — violation = log + drop frame, repeated = disconnect the reader; lower per-reader inbound frame cap. Deterministic sim unaffected by reader presence. Oscar''s design, Oscar implements.', 'backlog', 'high', NULL, 'server', NULL, '2026-07-17 06:02:17.012', '2026-07-17 06:02:17.012', NULL, 'e1d09618c5db58eaef42b5cf6dd80869', 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 ('06FPXATGN8QJAJVR0WCRHJS2TG', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Browse data proxy — six v1 entity read endpoints over the wire', 'D-254 SS4. BrowseRequest/Response per-entity-kind types (star systems, bodies, stations, corporations, commodities, trait templates) in server/src/atlas/, sibling to atlas_data_proxy.rs; rusqlite reads against systems.db via the CityContextReader::open() pattern (five-ish new read functions on a proven pattern); wired into the same inbound demux the reader connection uses. Wire-only by D-254 ruling (extends the T-949 precedent: the client never reads game data files directly; no SQLite GDExtension). Excluded from v1: cascade-derived atlas geometry tables (partially populated mid-Phase-4) and event-log tables (wrong UI shape) — exclusion list recorded in D-254. Blocked by the reader-connection ticket (needs the connection class to gate against).', 'backlog', 'high', NULL, 'server', NULL, '2026-07-17 06:02:28.906', '2026-07-17 06:02:28.906', NULL, 'a679c6b81981a1d3cd18a5bf4aaac757', 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 ('06FPXAWGRH9QK5QKV7584Q92Z8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Standalone entry scene + attach-or-spawn boot + make atlas target', 'D-254 SS1/SS3/SS5. client/scenes/atlas_standalone.tscn + atlas_standalone.gd (modeled on visual_capture.gd''s boot shape — NOT a flag on main.tscn, which is saturated with player-only paths): attach-mode = TCP connect SR_PORT-or-9876 with ~500ms timeout; ECONNREFUSED falls through to spawn-mode = own child server via the server_process.gd ownership pattern with --port 0 + LISTENING:{port} parse, REAL systems.db (no --test-mode); sends Reader-role StartupMessage; opens implant/map fullscreen (skips main menu/character creation); window title branding via DisplayServer.window_set_title; intercept atlas close-to-gameplay in atlas_standalone.gd (do not modify atlas_app.gd). Makefile: target = trivial build+launch (the scene owns the attach-or-spawn decision — no Makefile orchestration; and per Oscar''s flag this simplicity does NOT shrink the T-1130 server work). Exportable later as a Godot export preset, no architecture change. Blocked by T-1130 for real attach-mode testing (developable against spawn-mode before that). Blocks the browser-UI ticket (needs a shell to launch from).', 'backlog', 'high', NULL, 'client', NULL, '2026-07-17 06:02:45.316', '2026-07-17 06:02:45.316', NULL, '122d617d90df55dc685b1da0e0a62135', 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 ('06FPXAWGRH9QK5QKV7584Q92Z8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Standalone entry scene + attach-or-spawn boot + make atlas target', 'D-254 SS1/SS3/SS5. client/scenes/atlas_standalone.tscn + atlas_standalone.gd (modeled on visual_capture.gd''s boot shape — NOT a flag on main.tscn, which is saturated with player-only paths): attach-mode = TCP connect SR_PORT-or-9876 with ~500ms timeout; ECONNREFUSED falls through to spawn-mode = own child server via the server_process.gd ownership pattern with --port 0 + LISTENING:{port} parse, REAL systems.db (no --test-mode); sends Reader-role StartupMessage; opens implant/map fullscreen (skips main menu/character creation); window title branding via DisplayServer.window_set_title; intercept atlas close-to-gameplay in atlas_standalone.gd (do not modify atlas_app.gd). Makefile: target = trivial build+launch (the scene owns the attach-or-spawn decision — no Makefile orchestration; and per Oscar''s flag this simplicity does NOT shrink the T-1130 server work). Exportable later as a Godot export preset, no architecture change. Blocked by T-1130 for real attach-mode testing (developable against spawn-mode before that). Blocks the browser-UI ticket (needs a shell to launch from).
Correction: the Makefile target name in the line above is ''atlas'' (make atlas) — shell backticks ate the word during filing.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-17 06:02:45.316', '2026-07-17 06:03:20.063', NULL, '199d41e8b2cf05d2246531c3aa02cf89', 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 ('06FPXB28V72K8ZPE5WSNJS1SP8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Data browser UI — implant/browser app, six index+detail screens', 'D-254 SS4 + Jeroen''s IA ruling (2026-07-17): a SEPARATE implant app (implant/browser), sibling to the Atlas — the six registry entities do not share the Atlas''s geographic drill-down. ONE ticket for all six per Jeroen (single review sees the whole surface). Scope: new client/ui/implant/apps/browser/ app.tres + app.gd + index/detail screen pairs for star systems, bodies, stations, corporations, commodities, trait templates — composed ENTIRELY from D-169 implant components (ImplantPanel/ImplantHeader/ImplantDataRow/ImplantTextBlock; no new UI primitives); wired to the six BrowseRequest endpoints; reachable from the standalone shell (and by design also from the in-game implant later — it is a normal implant app). Blocked by the browse-proxy ticket (data source) and the standalone-shell ticket (launch surface).', 'backlog', 'high', NULL, 'client', NULL, '2026-07-17 06:03:32.441', '2026-07-17 06:03:32.441', NULL, 'a9e18307ecab0cc8cb83f9b3c2612553', 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 ('06FPXB41KV1J008A140B9CFD7M', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Spawn-mode startup screen — seed picker now, save picker slot (Phase 5+)', 'D-254 SS5. The pre-Atlas startup screen for spawn-mode: world-seed selection v1; the future save picker slots into this exact screen as a second choice (reusing main.gd''s LoadGame wire action verbatim once saves exist, Phase 5+ / T-888 lineage). Genuinely independent of the browser work; spawn-mode works with the server-default seed without it — UX polish on a working path. Blocked by the standalone-shell ticket.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-17 06:03:46.974', '2026-07-17 06:03:46.974', NULL, 'd1cdfff623d6af935993c00ea39962de', 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 ('06FPXAS26J3BS7ZJVRFT5JDXP8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Reader connection: multi-connection bridge + ConnectionRole gate', 'D-254 SS1/SS2 (record on the atlas-companion-app branch; ref attaches after merge+sync). THE GATING TICKET — everything else in T-1128 needs a server that can hold a second connection without hanging it (today: main.rs single blocking accept at ~line 120; a second client hangs silently forever). Scope: ConnectionRole field on StartupMessage (Player | Reader, serde default Player for back-compat, enum shaped for future TradingReader); TcpListener blocking single-accept -> non-blocking per-tick accept loop; BridgeResource becomes 0-1 Player + 0-N Reader collection (NOT general N-player — D-009 scope stays separate); Reader never enters the character-spawn path and receives NO ObserverSnapshot (structural, not filtered); per-connection response tagging for atlas/starmap/citynames request-response buffers; Player-only whole-server-shutdown-on-disconnect (Reader disconnects must never affect the player session); permitted-message matrix enforcement server-side — violation = log + drop frame, repeated = disconnect the reader; lower per-reader inbound frame cap. Deterministic sim unaffected by reader presence. Oscar''s design, Oscar implements.', 'in_progress', 'high', NULL, 'server', NULL, '2026-07-17 06:02:17.012', '2026-07-17 06:06:10.166', NULL, 'bccfacf7cb03ab591a93a37bf41c1311', 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 ('06FPXAWGRH9QK5QKV7584Q92Z8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Standalone entry scene + attach-or-spawn boot + make atlas target', 'D-254 SS1/SS3/SS5. client/scenes/atlas_standalone.tscn + atlas_standalone.gd (modeled on visual_capture.gd''s boot shape — NOT a flag on main.tscn, which is saturated with player-only paths): attach-mode = TCP connect SR_PORT-or-9876 with ~500ms timeout; ECONNREFUSED falls through to spawn-mode = own child server via the server_process.gd ownership pattern with --port 0 + LISTENING:{port} parse, REAL systems.db (no --test-mode); sends Reader-role StartupMessage; opens implant/map fullscreen (skips main menu/character creation); window title branding via DisplayServer.window_set_title; intercept atlas close-to-gameplay in atlas_standalone.gd (do not modify atlas_app.gd). Makefile: target = trivial build+launch (the scene owns the attach-or-spawn decision — no Makefile orchestration; and per Oscar''s flag this simplicity does NOT shrink the T-1130 server work). Exportable later as a Godot export preset, no architecture change. Blocked by T-1130 for real attach-mode testing (developable against spawn-mode before that). Blocks the browser-UI ticket (needs a shell to launch from).
Correction: the Makefile target name in the line above is ''atlas'' (make atlas) — shell backticks ate the word during filing.', 'in_progress', 'high', NULL, 'client', NULL, '2026-07-17 06:02:45.316', '2026-07-17 06:06:10.173', NULL, '1afe1914805d34f79ede6888f99599ba', 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 ('06FPXAS26J3BS7ZJVRFT5JDXP8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Reader connection: multi-connection bridge + ConnectionRole gate', 'D-254 SS1/SS2 (record on the atlas-companion-app branch; ref attaches after merge+sync). THE GATING TICKET — everything else in T-1128 needs a server that can hold a second connection without hanging it (today: main.rs single blocking accept at ~line 120; a second client hangs silently forever). Scope: ConnectionRole field on StartupMessage (Player | Reader, serde default Player for back-compat, enum shaped for future TradingReader); TcpListener blocking single-accept -> non-blocking per-tick accept loop; BridgeResource becomes 0-1 Player + 0-N Reader collection (NOT general N-player — D-009 scope stays separate); Reader never enters the character-spawn path and receives NO ObserverSnapshot (structural, not filtered); per-connection response tagging for atlas/starmap/citynames request-response buffers; Player-only whole-server-shutdown-on-disconnect (Reader disconnects must never affect the player session); permitted-message matrix enforcement server-side — violation = log + drop frame, repeated = disconnect the reader; lower per-reader inbound frame cap. Deterministic sim unaffected by reader presence. Oscar''s design, Oscar implements.', 'in_progress', 'high', 'oscar', 'server', NULL, '2026-07-17 06:02:17.012', '2026-07-17 06:06:19.074', NULL, 'f1178f106384e173b607f98b0a444d82', 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 ('06FPXAWGRH9QK5QKV7584Q92Z8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Standalone entry scene + attach-or-spawn boot + make atlas target', 'D-254 SS1/SS3/SS5. client/scenes/atlas_standalone.tscn + atlas_standalone.gd (modeled on visual_capture.gd''s boot shape — NOT a flag on main.tscn, which is saturated with player-only paths): attach-mode = TCP connect SR_PORT-or-9876 with ~500ms timeout; ECONNREFUSED falls through to spawn-mode = own child server via the server_process.gd ownership pattern with --port 0 + LISTENING:{port} parse, REAL systems.db (no --test-mode); sends Reader-role StartupMessage; opens implant/map fullscreen (skips main menu/character creation); window title branding via DisplayServer.window_set_title; intercept atlas close-to-gameplay in atlas_standalone.gd (do not modify atlas_app.gd). Makefile: target = trivial build+launch (the scene owns the attach-or-spawn decision — no Makefile orchestration; and per Oscar''s flag this simplicity does NOT shrink the T-1130 server work). Exportable later as a Godot export preset, no architecture change. Blocked by T-1130 for real attach-mode testing (developable against spawn-mode before that). Blocks the browser-UI ticket (needs a shell to launch from).
Correction: the Makefile target name in the line above is ''atlas'' (make atlas) — shell backticks ate the word during filing.', 'in_progress', 'high', 'stig', 'client', NULL, '2026-07-17 06:02:45.316', '2026-07-17 06:06:27.645', NULL, '4fcf8c907a270751258137a0352031b6', 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 ('06FPXAWGRH9QK5QKV7584Q92Z8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Standalone entry scene + attach-or-spawn boot + make atlas target', 'D-254 SS1/SS3/SS5. client/scenes/atlas_standalone.tscn + atlas_standalone.gd (modeled on visual_capture.gd''s boot shape — NOT a flag on main.tscn, which is saturated with player-only paths): attach-mode = TCP connect SR_PORT-or-9876 with ~500ms timeout; ECONNREFUSED falls through to spawn-mode = own child server via the server_process.gd ownership pattern with --port 0 + LISTENING:{port} parse, REAL systems.db (no --test-mode); sends Reader-role StartupMessage; opens implant/map fullscreen (skips main menu/character creation); window title branding via DisplayServer.window_set_title; intercept atlas close-to-gameplay in atlas_standalone.gd (do not modify atlas_app.gd). Makefile: target = trivial build+launch (the scene owns the attach-or-spawn decision — no Makefile orchestration; and per Oscar''s flag this simplicity does NOT shrink the T-1130 server work). Exportable later as a Godot export preset, no architecture change. Blocked by T-1130 for real attach-mode testing (developable against spawn-mode before that). Blocks the browser-UI ticket (needs a shell to launch from).
Correction: the Makefile target name in the line above is ''atlas'' (make atlas) — shell backticks ate the word during filing.
Jeroen''s requirement (2026-07-17, mid-implementation — now D-254 SS3): the standalone shell is a GENERIC IMPLANT HOST, not an Atlas launcher. Every ImplantRegistry app is automatically available in the companion; the manifest (app.tres) gains available_in_companion: bool = true (OPT-OUT — set false only for apps that need a playing character/gameplay context). Shell consults the flag at instantiate_all/app-open time and in any app-switching surface; the in-game implant ignores it. Both current apps (Atlas, Economics) stay true. Jeroen''s suggested name availableInAtlasApp recorded in D-254 with the available_in_companion rename rationale (GDScript snake_case + avoids colliding with apps/atlas).', 'in_progress', 'high', 'stig', 'client', NULL, '2026-07-17 06:02:45.316', '2026-07-17 06:10:24.124', NULL, 'e2e14774394c4da39d8f72bfb6a1a4cc', 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 ('06FPXB28V72K8ZPE5WSNJS1SP8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Data browser UI — implant/browser app, six index+detail screens', 'D-254 SS4 + Jeroen''s IA ruling (2026-07-17): a SEPARATE implant app (implant/browser), sibling to the Atlas — the six registry entities do not share the Atlas''s geographic drill-down. ONE ticket for all six per Jeroen (single review sees the whole surface). Scope: new client/ui/implant/apps/browser/ app.tres + app.gd + index/detail screen pairs for star systems, bodies, stations, corporations, commodities, trait templates — composed ENTIRELY from D-169 implant components (ImplantPanel/ImplantHeader/ImplantDataRow/ImplantTextBlock; no new UI primitives); wired to the six BrowseRequest endpoints; reachable from the standalone shell (and by design also from the in-game implant later — it is a normal implant app). Blocked by the browse-proxy ticket (data source) and the standalone-shell ticket (launch surface).
Note (D-254 SS3 addition, 2026-07-17): the implant/browser app manifest carries available_in_companion = true (the default). Because the companion shell is a generic implant host, this app appears in the standalone Atlas automatically the moment it registers — no companion-side wiring.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-17 06:03:32.441', '2026-07-17 06:10:33.805', NULL, 'eb0ef4abc9fd3af3d0069db6aee6433a', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'D-227 invented detail does not feed district classification — the orbit-to-jet-plane bridge ends at ~500km', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64.
Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010.
Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data.
Jeroen, further (2026-07-17): ''more ocean is more wet and rainy, for instance'' — hydrosphere/ocean coverage as a tier-1 driver chain: large hydrosphere -> wetter, rainier climate -> HIGHER EROSION -> smoother, more mature coastlines (and more dendritic/eroded relief); small hydrosphere / thin atmosphere -> dry -> sharp, young, uneroded coasts. Erosion-proneness therefore need not be a new authored field — it is derivable from hydrosphere + atmosphere + precipitation, compounding with the tectonic/glaciation drivers. Note for implementation: hydrosphere already feeds moisture derivation (T-1034 fixed the vocab fall-through), so part of this chain exists — the new link is wet/rainy feeding COASTLINE/relief character, not just biome wetness.
---
Gap resolution (2026-07-17, Si review + Jeroen''s rulings):
1. TILT/D-240: DROPPED — no axial tilt input; D-240 stays intact. latitude_deg alone carries the position-tier polar effects (glaciation_grade is already latitude/temperature-derived). Tier-1 envelope CONFIRMED as what exists: planet_class + hydrosphere + atmosphere + body_radius_km (BodyParams, body_params_reader.rs:14-20). No tectonic-activity or erosion field exists — tectonic_class derives from planet_class (derive_tectonic_class always falls to its planet_class branch in production); erosion-proneness is DERIVED from hydrosphere + atmosphere + precipitation per Jeroen''s chain. Do not hunt for richer body signals; they are not there.
2. CIRCULARITY: ACCEPT STALE INPUT — glaciation_grade reads the UNWARPED raw-bilinear ocean_fraction_q (one-step-stale, deterministic, coarse ~156km-pixel value anyway). Document on the call site. tectonic_class is cheap to hoist to the top of derive_district (needs only BodyParams).
3. SCOPE: BOTH PATHS — the invention must feed derive_district (on-demand: probe + future T-1124 view) AND the production derive_district_profile (district_profile.rs:1062-1134, the batch path BodyWorldState stores — currently has NO scatter at all; the paths silently diverged). Implementer may unify the shared classification core if cleaner than duplicating — his call, reviewers verify. Believability goldens (server/tests/golden/believability.json) WILL regenerate — expected and acknowledged via UPDATE_GOLDEN=1; report the metric deltas (contrast/coherence blocks) in the PR as evidence of improvement, not just churn.
Si verification facts for the implementer:
- Direct reusable precedents: climate_edge_warp (region_profile.rs:477-513) — warp-the-sampling with a distinct hash path, exactly the pattern for ocean_fraction_q; domain_warp.rs (T-1026, stateless (seed,body,pos)->offset); detail_scatter.rs voxel_mosaic (T-1084) proves slope-INDEPENDENT position-keyed scatter is established. Longitude variation comes free via position-keyed noise on world-metre coords — no explicit longitude plumbing needed.
- Fix shape (from the ticket + rulings): warp the ocean-mask sampling position (invented bays/capes), slope-independent scatter floor whose AMPLITUDE/CHARACTER varies by the two driver tiers (body envelope: planet_class/hydrosphere/atmosphere/radius -> personality incl. erosion-smoothness; position: latitude, glaciation_grade, tectonic_class, climate bands -> fjordy vs smooth vs sharp), plus seeded heterogeneity so same-coast stretches differ. All deterministic seed+body+position (D-227/D-010); continuous across district/region boundaries (D-243 edge-fuzz discipline — character never steps on a line).
- Acceptance: (a) before/after zoom ladders (aliveness_probe --render, same bodies/windows as T-1123: GJ380c/GJ820Bc/GJ244Ad at w256/w64/w16) reviewed by Jeroen — the primary artifact; (b) believability metrics moved not regressed; new coastline-legibility criterion work belongs to open Q-123, not this ticket. (c) determinism byte-compare as in T-1123.
- Performance: no per-district budget record exists; do the D-246-style check — verify derive_district cost stays same order (~8us was the T-1123 measurement) and the believability/derivation harness budgets stay green.', 'review', 'high', 'dudley', 'server', 'D-227', '2026-07-16 20:25:20.427', '2026-07-17 06:25:17.792', NULL, '9447b6c049bb753b47f64c389ed384c7', 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 ('06FPS6TF9XDZPKZY8DHG1T0E2R', 'task', '06FGSJNP8YM537DJCC82ZD6544', 'Frozen bodies read temperate-green on district morphology maps — render glaciation_grade', 'T-1123 zoom-ladder finding: GJ244Ad (frozen planet_class, temp panel pegged at the -60C clamp floor, vegetation solid Barren) still renders temperate-green morphology — the 17-zone MorphologyZone vocabulary (T-1027, FROZEN per its own contract) has no ice/glacial zone, and glaciation_grade is a separate DistrictProfile field nothing renders. Do NOT reopen the frozen vocab by default: the likely fix is rendering glaciation_grade as a modifier/overlay on the morphology color (ice-tinting affected zones) in both the T-1123 probe renderer and the future T-1124 view — or, if the team decides an ice zone belongs in the vocabulary after all, that is a deliberate amendment to the freeze decision, not a drive-by. Images: .cache/screenshots/district-windows gj244ad_w256_morphology.png vs _temp.png.
Refinement (2026-07-17, Si review): READY. Upstream verified HEALTHY — glaciation derivation (derive_glaciation_grade_from_climate, district_profile.rs:339-366) is correctly wired and even gates the Fjord family; GJ244Ad moisture clears the gate floor. This is purely a rendering gap: aliveness_probe render_window_panels computes the full DistrictProfile and discards .glaciation_grade (line ~458 reads only morphology_zone). SCOPE RULING (lead): probe-only THIS ticket — per-pixel ice tint blended on the morphology panel keyed on glaciation_grade (+ optionally a 6th dedicated glaciation panel; PANEL_NAMES/bufs are array-driven, cheap). The wire+client half (glaciation: Vec<u8> parallel array inside DistrictGridLayer + build_district_grid + protocol.gd + _draw_gen_district — governance-UNCONSTRAINED per the D-226 growth-ceiling reading: the cap governs new top-level Option layers, not per-cell fields inside a shipped layer; elev_q precedent) is DEFERRED to T-1124''s design pass, which owns the which-fields-ship-at-district-resolution question. Freeze contract confirmed: D-239 SS6 — tint is classifier-tuning territory, no MorphologyZone vocab reopen; an ice zone would need its own explicit D-amendment (not pursued).', 'review', 'medium', 'dudley', 'server', NULL, '2026-07-16 20:25:45.551', '2026-07-17 06:25:17.795', NULL, '2c8c127a9e8b7d3844311fb551df7cfa', 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 ('06FPS6RRBF9HHAED5QKTKQCY1G', 'bug', '06FGSJNP8YM537DJCC82ZD6544', 'derive_vegetation is ocean-blind — district maps paint Forest across open ocean', 'T-1123 zoom-ladder finding: derive_vegetation(temp, moisture, elev, near_water) has no ocean/water-mask input — GJ380c w256 renders solid Forest across ~250km of OpenOcean districts (see .cache/screenshots/district-windows gj380c_w256_veg.png vs _morphology.png). Voxel-level water generators mask this in-world, but any district-resolution Atlas view (T-1124) will show forested oceans. Fix: vegetation classification should return a None/Marine class when the district''s morphology/ocean_fraction says water (reuse ocean_fraction_q already computed in derive_district).
Gap resolution (2026-07-17, Si review + Jeroen): (1) GATE: morphology-derived — pass the already-computed morphology water verdict (OpenOcean/Lake) into the vegetation call; vegetation and morphology can NEVER disagree by construction; no fourth threshold is born. (2) VARIANT: Marine, appended at discriminant 6 (after RiparianThicket=5); update the load-bearing vegetation_class_discriminants_pinned test; document that Ord position is non-semantic (the density-ladder doc does not extend to Marine). (3) SEQUENCING: blocked by T-1125 (edge added) — classify against the warped/invented coastline, not the smooth one. Si facts: the ticket phrase near_water is a TRAP — derive_vegetation''s 4th param is near_perennial_water (riparian/river proximity), unrelated to ocean; do not reuse it. voxel.rs consumers are wildcard-safe (ocean districts route to the WaterBody generator upstream per D-239/T-1082) — no changes needed there. aliveness_probe.rs vegetation_rgb is an EXHAUSTIVE match and will fail to compile until it gets a Marine color — extend the palette deliberately (ocean blue distinct from morphology water tones). D-239 gets an implementation-addendum note recording the Marine variant (its §8 climate-vegetation law predates a water class).', 'review', 'medium', 'dudley', 'server', NULL, '2026-07-16 20:25:31.484', '2026-07-17 06:25:17.795', NULL, 'bfd6b2dbc9fb45181a41d85fb890c398', 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 ('06FPXHCT46V340D1AJZRKBHJN4', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Split character spawn out of monolithic world-setup — reader-only servers carry an inert PlayerCharacter', 'T-1130 deviation 3 (Oscar, 2026-07-17): setup_proof_room/setup_gauntlet spawn the PlayerCharacter unconditionally, so a server whose first and only connection is a Reader (D-254 spawn-mode companion) still gets an inert, unpiloted PlayerCharacter entity in its ECS world. Harmless today — nothing drives it, no Player exists to send inputs, and the Reader never learns of it since readers receive no ObserverSnapshot — but it is dead state in every companion-spawned world and would confuse future world-inspection tooling. Fix: split character spawn into its own setup path invoked on Player installation (first-connection Player or a future mid-session Player attach), not at world build. Documented at the call site in server/src/main.rs. Also noted in the same report, not ticketed separately: there is no cap on total reader COUNT (per-reader inbound frame cap only, 8/tick) — worst-case per-tick reader cost is O(N x 8); acceptable for loopback-only scope, revisit if the companion ever leaves loopback.', 'backlog', 'low', NULL, 'server', NULL, '2026-07-17 06:31:11.649', '2026-07-17 06:31:11.649', NULL, '4d8235ad2809e0f407c5ac7d23a510e0', 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 ('06FPS6QD5FG9TC2WXTDVA44FPW', 'story', '06FGSJNP8YM537DJCC82ZD6544', 'D-227 invented detail does not feed district classification — the orbit-to-jet-plane bridge ends at ~500km', 'T-1123 zoom-ladder finding (2026-07-16, first-ever look at district-resolution maps; images in .cache/screenshots/district-windows, evidence /tmp/district-windows). At w256 (524km) the district layer reads as coherent geography (coastal band sequences, legible coasts — but bilinear-smooth: rounded corners of ~156km heightmap pixels visible, no invented bays). At w64 (131km) it degenerates to razor-straight gradient stripes; at w16 (33km) a single flat color. MECHANISM (code-grounded by Dudley): derive_district classifies morphology from ocean_fraction_q = RAW BILINEAR of the ocean mask (district_profile.rs:1318 — no domain warp, no scatter), and the D-227 detail-scatter amplitude is slaved to local_slope of the coarse heightmap (1301-1303) — near zero on low-relief coastal plains, so invention collapses exactly where coastlines live. The scatter itself works (visible as cloudy texture in w256 elev panels) but barely feeds classification and never feeds the coastline. NEEDED: the D-227 invention (interpolation + domain warp + detail-scatter) must carry geographic content into district classification — warped coastline sampling, slope-independent scatter floor, invented drainage/relief between heightmap pixels. This is the heart of the Phase-4 deliverable (deterministic walkable-world data that reads as geography) and blocks T-1124''s regional map view being worth looking at below ~500km. Determinism verified byte-identical cross-process — the substrate is sound; it is the content of the invention that is missing. Reproduce: cargo run --bin aliveness_probe -- --body GJ380c --seed 42 --render /tmp/out --window 64.
Jeroen''s design ruling (2026-07-17, pre-refinement): CRINKLE SHOULD VARY. The invented coastline/terrain detail must not be a single global roughness constant or uniform scatter floor — its amplitude and character vary from place to place. Candidate drivers to pin during refinement/implementation (lead''s interpretation, Si to ground in what derive_district already carries): geological context (tectonic_class, glaciation_grade — fjord coasts jagged, deltas smooth, cliff coasts sharp, alluvial plains gentle), climate signals, and seeded spatial heterogeneity so stretches of the same coast differ. The variation itself must be deterministic (seed+position), per D-227/D-010.
Jeroen''s ruling, elaborated (2026-07-17): derive coastline variation from EXISTING body settings + lat/long — do not author new knobs. Some places fjordy, some smooth; some planets have tectonics, some are prone to higher erosion. Two driver tiers: (1) BODY-level — the variation already in body parameters (planet_class, atmosphere, hydrosphere, axial tilt, radius, and whatever tectonic/erosion signals exist or are derivable from them) sets each body''s coastline character envelope; (2) POSITION-level — latitude/longitude and the derived district context (glaciation_grade, tectonic_class, climate bands) modulate within it: high-latitude glaciated coasts go fjordy, humid climates erode smooth, arid/tectonic coasts stay sharp. The point: two planets should have recognisably different coastline personalities, and one planet''s coasts should vary by where you are — all deterministic from seed+body+position, zero new authored data.
Jeroen, further (2026-07-17): ''more ocean is more wet and rainy, for instance'' — hydrosphere/ocean coverage as a tier-1 driver chain: large hydrosphere -> wetter, rainier climate -> HIGHER EROSION -> smoother, more mature coastlines (and more dendritic/eroded relief); small hydrosphere / thin atmosphere -> dry -> sharp, young, uneroded coasts. Erosion-proneness therefore need not be a new authored field — it is derivable from hydrosphere + atmosphere + precipitation, compounding with the tectonic/glaciation drivers. Note for implementation: hydrosphere already feeds moisture derivation (T-1034 fixed the vocab fall-through), so part of this chain exists — the new link is wet/rainy feeding COASTLINE/relief character, not just biome wetness.
---
Gap resolution (2026-07-17, Si review + Jeroen''s rulings):
1. TILT/D-240: DROPPED — no axial tilt input; D-240 stays intact. latitude_deg alone carries the position-tier polar effects (glaciation_grade is already latitude/temperature-derived). Tier-1 envelope CONFIRMED as what exists: planet_class + hydrosphere + atmosphere + body_radius_km (BodyParams, body_params_reader.rs:14-20). No tectonic-activity or erosion field exists — tectonic_class derives from planet_class (derive_tectonic_class always falls to its planet_class branch in production); erosion-proneness is DERIVED from hydrosphere + atmosphere + precipitation per Jeroen''s chain. Do not hunt for richer body signals; they are not there.
2. CIRCULARITY: ACCEPT STALE INPUT — glaciation_grade reads the UNWARPED raw-bilinear ocean_fraction_q (one-step-stale, deterministic, coarse ~156km-pixel value anyway). Document on the call site. tectonic_class is cheap to hoist to the top of derive_district (needs only BodyParams).
3. SCOPE: BOTH PATHS — the invention must feed derive_district (on-demand: probe + future T-1124 view) AND the production derive_district_profile (district_profile.rs:1062-1134, the batch path BodyWorldState stores — currently has NO scatter at all; the paths silently diverged). Implementer may unify the shared classification core if cleaner than duplicating — his call, reviewers verify. Believability goldens (server/tests/golden/believability.json) WILL regenerate — expected and acknowledged via UPDATE_GOLDEN=1; report the metric deltas (contrast/coherence blocks) in the PR as evidence of improvement, not just churn.
Si verification facts for the implementer:
- Direct reusable precedents: climate_edge_warp (region_profile.rs:477-513) — warp-the-sampling with a distinct hash path, exactly the pattern for ocean_fraction_q; domain_warp.rs (T-1026, stateless (seed,body,pos)->offset); detail_scatter.rs voxel_mosaic (T-1084) proves slope-INDEPENDENT position-keyed scatter is established. Longitude variation comes free via position-keyed noise on world-metre coords — no explicit longitude plumbing needed.
- Fix shape (from the ticket + rulings): warp the ocean-mask sampling position (invented bays/capes), slope-independent scatter floor whose AMPLITUDE/CHARACTER varies by the two driver tiers (body envelope: planet_class/hydrosphere/atmosphere/radius -> personality incl. erosion-smoothness; position: latitude, glaciation_grade, tectonic_class, climate bands -> fjordy vs smooth vs sharp), plus seeded heterogeneity so same-coast stretches differ. All deterministic seed+body+position (D-227/D-010); continuous across district/region boundaries (D-243 edge-fuzz discipline — character never steps on a line).
- Acceptance: (a) before/after zoom ladders (aliveness_probe --render, same bodies/windows as T-1123: GJ380c/GJ820Bc/GJ244Ad at w256/w64/w16) reviewed by Jeroen — the primary artifact; (b) believability metrics moved not regressed; new coastline-legibility criterion work belongs to open Q-123, not this ticket. (c) determinism byte-compare as in T-1123.
- Performance: no per-district budget record exists; do the D-246-style check — verify derive_district cost stays same order (~8us was the T-1123 measurement) and the believability/derivation harness budgets stay green.', 'done', 'high', 'dudley', 'server', 'D-227', '2026-07-16 20:25:20.427', '2026-07-17 06:36:05.017', NULL, 'a1144b4d8d0e8b72bd6b511b7c5b77c6', 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 ('06FPS6RRBF9HHAED5QKTKQCY1G', 'bug', '06FGSJNP8YM537DJCC82ZD6544', 'derive_vegetation is ocean-blind — district maps paint Forest across open ocean', 'T-1123 zoom-ladder finding: derive_vegetation(temp, moisture, elev, near_water) has no ocean/water-mask input — GJ380c w256 renders solid Forest across ~250km of OpenOcean districts (see .cache/screenshots/district-windows gj380c_w256_veg.png vs _morphology.png). Voxel-level water generators mask this in-world, but any district-resolution Atlas view (T-1124) will show forested oceans. Fix: vegetation classification should return a None/Marine class when the district''s morphology/ocean_fraction says water (reuse ocean_fraction_q already computed in derive_district).
Gap resolution (2026-07-17, Si review + Jeroen): (1) GATE: morphology-derived — pass the already-computed morphology water verdict (OpenOcean/Lake) into the vegetation call; vegetation and morphology can NEVER disagree by construction; no fourth threshold is born. (2) VARIANT: Marine, appended at discriminant 6 (after RiparianThicket=5); update the load-bearing vegetation_class_discriminants_pinned test; document that Ord position is non-semantic (the density-ladder doc does not extend to Marine). (3) SEQUENCING: blocked by T-1125 (edge added) — classify against the warped/invented coastline, not the smooth one. Si facts: the ticket phrase near_water is a TRAP — derive_vegetation''s 4th param is near_perennial_water (riparian/river proximity), unrelated to ocean; do not reuse it. voxel.rs consumers are wildcard-safe (ocean districts route to the WaterBody generator upstream per D-239/T-1082) — no changes needed there. aliveness_probe.rs vegetation_rgb is an EXHAUSTIVE match and will fail to compile until it gets a Marine color — extend the palette deliberately (ocean blue distinct from morphology water tones). D-239 gets an implementation-addendum note recording the Marine variant (its §8 climate-vegetation law predates a water class).', 'done', 'medium', 'dudley', 'server', NULL, '2026-07-16 20:25:31.484', '2026-07-17 06:36:05.021', NULL, '2f36265ad3f0a37480f6b9a3ff12e5ef', 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 ('06FPS6TF9XDZPKZY8DHG1T0E2R', 'task', '06FGSJNP8YM537DJCC82ZD6544', 'Frozen bodies read temperate-green on district morphology maps — render glaciation_grade', 'T-1123 zoom-ladder finding: GJ244Ad (frozen planet_class, temp panel pegged at the -60C clamp floor, vegetation solid Barren) still renders temperate-green morphology — the 17-zone MorphologyZone vocabulary (T-1027, FROZEN per its own contract) has no ice/glacial zone, and glaciation_grade is a separate DistrictProfile field nothing renders. Do NOT reopen the frozen vocab by default: the likely fix is rendering glaciation_grade as a modifier/overlay on the morphology color (ice-tinting affected zones) in both the T-1123 probe renderer and the future T-1124 view — or, if the team decides an ice zone belongs in the vocabulary after all, that is a deliberate amendment to the freeze decision, not a drive-by. Images: .cache/screenshots/district-windows gj244ad_w256_morphology.png vs _temp.png.
Refinement (2026-07-17, Si review): READY. Upstream verified HEALTHY — glaciation derivation (derive_glaciation_grade_from_climate, district_profile.rs:339-366) is correctly wired and even gates the Fjord family; GJ244Ad moisture clears the gate floor. This is purely a rendering gap: aliveness_probe render_window_panels computes the full DistrictProfile and discards .glaciation_grade (line ~458 reads only morphology_zone). SCOPE RULING (lead): probe-only THIS ticket — per-pixel ice tint blended on the morphology panel keyed on glaciation_grade (+ optionally a 6th dedicated glaciation panel; PANEL_NAMES/bufs are array-driven, cheap). The wire+client half (glaciation: Vec<u8> parallel array inside DistrictGridLayer + build_district_grid + protocol.gd + _draw_gen_district — governance-UNCONSTRAINED per the D-226 growth-ceiling reading: the cap governs new top-level Option layers, not per-cell fields inside a shipped layer; elev_q precedent) is DEFERRED to T-1124''s design pass, which owns the which-fields-ship-at-district-resolution question. Freeze contract confirmed: D-239 SS6 — tint is classifier-tuning territory, no MorphologyZone vocab reopen; an ice zone would need its own explicit D-amendment (not pursued).', 'done', 'medium', 'dudley', 'server', NULL, '2026-07-16 20:25:45.551', '2026-07-17 06:36:05.022', NULL, 'f4a9417688f87f17eef4d66b8a8bb266', 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 ('06FPXTD20GS2CSC10RCC53Q6V4', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Spawned-server orphan watchdog — survive companion SIGTERM (parent-death signal or equivalent)', 'T-1132 known gap (Stig, live-verified): a raw SIGTERM to the companion process bypasses Godot''s entire notification system (_notification never fires, _exit_tree never runs — confirmed by isolated repro), so a spawn-mode companion killed that way orphans its owned server process. All WM close paths (X button, Alt+F4, wmctrl -c) are clean and verified. No GDScript fix exists — the mitigation is server-side: when spawned as a companion child (e.g. a --parent-watch flag or detecting the pipe), the server should exit when its parent dies — prctl(PR_SET_PDEATHSIG, SIGTERM) on Linux at minimum, or a portable stdin-EOF watchdog (the companion already holds the child''s stdio pipe via start_with_pipe, so stdin closing on parent death is a reliable cross-platform death signal). Keep it opt-in per spawn args so make game and test-harness spawns are unaffected.', 'backlog', 'low', NULL, 'server', NULL, '2026-07-17 07:10:32.964', '2026-07-17 07:10:32.964', NULL, 'ab6964ffc45341d3f5c0014f1bc0d01a', 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 ('06FPX7HZY1CK3VSTRXP1NP9VYW', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Design pass: D-254 companion-app architecture — connection model, reader protocol, app shell, data browser, save/load seam', 'Produce and record D-254 (id claimed). Participants: Tyre (architecture lead, writes the record) + Oscar (protocol/connection model — his activation). Must pin: (1) CONNECTION MODEL — attach to the running game server vs spawn-own (--test-mode? which world?) incl. discovery (port/SR_ADDR), and the multi-connection reality of the bridge today (recon: almost certainly single-connection; a second reader connection is new protocol work); (2) READER CONNECTION CLASS — handshake variant that never spawns a character and cannot send inputs (read-only enforced SERVER-side, not client courtesy — future market trading would widen this deliberately per-verb, design the enum now); (3) APP SHELL — how make atlas launches the existing implant Atlas UI standalone (second main scene vs feature flag vs export preset; keep the implant aesthetic — the diegetic second-monitor pattern is a product goal); (4) DATA BROWSER — Jeroen wants to scan ALL database data: define the browse surface (entity list/detail screens from implant components: systems, bodies, corps, settlements, markets) and its data path (existing wire requests vs a generic read API vs local systems.db read for static data + wire for dynamic — mind the D-010 information-boundary rules for what a reader may see); (5) SAVE/LOAD SEAM — saves are Phase 5+; design the interaction now (attach = inherits the running world; spawn = pick seed today, pick save later), record the hook; (6) FUTURE TRADING note — what changes when the app gains write verbs (auth? per-verb allowlist on the reader class?). Deliverable: D-254 record written in governance/decisions/architecture.md + a proposed implementation ticket tree under T-1128. Wiring is NOT this ticket.', 'review', 'high', NULL, 'server', NULL, '2026-07-17 05:48:12.656', '2026-07-17 07:16:47.102', NULL, '7fe0942f3717617e7cedc71441c6c1f9', 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 ('06FPXAWGRH9QK5QKV7584Q92Z8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Standalone entry scene + attach-or-spawn boot + make atlas target', 'D-254 SS1/SS3/SS5. client/scenes/atlas_standalone.tscn + atlas_standalone.gd (modeled on visual_capture.gd''s boot shape — NOT a flag on main.tscn, which is saturated with player-only paths): attach-mode = TCP connect SR_PORT-or-9876 with ~500ms timeout; ECONNREFUSED falls through to spawn-mode = own child server via the server_process.gd ownership pattern with --port 0 + LISTENING:{port} parse, REAL systems.db (no --test-mode); sends Reader-role StartupMessage; opens implant/map fullscreen (skips main menu/character creation); window title branding via DisplayServer.window_set_title; intercept atlas close-to-gameplay in atlas_standalone.gd (do not modify atlas_app.gd). Makefile: target = trivial build+launch (the scene owns the attach-or-spawn decision — no Makefile orchestration; and per Oscar''s flag this simplicity does NOT shrink the T-1130 server work). Exportable later as a Godot export preset, no architecture change. Blocked by T-1130 for real attach-mode testing (developable against spawn-mode before that). Blocks the browser-UI ticket (needs a shell to launch from).
Correction: the Makefile target name in the line above is ''atlas'' (make atlas) — shell backticks ate the word during filing.
Jeroen''s requirement (2026-07-17, mid-implementation — now D-254 SS3): the standalone shell is a GENERIC IMPLANT HOST, not an Atlas launcher. Every ImplantRegistry app is automatically available in the companion; the manifest (app.tres) gains available_in_companion: bool = true (OPT-OUT — set false only for apps that need a playing character/gameplay context). Shell consults the flag at instantiate_all/app-open time and in any app-switching surface; the in-game implant ignores it. Both current apps (Atlas, Economics) stay true. Jeroen''s suggested name availableInAtlasApp recorded in D-254 with the available_in_companion rename rationale (GDScript snake_case + avoids colliding with apps/atlas).', 'review', 'high', 'stig', 'client', NULL, '2026-07-17 06:02:45.316', '2026-07-17 07:16:47.106', NULL, '775c513c295216c2f2892585d3183516', 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 ('06FPXAS26J3BS7ZJVRFT5JDXP8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Reader connection: multi-connection bridge + ConnectionRole gate', 'D-254 SS1/SS2 (record on the atlas-companion-app branch; ref attaches after merge+sync). THE GATING TICKET — everything else in T-1128 needs a server that can hold a second connection without hanging it (today: main.rs single blocking accept at ~line 120; a second client hangs silently forever). Scope: ConnectionRole field on StartupMessage (Player | Reader, serde default Player for back-compat, enum shaped for future TradingReader); TcpListener blocking single-accept -> non-blocking per-tick accept loop; BridgeResource becomes 0-1 Player + 0-N Reader collection (NOT general N-player — D-009 scope stays separate); Reader never enters the character-spawn path and receives NO ObserverSnapshot (structural, not filtered); per-connection response tagging for atlas/starmap/citynames request-response buffers; Player-only whole-server-shutdown-on-disconnect (Reader disconnects must never affect the player session); permitted-message matrix enforcement server-side — violation = log + drop frame, repeated = disconnect the reader; lower per-reader inbound frame cap. Deterministic sim unaffected by reader presence. Oscar''s design, Oscar implements.', 'review', 'high', 'oscar', 'server', NULL, '2026-07-17 06:02:17.012', '2026-07-17 07:16:47.106', NULL, 'ee8ec1772f97e0bbbbf877ad0773945f', 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 ('06FPX7HZY1CK3VSTRXP1NP9VYW', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Design pass: D-254 companion-app architecture — connection model, reader protocol, app shell, data browser, save/load seam', 'Produce and record D-254 (id claimed). Participants: Tyre (architecture lead, writes the record) + Oscar (protocol/connection model — his activation). Must pin: (1) CONNECTION MODEL — attach to the running game server vs spawn-own (--test-mode? which world?) incl. discovery (port/SR_ADDR), and the multi-connection reality of the bridge today (recon: almost certainly single-connection; a second reader connection is new protocol work); (2) READER CONNECTION CLASS — handshake variant that never spawns a character and cannot send inputs (read-only enforced SERVER-side, not client courtesy — future market trading would widen this deliberately per-verb, design the enum now); (3) APP SHELL — how make atlas launches the existing implant Atlas UI standalone (second main scene vs feature flag vs export preset; keep the implant aesthetic — the diegetic second-monitor pattern is a product goal); (4) DATA BROWSER — Jeroen wants to scan ALL database data: define the browse surface (entity list/detail screens from implant components: systems, bodies, corps, settlements, markets) and its data path (existing wire requests vs a generic read API vs local systems.db read for static data + wire for dynamic — mind the D-010 information-boundary rules for what a reader may see); (5) SAVE/LOAD SEAM — saves are Phase 5+; design the interaction now (attach = inherits the running world; spawn = pick seed today, pick save later), record the hook; (6) FUTURE TRADING note — what changes when the app gains write verbs (auth? per-verb allowlist on the reader class?). Deliverable: D-254 record written in governance/decisions/architecture.md + a proposed implementation ticket tree under T-1128. Wiring is NOT this ticket.', 'done', 'high', NULL, 'server', NULL, '2026-07-17 05:48:12.656', '2026-07-17 07:33:14.221', NULL, '4dc5624d213d5435919f86a698657681', 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 ('06FPXAS26J3BS7ZJVRFT5JDXP8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Reader connection: multi-connection bridge + ConnectionRole gate', 'D-254 SS1/SS2 (record on the atlas-companion-app branch; ref attaches after merge+sync). THE GATING TICKET — everything else in T-1128 needs a server that can hold a second connection without hanging it (today: main.rs single blocking accept at ~line 120; a second client hangs silently forever). Scope: ConnectionRole field on StartupMessage (Player | Reader, serde default Player for back-compat, enum shaped for future TradingReader); TcpListener blocking single-accept -> non-blocking per-tick accept loop; BridgeResource becomes 0-1 Player + 0-N Reader collection (NOT general N-player — D-009 scope stays separate); Reader never enters the character-spawn path and receives NO ObserverSnapshot (structural, not filtered); per-connection response tagging for atlas/starmap/citynames request-response buffers; Player-only whole-server-shutdown-on-disconnect (Reader disconnects must never affect the player session); permitted-message matrix enforcement server-side — violation = log + drop frame, repeated = disconnect the reader; lower per-reader inbound frame cap. Deterministic sim unaffected by reader presence. Oscar''s design, Oscar implements.', 'done', 'high', 'oscar', 'server', NULL, '2026-07-17 06:02:17.012', '2026-07-17 07:33:14.223', NULL, '9b4572b19e21a76292d33bad4ac9310b', 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 ('06FPXAWGRH9QK5QKV7584Q92Z8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Standalone entry scene + attach-or-spawn boot + make atlas target', 'D-254 SS1/SS3/SS5. client/scenes/atlas_standalone.tscn + atlas_standalone.gd (modeled on visual_capture.gd''s boot shape — NOT a flag on main.tscn, which is saturated with player-only paths): attach-mode = TCP connect SR_PORT-or-9876 with ~500ms timeout; ECONNREFUSED falls through to spawn-mode = own child server via the server_process.gd ownership pattern with --port 0 + LISTENING:{port} parse, REAL systems.db (no --test-mode); sends Reader-role StartupMessage; opens implant/map fullscreen (skips main menu/character creation); window title branding via DisplayServer.window_set_title; intercept atlas close-to-gameplay in atlas_standalone.gd (do not modify atlas_app.gd). Makefile: target = trivial build+launch (the scene owns the attach-or-spawn decision — no Makefile orchestration; and per Oscar''s flag this simplicity does NOT shrink the T-1130 server work). Exportable later as a Godot export preset, no architecture change. Blocked by T-1130 for real attach-mode testing (developable against spawn-mode before that). Blocks the browser-UI ticket (needs a shell to launch from).
Correction: the Makefile target name in the line above is ''atlas'' (make atlas) — shell backticks ate the word during filing.
Jeroen''s requirement (2026-07-17, mid-implementation — now D-254 SS3): the standalone shell is a GENERIC IMPLANT HOST, not an Atlas launcher. Every ImplantRegistry app is automatically available in the companion; the manifest (app.tres) gains available_in_companion: bool = true (OPT-OUT — set false only for apps that need a playing character/gameplay context). Shell consults the flag at instantiate_all/app-open time and in any app-switching surface; the in-game implant ignores it. Both current apps (Atlas, Economics) stay true. Jeroen''s suggested name availableInAtlasApp recorded in D-254 with the available_in_companion rename rationale (GDScript snake_case + avoids colliding with apps/atlas).', 'done', 'high', 'stig', 'client', NULL, '2026-07-17 06:02:45.316', '2026-07-17 07:33:14.224', NULL, 'afe855927790e15891e9ac9492f54642', 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 ('06FPXATGN8QJAJVR0WCRHJS2TG', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Browse data proxy — six v1 entity read endpoints over the wire', 'D-254 SS4. BrowseRequest/Response per-entity-kind types (star systems, bodies, stations, corporations, commodities, trait templates) in server/src/atlas/, sibling to atlas_data_proxy.rs; rusqlite reads against systems.db via the CityContextReader::open() pattern (five-ish new read functions on a proven pattern); wired into the same inbound demux the reader connection uses. Wire-only by D-254 ruling (extends the T-949 precedent: the client never reads game data files directly; no SQLite GDExtension). Excluded from v1: cascade-derived atlas geometry tables (partially populated mid-Phase-4) and event-log tables (wrong UI shape) — exclusion list recorded in D-254. Blocked by the reader-connection ticket (needs the connection class to gate against).', 'in_progress', 'high', NULL, 'server', NULL, '2026-07-17 06:02:28.906', '2026-07-17 07:34:22.189', NULL, '9b6e8e45af2980978c872d3738f01850', 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 ('06FPXB28V72K8ZPE5WSNJS1SP8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Data browser UI — implant/browser app, six index+detail screens', 'D-254 SS4 + Jeroen''s IA ruling (2026-07-17): a SEPARATE implant app (implant/browser), sibling to the Atlas — the six registry entities do not share the Atlas''s geographic drill-down. ONE ticket for all six per Jeroen (single review sees the whole surface). Scope: new client/ui/implant/apps/browser/ app.tres + app.gd + index/detail screen pairs for star systems, bodies, stations, corporations, commodities, trait templates — composed ENTIRELY from D-169 implant components (ImplantPanel/ImplantHeader/ImplantDataRow/ImplantTextBlock; no new UI primitives); wired to the six BrowseRequest endpoints; reachable from the standalone shell (and by design also from the in-game implant later — it is a normal implant app). Blocked by the browse-proxy ticket (data source) and the standalone-shell ticket (launch surface).
Note (D-254 SS3 addition, 2026-07-17): the implant/browser app manifest carries available_in_companion = true (the default). Because the companion shell is a generic implant host, this app appears in the standalone Atlas automatically the moment it registers — no companion-side wiring.', 'in_progress', 'high', NULL, 'client', NULL, '2026-07-17 06:03:32.441', '2026-07-17 07:34:22.191', NULL, 'c48ec6ba702715e60cca4f721b2d43a3', 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 ('06FPXATGN8QJAJVR0WCRHJS2TG', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Browse data proxy — six v1 entity read endpoints over the wire', 'D-254 SS4. BrowseRequest/Response per-entity-kind types (star systems, bodies, stations, corporations, commodities, trait templates) in server/src/atlas/, sibling to atlas_data_proxy.rs; rusqlite reads against systems.db via the CityContextReader::open() pattern (five-ish new read functions on a proven pattern); wired into the same inbound demux the reader connection uses. Wire-only by D-254 ruling (extends the T-949 precedent: the client never reads game data files directly; no SQLite GDExtension). Excluded from v1: cascade-derived atlas geometry tables (partially populated mid-Phase-4) and event-log tables (wrong UI shape) — exclusion list recorded in D-254. Blocked by the reader-connection ticket (needs the connection class to gate against).', 'review', 'high', NULL, 'server', NULL, '2026-07-17 06:02:28.906', '2026-07-17 08:44:33.132', NULL, 'e83c5a0d336f02877e614c196da847c4', 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 ('06FPXB28V72K8ZPE5WSNJS1SP8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Data browser UI — implant/browser app, six index+detail screens', 'D-254 SS4 + Jeroen''s IA ruling (2026-07-17): a SEPARATE implant app (implant/browser), sibling to the Atlas — the six registry entities do not share the Atlas''s geographic drill-down. ONE ticket for all six per Jeroen (single review sees the whole surface). Scope: new client/ui/implant/apps/browser/ app.tres + app.gd + index/detail screen pairs for star systems, bodies, stations, corporations, commodities, trait templates — composed ENTIRELY from D-169 implant components (ImplantPanel/ImplantHeader/ImplantDataRow/ImplantTextBlock; no new UI primitives); wired to the six BrowseRequest endpoints; reachable from the standalone shell (and by design also from the in-game implant later — it is a normal implant app). Blocked by the browse-proxy ticket (data source) and the standalone-shell ticket (launch surface).
Note (D-254 SS3 addition, 2026-07-17): the implant/browser app manifest carries available_in_companion = true (the default). Because the companion shell is a generic implant host, this app appears in the standalone Atlas automatically the moment it registers — no companion-side wiring.', 'review', 'high', NULL, 'client', NULL, '2026-07-17 06:03:32.441', '2026-07-17 08:44:33.135', NULL, 'e6f25d5beb2dcb3a68eba510cf0e3c22', 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 ('06FPXATGN8QJAJVR0WCRHJS2TG', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Browse data proxy — six v1 entity read endpoints over the wire', 'D-254 SS4. BrowseRequest/Response per-entity-kind types (star systems, bodies, stations, corporations, commodities, trait templates) in server/src/atlas/, sibling to atlas_data_proxy.rs; rusqlite reads against systems.db via the CityContextReader::open() pattern (five-ish new read functions on a proven pattern); wired into the same inbound demux the reader connection uses. Wire-only by D-254 ruling (extends the T-949 precedent: the client never reads game data files directly; no SQLite GDExtension). Excluded from v1: cascade-derived atlas geometry tables (partially populated mid-Phase-4) and event-log tables (wrong UI shape) — exclusion list recorded in D-254. Blocked by the reader-connection ticket (needs the connection class to gate against).', 'done', 'high', NULL, 'server', NULL, '2026-07-17 06:02:28.906', '2026-07-17 08:57:02.424', NULL, '1e55e54b81178fc09329b0f255e9b978', 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 ('06FPXB28V72K8ZPE5WSNJS1SP8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Data browser UI — implant/browser app, six index+detail screens', 'D-254 SS4 + Jeroen''s IA ruling (2026-07-17): a SEPARATE implant app (implant/browser), sibling to the Atlas — the six registry entities do not share the Atlas''s geographic drill-down. ONE ticket for all six per Jeroen (single review sees the whole surface). Scope: new client/ui/implant/apps/browser/ app.tres + app.gd + index/detail screen pairs for star systems, bodies, stations, corporations, commodities, trait templates — composed ENTIRELY from D-169 implant components (ImplantPanel/ImplantHeader/ImplantDataRow/ImplantTextBlock; no new UI primitives); wired to the six BrowseRequest endpoints; reachable from the standalone shell (and by design also from the in-game implant later — it is a normal implant app). Blocked by the browse-proxy ticket (data source) and the standalone-shell ticket (launch surface).
Note (D-254 SS3 addition, 2026-07-17): the implant/browser app manifest carries available_in_companion = true (the default). Because the companion shell is a generic implant host, this app appears in the standalone Atlas automatically the moment it registers — no companion-side wiring.', 'done', 'high', NULL, 'client', NULL, '2026-07-17 06:03:32.441', '2026-07-17 08:57:02.427', NULL, 'fd972bbfcc3dca5fb2bfa18e529b875c', 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 ('06FPRZZ0P42NHC1QR5S3737EV4', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Design: Atlas regional map view — windowed district-resolution biome/climate screen (D-226)', 'Jeroen (2026-07-16): the Atlas needs the jet-plane altitude — a windowed district-resolution (2km-cell) map screen bridging the planetary canvas and the (never-mapped, D-226(d)) chunk/voxel world. Today the viewer is one whole-body texture with client-side pan/zoom; AtlasLayerRequest is {body_id, up_to} whole-body only — no rect/window/LOD machinery exists anywhere (recon-verified). Design pass (Tyre data shape + Araminta encoding, the T-1112 pattern): (1) windowed request protocol — rect/center+radius params on a new or extended layer request, served by calling derive_district per cell (district_profile.rs:1256, pure/on-demand — NOT the coarse eager grid); (2) which fields ship at district resolution (morphology, elev_q, temperature, moisture, vegetation — wire-integer discipline per D-010); (3) the screen: entry from a planetary-map click/settlement, second canvas vs zoom-threshold LOD swap, overlay/legend reuse; (4) budget: window size caps, caching, re-request-on-pan. Record as a D-226 amendment. District resolution is ABOVE the D-226(d) quarter-skeleton floor — in remit. Wiring is a follow-up ticket once the design is recorded. Natural batch-mates: T-1118 (climate overlay) + T-1119 (quarter glyphs). T-1123 (server-side window renderer) is the data-side proof and shares the per-cell derive pattern.', 'in_progress', 'high', NULL, 'client', 'D-226', '2026-07-16 19:55:47.761', '2026-07-18 09:44:19.598', NULL, '0526f91ba4e22e898f556e04385e4132', 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 ('06FPQXFFHXEEDQEM7PGXAH3534', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas climate overlay — render region_grid (temp/season/weather/moisture) in the viewer', 'Follow-up to T-1113 (merged PR #179): the server now serves RegionGridLayer (dense row-major; season/weather repr(u8) discriminants, mean_temp deci-degC i16 with i16::MIN airless sentinel, moisture_q u8) through AtlasLayerResponse.region_grid, and protocol.gd passes it through. Build the client overlay per the D-226 incremental-overlay pattern: OVERLAY_DEFS gen_* id (atlas_viewer.gd), _draw_gen_* renderer (atlas_marker_overlay.gd — a region-grid fill analogous to _draw_gen_district but at region granularity), GENERATION_LEGEND entry (atlas_legend_panel.gd). Pick ONE visual channel (mean temp ramp is the obvious first; season/weather as tooltip or variant overlays). Also add a populated region_grid wire fixture (all three committed fixtures carry nil — noted in PR #179).', 'in_progress', 'medium', NULL, 'client', 'D-226', '2026-07-16 17:25:07.599', '2026-07-18 09:44:19.607', NULL, 'f785216f0a2634f1106ef2784d698f57', 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 ('06FPQXHDSMSVD3VADV38NG9STW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire QuarterFootprintLayer per the D-226 T-1112 amendment (six touch points)', 'Implements the design recorded in the D-226 amendment (2026-07-16, T-1112, merged PR #179). Six touch points, each named in the amendment SS4: (1) ZoningType gains PartialOrd/Ord derives mirroring DistrictType''s T-994 precedent (enables the dominant_zoning declaration-order tie-break); (2) AtlasLayerResponse.quarter_footprints field + build_quarter_footprint_layer() mirroring build_district_grid, keyed off non-empty state.quarters, aggregates keyed by city_id (join via the same deterministic QuarterId derivation the dispatch uses); (3) protocol.gd passthrough; (4) OVERLAY_DEFS gen_* id (atlas_viewer.gd); (5) _draw_gen_* scaled-glyph renderer anchored on the L3 settlement dot — shape=dominant district type, color=density ramp on the settlement-gold family, zoom-gated at SETTLEMENT_LABEL_MIN_ZOOM (atlas_marker_overlay.gd); (6) GENERATION_LEGEND entry (atlas_legend_panel.gd). Five aggregate fields: density_avg_pct u8, dominant_district_type, dominant_zoning, landmark_count u8, corridor_count u8; landmark/corridor tooltip-only per the D-226(d) ceiling.', 'in_progress', 'medium', NULL, 'server', 'D-226', '2026-07-16 17:25:23.533', '2026-07-18 09:44:19.608', NULL, 'e1c2c7c462080ef235225f5fbc7c0464', 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 ('06FPRZZ0P42NHC1QR5S3737EV4', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Design: Atlas regional map view — windowed district-resolution biome/climate screen (D-226)', 'Jeroen (2026-07-16): the Atlas needs the jet-plane altitude — a windowed district-resolution (2km-cell) map screen bridging the planetary canvas and the (never-mapped, D-226(d)) chunk/voxel world. Today the viewer is one whole-body texture with client-side pan/zoom; AtlasLayerRequest is {body_id, up_to} whole-body only — no rect/window/LOD machinery exists anywhere (recon-verified). Design pass (Tyre data shape + Araminta encoding, the T-1112 pattern): (1) windowed request protocol — rect/center+radius params on a new or extended layer request, served by calling derive_district per cell (district_profile.rs:1256, pure/on-demand — NOT the coarse eager grid); (2) which fields ship at district resolution (morphology, elev_q, temperature, moisture, vegetation — wire-integer discipline per D-010); (3) the screen: entry from a planetary-map click/settlement, second canvas vs zoom-threshold LOD swap, overlay/legend reuse; (4) budget: window size caps, caching, re-request-on-pan. Record as a D-226 amendment. District resolution is ABOVE the D-226(d) quarter-skeleton floor — in remit. Wiring is a follow-up ticket once the design is recorded. Natural batch-mates: T-1118 (climate overlay) + T-1119 (quarter glyphs). T-1123 (server-side window renderer) is the data-side proof and shares the per-cell derive pattern.
---
Refinement (2026-07-18, Si review + lead rulings — binding for the design pass):
CORRECTED ANCHORS (post PR #182 rewrite): derive_district is now district_profile.rs:1415 (signature gained climate: &ClimateConstants; delegates to the shared invent_primitives — still pure/on-demand, still NOT the coarse eager grid). AtlasLayerRequest is server/src/atlas/layer_proxy.rs:39 (not bridge/mod.rs); its up_to field is currently unwired (cascade always runs to Region). AtlasLayerResponse growth-ceiling doc at layer_proxy.rs:76-79. Five-shape demux cap at bridge/mod.rs:60-76.
RULING 1 — request side (closed, not open): the windowed request MUST be optional window fields ON AtlasLayerRequest (serde-default; absent = whole-body, wire back-compat) — a new top-level request shape is FORBIDDEN by the five-shape demux ceiling (PR #184 Tyre ruling: the sixth shape triggers the D-225 tagged-envelope migration; a window param does not justify paying that now). The ticket phrase ''a new or extended layer request'' is superseded: extended only.
RULING 2 — response side (mandatory explicit resolution IN the amendment): the AtlasLayerResponse one-Option-field-per-layer pattern tops out around six per its own doc; region_grid is the fifth and quarter_footprints (T-1119, THIS batch) consumes the sixth. The windowed district payload is therefore the SEVENTH — the design must explicitly choose and record its carrier: (a) a seventh Option field busting the soft response ceiling with rationale + a re-pinned ceiling, (b) a response-side tagged-envelope/second-response-shape migration, or (c) another mechanism (e.g. the window response as its own payload kind inside the existing atlas request/response buffers). Cite the T-1127 ruling (the ceiling governs NEW TOP-LEVEL Option layers, not per-cell fields inside a shipped layer) and keep the request-cap and response-budget questions explicitly distinct — they are different ceilings with different hardness.
FIELD LIST — two mandatory dispositions: (1) glaciation_grade — T-1127 explicitly deferred its wire+client half to this design pass (Vec of u8, elev_q precedent, probe ice-tint as the render pattern); accept or reject EXPLICITLY, lean accept. (2) vegetation now includes Marine at discriminant 6 (T-1126) — any palette/legend must be exhaustive (the probe palette match is a compile-time guard; the GDScript side needs the discipline manually).
Reusable precedents verified: aliveness_probe --render (T-1123) is the working per-cell derive_district-in-a-window loop + 17-entry MORPHOLOGY_RGB palette; RegionGridLayer (layer_proxy.rs:154-165) is the dense-grid wire-shape precedent (parallel Vec arrays, documented sentinels e.g. REGION_TEMP_NONE_DC); DistrictPos = (i32,i32) at scale.rs:75. Deliverable unchanged: D-226 amendment + proposed follow-up wiring ticket(s); no implementation in this ticket.', 'in_progress', 'high', NULL, 'client', 'D-226', '2026-07-16 19:55:47.761', '2026-07-18 09:46:46.436', NULL, '6d36cf2c007c6a8e80d8bcc8a8c81ab5', 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 ('06FPQXFFHXEEDQEM7PGXAH3534', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas climate overlay — render region_grid (temp/season/weather/moisture) in the viewer', 'Follow-up to T-1113 (merged PR #179): the server now serves RegionGridLayer (dense row-major; season/weather repr(u8) discriminants, mean_temp deci-degC i16 with i16::MIN airless sentinel, moisture_q u8) through AtlasLayerResponse.region_grid, and protocol.gd passes it through. Build the client overlay per the D-226 incremental-overlay pattern: OVERLAY_DEFS gen_* id (atlas_viewer.gd), _draw_gen_* renderer (atlas_marker_overlay.gd — a region-grid fill analogous to _draw_gen_district but at region granularity), GENERATION_LEGEND entry (atlas_legend_panel.gd). Pick ONE visual channel (mean temp ramp is the obvious first; season/weather as tooltip or variant overlays). Also add a populated region_grid wire fixture (all three committed fixtures carry nil — noted in PR #179).
---
Refinement (2026-07-18, Si review — verified anchors): protocol.gd = client/scripts/protocol/protocol.gd (passthrough already shipped at :821). Touch points with directly-copyable district_grid precedent: atlas_viewer.gd:149-153 (OVERLAY_DEFS pattern) + :376-385 (set/get_generation_district_grid accessor pair to mirror) + :436 (dispatch in _on_atlas_layers_received); atlas_marker_overlay.gd:477-494 (_draw_gen_district, dispatched :185-188); atlas_legend_panel.gd:82-88.
COORDINATE PATH (explicit, two patterns coexist in the same file): copy _draw_gen_district''s SELF-CONTAINED mapping — cols/rows come from the layer dict itself (server-computed extent), cw = tex_w/cols, ch = tex_h/rows, fills the whole heightmap texture. Do NOT use the _gen_pos/_gen_grid_w/_gen_grid_h path (that is for Layer1 point data like rivers).
Legend row is GATE-ENFORCED: test_atlas_overlays.gd:192-209 generically asserts every gen_* overlay id has a legend row — the legend entry is required for the suite to pass, not polish.
Fixtures: client/tests/fixtures/msgpack/atlas_response_{ready,pending,not_found}.msgpack, generated by server/tests/gen_fixtures.rs::generate_atlas_layer_response_fixtures (:627/:641/:655 currently hardcode region_grid: None). Regen = make fixtures (NOT make fixtures-gauntlet — separate live pipeline). Populated fixture = add a non-empty RegionGridLayer literal following the road_graph/settlements populated literals at :577-618, run make fixtures, extend the Tier-2 replay test at test_atlas_overlays.gd:149. The server-side literal edit lands via Dudley (batch-mate, owns server/tests in this batch) — coordinate the literal''s contents with him; the client-side replay-test extension is this ticket''s.
Channel ruling confirmed: mean-temp ramp first (i16 deci-degC, REGION_TEMP_NONE_DC = i16::MIN airless sentinel needs an explicit render disposition — distinct sentinel color or skip); season/weather/moisture as tooltip or later variant overlays, not this ticket.', 'in_progress', 'medium', NULL, 'client', 'D-226', '2026-07-16 17:25:07.599', '2026-07-18 09:46:57.153', NULL, 'ebfa950210de9599ae598aa50e121126', 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 ('06FPQXHDSMSVD3VADV38NG9STW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire QuarterFootprintLayer per the D-226 T-1112 amendment (six touch points)', 'Implements the design recorded in the D-226 amendment (2026-07-16, T-1112, merged PR #179). Six touch points, each named in the amendment SS4: (1) ZoningType gains PartialOrd/Ord derives mirroring DistrictType''s T-994 precedent (enables the dominant_zoning declaration-order tie-break); (2) AtlasLayerResponse.quarter_footprints field + build_quarter_footprint_layer() mirroring build_district_grid, keyed off non-empty state.quarters, aggregates keyed by city_id (join via the same deterministic QuarterId derivation the dispatch uses); (3) protocol.gd passthrough; (4) OVERLAY_DEFS gen_* id (atlas_viewer.gd); (5) _draw_gen_* scaled-glyph renderer anchored on the L3 settlement dot — shape=dominant district type, color=density ramp on the settlement-gold family, zoom-gated at SETTLEMENT_LABEL_MIN_ZOOM (atlas_marker_overlay.gd); (6) GENERATION_LEGEND entry (atlas_legend_panel.gd). Five aggregate fields: density_avg_pct u8, dominant_district_type, dominant_zoning, landmark_count u8, corridor_count u8; landmark/corridor tooltip-only per the D-226(d) ceiling.
---
Refinement (2026-07-18, Si review — READY, all anchors verified current): ZoningType at generator.rs:234-235 missing PartialOrd/Ord exactly as stated (DistrictType''s T-994 derive block at :207 is the mirror). build_district_grid mirror template at layer_proxy.rs:106-133 (None-on-empty-source pattern confirmed); AtlasLayerResponse currently 5 layer fields — quarter_footprints is the SIXTH and, per the struct''s own growth doc at :76-79, the final one the current pattern allows (T-1124''s design pass, same batch, resolves what carries any seventh — do not resolve it here, just implement the sixth cleanly).
QUARTERS ARE POPULATED TODAY (critical check passed): BodyWorldState.quarters (body_world_state.rs:98) is filled by plugin.rs:365-367 on GenCompletion::SkeletonGenerated, and the executor (gen_queue.rs:436-474 GenerateSkeleton arm) calls the real generate_quarter_skeleton() + assign_all_block_tags() — NOT a stub. The stale TODO(#957) comment at plugin.rs:361-363 predates T-957 landing; delete it in passing. QuarterId derivation: plugin.rs:430/:648 via SeedDomain::Layer4Quarter (seed.rs:301).
Client anchors: SETTLEMENT_LABEL_MIN_ZOOM = 2.0 at atlas_marker_overlay.gd:113; L3 settlement-dot anchor _draw_gen_settlements at :695; _draw_gen_district shape template at :477; GENERATION_LEGEND at atlas_legend_panel.gd:41 (pure additive table; legend rows are gate-enforced by test_atlas_overlays.gd:192-209); protocol.gd:821 region_grid passthrough is the freshest sibling to mirror.
IN-SCOPE boilerplate not named in the six points: the get/set_generation_quarter_footprints accessor pair on atlas_viewer.gd (folded into touch point 4, exactly as region_grid''s accessors ride its OVERLAY_DEFS entry; protocol dispatch calls the setter — see protocol.gd:436 pattern).
Batch split (lead): points 1-2 (server) = Dudley, who also owns the gen_fixtures.rs edits for this batch (consider adding a populated quarter_footprints literal alongside T-1118''s region_grid literal — reviewers will expect the new layer to have fixture coverage); points 3-6 (client) + T-1118 = Stig. Dudley pins the quarter_footprints wire field names/types FIRST and messages Stig the shape before Stig wires the client half.', 'in_progress', 'medium', NULL, 'server', 'D-226', '2026-07-16 17:25:23.533', '2026-07-18 09:47:00.752', NULL, '1f5f50c600376af09f910a9055ca7160', 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 ('06FQ9B8F39QD7PGPG4W35AWCRG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire windowed district-resolution Atlas layer — server half (D-226 T-1124 amendment)', 'Implement the D-226 T-1124 amendment SS1-SS4 (server half). (1) AtlasLayerRequest gains window_center: Option-DistrictPos + window_n: u32, both serde(default) — absent = whole-body, wire back-compat. (2) AtlasLayerResponse gains district_window: Option-DistrictWindowLayer per the amendment''s exact field list (echoed center/n + six parallel arrays: morphology u8, elev_q u8, temp_dc i16 with the RegionGridLayer sentinel, moisture_q u8, vegetation u8 incl Marine=6, glaciation u8). (3) handle_atlas_request gains a window-serving branch calling derive_district per cell (district_profile.rs:1415), clamping window_n to 1..=DISTRICT_WINDOW_MAX_N=64 server-side (never trust the wire).
MUST RESOLVE FIRST — the TerrainAnalysis availability decision (named in the amendment + T-1124 report): BodyWorldState does NOT retain TerrainAnalysis after cascade completion (transient ~2 MB, dropped at cascade.rs:141 per D-203 memory budget), but derive_district needs a reference to it. Either (a) cache TerrainAnalysis alongside BodyWorldState (quantify the ~2 MB/body cost against BodyWorldStateCache LRU capacity) or (b) re-derive via run_layer1 per cache miss (quantify the O(w*h) whole-body re-analysis as a window-request tax — aliveness_probe''s current workaround). Decide with numbers, record the choice in the PR.
(4) Tests mirroring build_region_grid/build_district_grid precedents: empty/clamped-window edges, msgpack round-trip, and a determinism spot-check (two full passes byte-identical — promoting the T-1123 probe-only proof to a real test, since this is the first served layer built from the on-demand derive path).', 'backlog', 'high', NULL, 'server', NULL, '2026-07-18 10:02:06.490', '2026-07-18 10:02:06.490', NULL, '08accefa37e20153780ff432ac9da44c', 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 ('06FQ9B977KGH3BWP1YQTN32TYM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas regional map screen — client half (D-226 T-1124 amendment SS5)', 'Implement the D-226 T-1124 amendment SS5 (client half). protocol.gd passthrough: encode_atlas_layer_request gains the window params (serde-default absent = whole-body); atlas_response_from_raw gains district_window passthrough (the doc comment near protocol.gd:801 names this the one client-side spot). New regional viewing mode per SS5: zoom-threshold LOD swap in the existing AtlasViewer (new DISTRICT_WINDOW_MIN_ZOOM past SETTLEMENT_LABEL_MIN_ZOOM — not a new screen, not a nav push); morphology base layer lightness-modulated by elev_q (probe MORPHOLOGY_RGB hues); gen_dw_temp/gen_dw_moisture/gen_dw_veg toggle overlays (temp reuses the T-1118 region-ramp colorizer EXACTLY — one colorizer across both zoom levels; Marine=6 renders transparent; glaciation is an always-on ice-tint modifier, not a toggle); GENERATION_LEGEND entries with the 17-zone base folded to ~5 family rows. Client window cache keyed (body_id, center, n), LRU-evict only (D-227 determinism — no freshness check). Re-request on PAN ONLY when the view passes the held window''s edge (zoom never refetches); 150ms post-drag-release debounce; windows float on the pan center (not grid-snapped). Implant chrome: ImplantHeader anchor name + extent subtitle; map palettes stay out of theme accent roles (ACCENT_ACTIVE gold is the settlement marker''s).', 'backlog', 'high', NULL, 'client', NULL, '2026-07-18 10:02:12.668', '2026-07-18 10:02:12.668', NULL, '4a3278bdf01b1b5e43512243f628f639', 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 ('06FQ9B977KGH3BWP1YQTN32TYM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas regional map screen — client half (D-226 T-1124 amendment SS5)', 'Implement the D-226 T-1124 amendment SS5 (client half). protocol.gd passthrough: encode_atlas_layer_request gains the window params (serde-default absent = whole-body); atlas_response_from_raw gains district_window passthrough (the doc comment near protocol.gd:801 names this the one client-side spot). New regional viewing mode per SS5: zoom-threshold LOD swap in the existing AtlasViewer (new DISTRICT_WINDOW_MIN_ZOOM past SETTLEMENT_LABEL_MIN_ZOOM — not a new screen, not a nav push); morphology base layer lightness-modulated by elev_q (probe MORPHOLOGY_RGB hues); gen_dw_temp/gen_dw_moisture/gen_dw_veg toggle overlays (temp reuses the T-1118 region-ramp colorizer EXACTLY — one colorizer across both zoom levels; Marine=6 renders transparent; glaciation is an always-on ice-tint modifier, not a toggle); GENERATION_LEGEND entries with the 17-zone base folded to ~5 family rows. Client window cache keyed (body_id, center, n), LRU-evict only (D-227 determinism — no freshness check). Re-request on PAN ONLY when the view passes the held window''s edge (zoom never refetches); 150ms post-drag-release debounce; windows float on the pan center (not grid-snapped). Implant chrome: ImplantHeader anchor name + extent subtitle; map palettes stay out of theme accent roles (ACCENT_ACTIVE gold is the settlement marker''s).
Implementation note (Tyre, T-1124 report): DISTRICT_WINDOW_MIN_ZOOM proposed at 6.0 but atlas_viewer.gd caps _view_zoom at MAX_ZOOM = 8.0 — only a 6.0-8.0 band for the whole in-window-zoom detail story the SS5 legibility argument depends on. Tunable client constant, not wire contract: either lower the swap threshold or raise MAX_ZOOM; decide with on-screen-scale math at implementation.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-18 10:02:12.668', '2026-07-18 10:02:32.846', NULL, 'a62fcba50644d79ffb1a1950f2df8a32', 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 ('06FQ9GQDWFC5S4PK17NGX35RFG', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'msgpack Vec-u8 layer fields decode as generic Array, not PackedByteArray — add serde_bytes or client-side conversion', 'Found during T-1118/T-1119 (Stig, confirmed by Dudley): rmp_serde serializes plain Vec-u8 struct fields (RegionGridLayer season/weather/moisture_q, DistrictGridLayer morphology/elev_q, and now QuarterFootprintLayer-adjacent arrays) as generic msgpack arrays rather than bin format, so the pure-GDScript msgpack addon decodes them as generic Array, not PackedByteArray. The existing ''is PackedByteArray'' guards on the client are therefore untested dead branches against real wire data; per-element Variant overhead on dense grids is real but small at current sizes. Fix options: (a) serde_bytes annotations server-side (bin encoding — check the GDScript addon decodes bin to PackedByteArray), (b) drop the dead guards and standardize on generic-Array handling with defensive decode (what the new T-1118/T-1119 code does), (c) leave as-is documented. Decide once, apply consistently across all dense-grid layers. Pre-existing gap — predates this batch; district_grid shipped this way in T-1046.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-18 10:25:59.779', '2026-07-18 10:25:59.779', NULL, '05cc3a346e0a920cf531becf89f42d11', 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 ('06FPRZZ0P42NHC1QR5S3737EV4', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Design: Atlas regional map view — windowed district-resolution biome/climate screen (D-226)', 'Jeroen (2026-07-16): the Atlas needs the jet-plane altitude — a windowed district-resolution (2km-cell) map screen bridging the planetary canvas and the (never-mapped, D-226(d)) chunk/voxel world. Today the viewer is one whole-body texture with client-side pan/zoom; AtlasLayerRequest is {body_id, up_to} whole-body only — no rect/window/LOD machinery exists anywhere (recon-verified). Design pass (Tyre data shape + Araminta encoding, the T-1112 pattern): (1) windowed request protocol — rect/center+radius params on a new or extended layer request, served by calling derive_district per cell (district_profile.rs:1256, pure/on-demand — NOT the coarse eager grid); (2) which fields ship at district resolution (morphology, elev_q, temperature, moisture, vegetation — wire-integer discipline per D-010); (3) the screen: entry from a planetary-map click/settlement, second canvas vs zoom-threshold LOD swap, overlay/legend reuse; (4) budget: window size caps, caching, re-request-on-pan. Record as a D-226 amendment. District resolution is ABOVE the D-226(d) quarter-skeleton floor — in remit. Wiring is a follow-up ticket once the design is recorded. Natural batch-mates: T-1118 (climate overlay) + T-1119 (quarter glyphs). T-1123 (server-side window renderer) is the data-side proof and shares the per-cell derive pattern.
---
Refinement (2026-07-18, Si review + lead rulings — binding for the design pass):
CORRECTED ANCHORS (post PR #182 rewrite): derive_district is now district_profile.rs:1415 (signature gained climate: &ClimateConstants; delegates to the shared invent_primitives — still pure/on-demand, still NOT the coarse eager grid). AtlasLayerRequest is server/src/atlas/layer_proxy.rs:39 (not bridge/mod.rs); its up_to field is currently unwired (cascade always runs to Region). AtlasLayerResponse growth-ceiling doc at layer_proxy.rs:76-79. Five-shape demux cap at bridge/mod.rs:60-76.
RULING 1 — request side (closed, not open): the windowed request MUST be optional window fields ON AtlasLayerRequest (serde-default; absent = whole-body, wire back-compat) — a new top-level request shape is FORBIDDEN by the five-shape demux ceiling (PR #184 Tyre ruling: the sixth shape triggers the D-225 tagged-envelope migration; a window param does not justify paying that now). The ticket phrase ''a new or extended layer request'' is superseded: extended only.
RULING 2 — response side (mandatory explicit resolution IN the amendment): the AtlasLayerResponse one-Option-field-per-layer pattern tops out around six per its own doc; region_grid is the fifth and quarter_footprints (T-1119, THIS batch) consumes the sixth. The windowed district payload is therefore the SEVENTH — the design must explicitly choose and record its carrier: (a) a seventh Option field busting the soft response ceiling with rationale + a re-pinned ceiling, (b) a response-side tagged-envelope/second-response-shape migration, or (c) another mechanism (e.g. the window response as its own payload kind inside the existing atlas request/response buffers). Cite the T-1127 ruling (the ceiling governs NEW TOP-LEVEL Option layers, not per-cell fields inside a shipped layer) and keep the request-cap and response-budget questions explicitly distinct — they are different ceilings with different hardness.
FIELD LIST — two mandatory dispositions: (1) glaciation_grade — T-1127 explicitly deferred its wire+client half to this design pass (Vec of u8, elev_q precedent, probe ice-tint as the render pattern); accept or reject EXPLICITLY, lean accept. (2) vegetation now includes Marine at discriminant 6 (T-1126) — any palette/legend must be exhaustive (the probe palette match is a compile-time guard; the GDScript side needs the discipline manually).
Reusable precedents verified: aliveness_probe --render (T-1123) is the working per-cell derive_district-in-a-window loop + 17-entry MORPHOLOGY_RGB palette; RegionGridLayer (layer_proxy.rs:154-165) is the dense-grid wire-shape precedent (parallel Vec arrays, documented sentinels e.g. REGION_TEMP_NONE_DC); DistrictPos = (i32,i32) at scale.rs:75. Deliverable unchanged: D-226 amendment + proposed follow-up wiring ticket(s); no implementation in this ticket.', 'review', 'high', NULL, 'client', 'D-226', '2026-07-16 19:55:47.761', '2026-07-18 10:30:08.108', NULL, '55a8b7e057612d1b6f1d8bd41de74d3c', 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 ('06FPQXHDSMSVD3VADV38NG9STW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire QuarterFootprintLayer per the D-226 T-1112 amendment (six touch points)', 'Implements the design recorded in the D-226 amendment (2026-07-16, T-1112, merged PR #179). Six touch points, each named in the amendment SS4: (1) ZoningType gains PartialOrd/Ord derives mirroring DistrictType''s T-994 precedent (enables the dominant_zoning declaration-order tie-break); (2) AtlasLayerResponse.quarter_footprints field + build_quarter_footprint_layer() mirroring build_district_grid, keyed off non-empty state.quarters, aggregates keyed by city_id (join via the same deterministic QuarterId derivation the dispatch uses); (3) protocol.gd passthrough; (4) OVERLAY_DEFS gen_* id (atlas_viewer.gd); (5) _draw_gen_* scaled-glyph renderer anchored on the L3 settlement dot — shape=dominant district type, color=density ramp on the settlement-gold family, zoom-gated at SETTLEMENT_LABEL_MIN_ZOOM (atlas_marker_overlay.gd); (6) GENERATION_LEGEND entry (atlas_legend_panel.gd). Five aggregate fields: density_avg_pct u8, dominant_district_type, dominant_zoning, landmark_count u8, corridor_count u8; landmark/corridor tooltip-only per the D-226(d) ceiling.
---
Refinement (2026-07-18, Si review — READY, all anchors verified current): ZoningType at generator.rs:234-235 missing PartialOrd/Ord exactly as stated (DistrictType''s T-994 derive block at :207 is the mirror). build_district_grid mirror template at layer_proxy.rs:106-133 (None-on-empty-source pattern confirmed); AtlasLayerResponse currently 5 layer fields — quarter_footprints is the SIXTH and, per the struct''s own growth doc at :76-79, the final one the current pattern allows (T-1124''s design pass, same batch, resolves what carries any seventh — do not resolve it here, just implement the sixth cleanly).
QUARTERS ARE POPULATED TODAY (critical check passed): BodyWorldState.quarters (body_world_state.rs:98) is filled by plugin.rs:365-367 on GenCompletion::SkeletonGenerated, and the executor (gen_queue.rs:436-474 GenerateSkeleton arm) calls the real generate_quarter_skeleton() + assign_all_block_tags() — NOT a stub. The stale TODO(#957) comment at plugin.rs:361-363 predates T-957 landing; delete it in passing. QuarterId derivation: plugin.rs:430/:648 via SeedDomain::Layer4Quarter (seed.rs:301).
Client anchors: SETTLEMENT_LABEL_MIN_ZOOM = 2.0 at atlas_marker_overlay.gd:113; L3 settlement-dot anchor _draw_gen_settlements at :695; _draw_gen_district shape template at :477; GENERATION_LEGEND at atlas_legend_panel.gd:41 (pure additive table; legend rows are gate-enforced by test_atlas_overlays.gd:192-209); protocol.gd:821 region_grid passthrough is the freshest sibling to mirror.
IN-SCOPE boilerplate not named in the six points: the get/set_generation_quarter_footprints accessor pair on atlas_viewer.gd (folded into touch point 4, exactly as region_grid''s accessors ride its OVERLAY_DEFS entry; protocol dispatch calls the setter — see protocol.gd:436 pattern).
Batch split (lead): points 1-2 (server) = Dudley, who also owns the gen_fixtures.rs edits for this batch (consider adding a populated quarter_footprints literal alongside T-1118''s region_grid literal — reviewers will expect the new layer to have fixture coverage); points 3-6 (client) + T-1118 = Stig. Dudley pins the quarter_footprints wire field names/types FIRST and messages Stig the shape before Stig wires the client half.', 'review', 'medium', NULL, 'server', 'D-226', '2026-07-16 17:25:23.533', '2026-07-18 10:30:08.116', NULL, '5df4d43574cc60403e3bf1b649459786', 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 ('06FPQXFFHXEEDQEM7PGXAH3534', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas climate overlay — render region_grid (temp/season/weather/moisture) in the viewer', 'Follow-up to T-1113 (merged PR #179): the server now serves RegionGridLayer (dense row-major; season/weather repr(u8) discriminants, mean_temp deci-degC i16 with i16::MIN airless sentinel, moisture_q u8) through AtlasLayerResponse.region_grid, and protocol.gd passes it through. Build the client overlay per the D-226 incremental-overlay pattern: OVERLAY_DEFS gen_* id (atlas_viewer.gd), _draw_gen_* renderer (atlas_marker_overlay.gd — a region-grid fill analogous to _draw_gen_district but at region granularity), GENERATION_LEGEND entry (atlas_legend_panel.gd). Pick ONE visual channel (mean temp ramp is the obvious first; season/weather as tooltip or variant overlays). Also add a populated region_grid wire fixture (all three committed fixtures carry nil — noted in PR #179).
---
Refinement (2026-07-18, Si review — verified anchors): protocol.gd = client/scripts/protocol/protocol.gd (passthrough already shipped at :821). Touch points with directly-copyable district_grid precedent: atlas_viewer.gd:149-153 (OVERLAY_DEFS pattern) + :376-385 (set/get_generation_district_grid accessor pair to mirror) + :436 (dispatch in _on_atlas_layers_received); atlas_marker_overlay.gd:477-494 (_draw_gen_district, dispatched :185-188); atlas_legend_panel.gd:82-88.
COORDINATE PATH (explicit, two patterns coexist in the same file): copy _draw_gen_district''s SELF-CONTAINED mapping — cols/rows come from the layer dict itself (server-computed extent), cw = tex_w/cols, ch = tex_h/rows, fills the whole heightmap texture. Do NOT use the _gen_pos/_gen_grid_w/_gen_grid_h path (that is for Layer1 point data like rivers).
Legend row is GATE-ENFORCED: test_atlas_overlays.gd:192-209 generically asserts every gen_* overlay id has a legend row — the legend entry is required for the suite to pass, not polish.
Fixtures: client/tests/fixtures/msgpack/atlas_response_{ready,pending,not_found}.msgpack, generated by server/tests/gen_fixtures.rs::generate_atlas_layer_response_fixtures (:627/:641/:655 currently hardcode region_grid: None). Regen = make fixtures (NOT make fixtures-gauntlet — separate live pipeline). Populated fixture = add a non-empty RegionGridLayer literal following the road_graph/settlements populated literals at :577-618, run make fixtures, extend the Tier-2 replay test at test_atlas_overlays.gd:149. The server-side literal edit lands via Dudley (batch-mate, owns server/tests in this batch) — coordinate the literal''s contents with him; the client-side replay-test extension is this ticket''s.
Channel ruling confirmed: mean-temp ramp first (i16 deci-degC, REGION_TEMP_NONE_DC = i16::MIN airless sentinel needs an explicit render disposition — distinct sentinel color or skip); season/weather/moisture as tooltip or later variant overlays, not this ticket.', 'review', 'medium', NULL, 'client', 'D-226', '2026-07-16 17:25:07.599', '2026-07-18 10:30:08.116', NULL, 'a018183b9ea2c1388fdc613cd72a3217', 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 ('06FQ9B8F39QD7PGPG4W35AWCRG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire windowed district-resolution Atlas layer — server half (D-226 T-1124 amendment)', 'Implement the D-226 T-1124 amendment SS1-SS4 (server half). (1) AtlasLayerRequest gains window_center: Option-DistrictPos + window_n: u32, both serde(default) — absent = whole-body, wire back-compat. (2) AtlasLayerResponse gains district_window: Option-DistrictWindowLayer per the amendment''s exact field list (echoed center/n + six parallel arrays: morphology u8, elev_q u8, temp_dc i16 with the RegionGridLayer sentinel, moisture_q u8, vegetation u8 incl Marine=6, glaciation u8). (3) handle_atlas_request gains a window-serving branch calling derive_district per cell (district_profile.rs:1415), clamping window_n to 1..=DISTRICT_WINDOW_MAX_N=64 server-side (never trust the wire).
MUST RESOLVE FIRST — the TerrainAnalysis availability decision (named in the amendment + T-1124 report): BodyWorldState does NOT retain TerrainAnalysis after cascade completion (transient ~2 MB, dropped at cascade.rs:141 per D-203 memory budget), but derive_district needs a reference to it. Either (a) cache TerrainAnalysis alongside BodyWorldState (quantify the ~2 MB/body cost against BodyWorldStateCache LRU capacity) or (b) re-derive via run_layer1 per cache miss (quantify the O(w*h) whole-body re-analysis as a window-request tax — aliveness_probe''s current workaround). Decide with numbers, record the choice in the PR.
(4) Tests mirroring build_region_grid/build_district_grid precedents: empty/clamped-window edges, msgpack round-trip, and a determinism spot-check (two full passes byte-identical — promoting the T-1123 probe-only proof to a real test, since this is the first served layer built from the on-demand derive path).
Design change from the PR #185 Troblum review (binding, folded into the amendment): the window-serving branch must NOT call derive_district inline in serve_atlas_requests/PreInput — window derives ride the SAME Rayon background queue + Pending-response pattern the whole-body cache-miss path uses (response on a later tick; the client''s SS5 border-fade covers the wait). Recommended: per-connection coalescing — a newly queued window request supersedes an unserved older one for the same body. The SS4 numbers (~7ms n=32 / ~29ms n=64 debug) are background-queue latency, not tick-thread cost. Also: the amendment now pins a windowed-family ceiling — district_window is the ONLY windowed-query field this struct may carry; a second windowed query is a dedicated response message by rule.', 'backlog', 'high', NULL, 'server', NULL, '2026-07-18 10:02:06.490', '2026-07-18 10:38:20.072', NULL, '70938bfe2ea753dc473e99143d62f745', 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 ('06FPRZZ0P42NHC1QR5S3737EV4', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Design: Atlas regional map view — windowed district-resolution biome/climate screen (D-226)', 'Jeroen (2026-07-16): the Atlas needs the jet-plane altitude — a windowed district-resolution (2km-cell) map screen bridging the planetary canvas and the (never-mapped, D-226(d)) chunk/voxel world. Today the viewer is one whole-body texture with client-side pan/zoom; AtlasLayerRequest is {body_id, up_to} whole-body only — no rect/window/LOD machinery exists anywhere (recon-verified). Design pass (Tyre data shape + Araminta encoding, the T-1112 pattern): (1) windowed request protocol — rect/center+radius params on a new or extended layer request, served by calling derive_district per cell (district_profile.rs:1256, pure/on-demand — NOT the coarse eager grid); (2) which fields ship at district resolution (morphology, elev_q, temperature, moisture, vegetation — wire-integer discipline per D-010); (3) the screen: entry from a planetary-map click/settlement, second canvas vs zoom-threshold LOD swap, overlay/legend reuse; (4) budget: window size caps, caching, re-request-on-pan. Record as a D-226 amendment. District resolution is ABOVE the D-226(d) quarter-skeleton floor — in remit. Wiring is a follow-up ticket once the design is recorded. Natural batch-mates: T-1118 (climate overlay) + T-1119 (quarter glyphs). T-1123 (server-side window renderer) is the data-side proof and shares the per-cell derive pattern.
---
Refinement (2026-07-18, Si review + lead rulings — binding for the design pass):
CORRECTED ANCHORS (post PR #182 rewrite): derive_district is now district_profile.rs:1415 (signature gained climate: &ClimateConstants; delegates to the shared invent_primitives — still pure/on-demand, still NOT the coarse eager grid). AtlasLayerRequest is server/src/atlas/layer_proxy.rs:39 (not bridge/mod.rs); its up_to field is currently unwired (cascade always runs to Region). AtlasLayerResponse growth-ceiling doc at layer_proxy.rs:76-79. Five-shape demux cap at bridge/mod.rs:60-76.
RULING 1 — request side (closed, not open): the windowed request MUST be optional window fields ON AtlasLayerRequest (serde-default; absent = whole-body, wire back-compat) — a new top-level request shape is FORBIDDEN by the five-shape demux ceiling (PR #184 Tyre ruling: the sixth shape triggers the D-225 tagged-envelope migration; a window param does not justify paying that now). The ticket phrase ''a new or extended layer request'' is superseded: extended only.
RULING 2 — response side (mandatory explicit resolution IN the amendment): the AtlasLayerResponse one-Option-field-per-layer pattern tops out around six per its own doc; region_grid is the fifth and quarter_footprints (T-1119, THIS batch) consumes the sixth. The windowed district payload is therefore the SEVENTH — the design must explicitly choose and record its carrier: (a) a seventh Option field busting the soft response ceiling with rationale + a re-pinned ceiling, (b) a response-side tagged-envelope/second-response-shape migration, or (c) another mechanism (e.g. the window response as its own payload kind inside the existing atlas request/response buffers). Cite the T-1127 ruling (the ceiling governs NEW TOP-LEVEL Option layers, not per-cell fields inside a shipped layer) and keep the request-cap and response-budget questions explicitly distinct — they are different ceilings with different hardness.
FIELD LIST — two mandatory dispositions: (1) glaciation_grade — T-1127 explicitly deferred its wire+client half to this design pass (Vec of u8, elev_q precedent, probe ice-tint as the render pattern); accept or reject EXPLICITLY, lean accept. (2) vegetation now includes Marine at discriminant 6 (T-1126) — any palette/legend must be exhaustive (the probe palette match is a compile-time guard; the GDScript side needs the discipline manually).
Reusable precedents verified: aliveness_probe --render (T-1123) is the working per-cell derive_district-in-a-window loop + 17-entry MORPHOLOGY_RGB palette; RegionGridLayer (layer_proxy.rs:154-165) is the dense-grid wire-shape precedent (parallel Vec arrays, documented sentinels e.g. REGION_TEMP_NONE_DC); DistrictPos = (i32,i32) at scale.rs:75. Deliverable unchanged: D-226 amendment + proposed follow-up wiring ticket(s); no implementation in this ticket.', 'done', 'high', NULL, 'client', 'D-226', '2026-07-16 19:55:47.761', '2026-07-18 10:47:49.020', NULL, '4327e8338d4f4b6d321a9612011d5db1', 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 ('06FPQXFFHXEEDQEM7PGXAH3534', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas climate overlay — render region_grid (temp/season/weather/moisture) in the viewer', 'Follow-up to T-1113 (merged PR #179): the server now serves RegionGridLayer (dense row-major; season/weather repr(u8) discriminants, mean_temp deci-degC i16 with i16::MIN airless sentinel, moisture_q u8) through AtlasLayerResponse.region_grid, and protocol.gd passes it through. Build the client overlay per the D-226 incremental-overlay pattern: OVERLAY_DEFS gen_* id (atlas_viewer.gd), _draw_gen_* renderer (atlas_marker_overlay.gd — a region-grid fill analogous to _draw_gen_district but at region granularity), GENERATION_LEGEND entry (atlas_legend_panel.gd). Pick ONE visual channel (mean temp ramp is the obvious first; season/weather as tooltip or variant overlays). Also add a populated region_grid wire fixture (all three committed fixtures carry nil — noted in PR #179).
---
Refinement (2026-07-18, Si review — verified anchors): protocol.gd = client/scripts/protocol/protocol.gd (passthrough already shipped at :821). Touch points with directly-copyable district_grid precedent: atlas_viewer.gd:149-153 (OVERLAY_DEFS pattern) + :376-385 (set/get_generation_district_grid accessor pair to mirror) + :436 (dispatch in _on_atlas_layers_received); atlas_marker_overlay.gd:477-494 (_draw_gen_district, dispatched :185-188); atlas_legend_panel.gd:82-88.
COORDINATE PATH (explicit, two patterns coexist in the same file): copy _draw_gen_district''s SELF-CONTAINED mapping — cols/rows come from the layer dict itself (server-computed extent), cw = tex_w/cols, ch = tex_h/rows, fills the whole heightmap texture. Do NOT use the _gen_pos/_gen_grid_w/_gen_grid_h path (that is for Layer1 point data like rivers).
Legend row is GATE-ENFORCED: test_atlas_overlays.gd:192-209 generically asserts every gen_* overlay id has a legend row — the legend entry is required for the suite to pass, not polish.
Fixtures: client/tests/fixtures/msgpack/atlas_response_{ready,pending,not_found}.msgpack, generated by server/tests/gen_fixtures.rs::generate_atlas_layer_response_fixtures (:627/:641/:655 currently hardcode region_grid: None). Regen = make fixtures (NOT make fixtures-gauntlet — separate live pipeline). Populated fixture = add a non-empty RegionGridLayer literal following the road_graph/settlements populated literals at :577-618, run make fixtures, extend the Tier-2 replay test at test_atlas_overlays.gd:149. The server-side literal edit lands via Dudley (batch-mate, owns server/tests in this batch) — coordinate the literal''s contents with him; the client-side replay-test extension is this ticket''s.
Channel ruling confirmed: mean-temp ramp first (i16 deci-degC, REGION_TEMP_NONE_DC = i16::MIN airless sentinel needs an explicit render disposition — distinct sentinel color or skip); season/weather/moisture as tooltip or later variant overlays, not this ticket.', 'done', 'medium', NULL, 'client', 'D-226', '2026-07-16 17:25:07.599', '2026-07-18 10:47:49.028', NULL, '896a8ab8f0fd7aab7e16324e9af0536d', 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 ('06FPQXHDSMSVD3VADV38NG9STW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire QuarterFootprintLayer per the D-226 T-1112 amendment (six touch points)', 'Implements the design recorded in the D-226 amendment (2026-07-16, T-1112, merged PR #179). Six touch points, each named in the amendment SS4: (1) ZoningType gains PartialOrd/Ord derives mirroring DistrictType''s T-994 precedent (enables the dominant_zoning declaration-order tie-break); (2) AtlasLayerResponse.quarter_footprints field + build_quarter_footprint_layer() mirroring build_district_grid, keyed off non-empty state.quarters, aggregates keyed by city_id (join via the same deterministic QuarterId derivation the dispatch uses); (3) protocol.gd passthrough; (4) OVERLAY_DEFS gen_* id (atlas_viewer.gd); (5) _draw_gen_* scaled-glyph renderer anchored on the L3 settlement dot — shape=dominant district type, color=density ramp on the settlement-gold family, zoom-gated at SETTLEMENT_LABEL_MIN_ZOOM (atlas_marker_overlay.gd); (6) GENERATION_LEGEND entry (atlas_legend_panel.gd). Five aggregate fields: density_avg_pct u8, dominant_district_type, dominant_zoning, landmark_count u8, corridor_count u8; landmark/corridor tooltip-only per the D-226(d) ceiling.
---
Refinement (2026-07-18, Si review — READY, all anchors verified current): ZoningType at generator.rs:234-235 missing PartialOrd/Ord exactly as stated (DistrictType''s T-994 derive block at :207 is the mirror). build_district_grid mirror template at layer_proxy.rs:106-133 (None-on-empty-source pattern confirmed); AtlasLayerResponse currently 5 layer fields — quarter_footprints is the SIXTH and, per the struct''s own growth doc at :76-79, the final one the current pattern allows (T-1124''s design pass, same batch, resolves what carries any seventh — do not resolve it here, just implement the sixth cleanly).
QUARTERS ARE POPULATED TODAY (critical check passed): BodyWorldState.quarters (body_world_state.rs:98) is filled by plugin.rs:365-367 on GenCompletion::SkeletonGenerated, and the executor (gen_queue.rs:436-474 GenerateSkeleton arm) calls the real generate_quarter_skeleton() + assign_all_block_tags() — NOT a stub. The stale TODO(#957) comment at plugin.rs:361-363 predates T-957 landing; delete it in passing. QuarterId derivation: plugin.rs:430/:648 via SeedDomain::Layer4Quarter (seed.rs:301).
Client anchors: SETTLEMENT_LABEL_MIN_ZOOM = 2.0 at atlas_marker_overlay.gd:113; L3 settlement-dot anchor _draw_gen_settlements at :695; _draw_gen_district shape template at :477; GENERATION_LEGEND at atlas_legend_panel.gd:41 (pure additive table; legend rows are gate-enforced by test_atlas_overlays.gd:192-209); protocol.gd:821 region_grid passthrough is the freshest sibling to mirror.
IN-SCOPE boilerplate not named in the six points: the get/set_generation_quarter_footprints accessor pair on atlas_viewer.gd (folded into touch point 4, exactly as region_grid''s accessors ride its OVERLAY_DEFS entry; protocol dispatch calls the setter — see protocol.gd:436 pattern).
Batch split (lead): points 1-2 (server) = Dudley, who also owns the gen_fixtures.rs edits for this batch (consider adding a populated quarter_footprints literal alongside T-1118''s region_grid literal — reviewers will expect the new layer to have fixture coverage); points 3-6 (client) + T-1118 = Stig. Dudley pins the quarter_footprints wire field names/types FIRST and messages Stig the shape before Stig wires the client half.', 'done', 'medium', NULL, 'server', 'D-226', '2026-07-16 17:25:23.533', '2026-07-18 10:47:49.028', NULL, '8b740a6c9acd80c1a96141ebf05d491b', 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 ('06FQ9B977KGH3BWP1YQTN32TYM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas regional map screen — client half (D-226 T-1124 amendment SS5)', 'Implement the D-226 T-1124 amendment SS5 (client half). protocol.gd passthrough: encode_atlas_layer_request gains the window params (serde-default absent = whole-body); atlas_response_from_raw gains district_window passthrough (the doc comment near protocol.gd:801 names this the one client-side spot). New regional viewing mode per SS5: zoom-threshold LOD swap in the existing AtlasViewer (new DISTRICT_WINDOW_MIN_ZOOM past SETTLEMENT_LABEL_MIN_ZOOM — not a new screen, not a nav push); morphology base layer lightness-modulated by elev_q (probe MORPHOLOGY_RGB hues); gen_dw_temp/gen_dw_moisture/gen_dw_veg toggle overlays (temp reuses the T-1118 region-ramp colorizer EXACTLY — one colorizer across both zoom levels; Marine=6 renders transparent; glaciation is an always-on ice-tint modifier, not a toggle); GENERATION_LEGEND entries with the 17-zone base folded to ~5 family rows. Client window cache keyed (body_id, center, n), LRU-evict only (D-227 determinism — no freshness check). Re-request on PAN ONLY when the view passes the held window''s edge (zoom never refetches); 150ms post-drag-release debounce; windows float on the pan center (not grid-snapped). Implant chrome: ImplantHeader anchor name + extent subtitle; map palettes stay out of theme accent roles (ACCENT_ACTIVE gold is the settlement marker''s).
Implementation note (Tyre, T-1124 report): DISTRICT_WINDOW_MIN_ZOOM proposed at 6.0 but atlas_viewer.gd caps _view_zoom at MAX_ZOOM = 8.0 — only a 6.0-8.0 band for the whole in-window-zoom detail story the SS5 legibility argument depends on. Tunable client constant, not wire contract: either lower the swap threshold or raise MAX_ZOOM; decide with on-screen-scale math at implementation.
SUPERSEDED ENTRY MECHANIC (Jeroen, 2026-07-21 companion hands-on — recorded as the D-226 T-1124 SS5 entry revision): NO zoom-threshold LOD swap and NO DISTRICT_WINDOW_MIN_ZOOM — the earlier zoom-headroom note is moot. The planetary heightmap view becomes FIXED: remove drag-pan/wheel-zoom of the planetary canvas IN THIS TICKET, same change as the replacement (never strand close inspection), and update the header hint text accordingly. Entry: hovering the planetary heightmap shows a rectangle cursor representing the regional-mode bounds; click descends into the regional map centered on the click point''s derived DistrictPos (float-on-center rules carry over with pan-center read as click-point). In-mode behavior unchanged (pan-only refetch, 150ms debounce, D-227 cache, border-fade). Morph transition between modes explicitly deferred — the descent may cut. Open design point: the rectangle cursor is an affordance, not to scale (n=64 = ~131km = a few px on a planetary canvas) — resolve the honest representation (true-extent rectangle + zoom-in cut, or not-to-scale reticle with the real extent labeled) without implying more coverage than real. Note: set_view/get_view_offset (T-1120 capture API) must survive for the visual-golden harness even after user pan/zoom is removed.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-18 10:02:12.668', '2026-07-21 06:58:26.869', NULL, '946d4b4a331ee0dd7bf0ae1e08bb2da1', 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 ('06FR6Y1CNA5JJ68RN897RM7FBC', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Companion Atlas friction — overlay bar overflows right edge; dev ticket ids in tooltips', 'Jeroen''s first make atlas hands-on (2026-07-21). Two UI defects on the shared Atlas viewer (visible in-game too): (1) the overlay toggle bar is a single HBoxContainer row — 17 chips at 42px min overflow the right screen edge (his 2000px window cut the bar mid-BAS); positioning also measured the bar''s own width with a 520px fallback, aggravating the overflow. Fix: HFlowContainer with ALIGNMENT_END, viewer gives it everything right of the header (OVERLAY_BAR_HEADER_RESERVE) and rows wrap right-aligned. (2) OVERLAY_DEFS tooltips leaked dev provenance into player-facing text — ticket ids (#960, T-960, T-1046, T-1118, T-1119) and decision ids (D-181, D-226, D-243) mean nothing in-game; stripped to plain language keeping the semantic content (visibility-ladder wording like observable/needs corporate contact stays). His third point — planetary map fixed + rectangle-cursor click-through entry — is a design revision recorded on D-226 (T-1124 SS5 entry revision) and folded into T-1138, not this bug.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-21 06:58:38.890', '2026-07-21 06:58:38.890', NULL, '685d1e4e2587c40abb524d7a9a64c4d7', 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 ('06FR6Y1CNA5JJ68RN897RM7FBC', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Companion Atlas friction — overlay bar overflows right edge; dev ticket ids in tooltips', 'Jeroen''s first make atlas hands-on (2026-07-21). Two UI defects on the shared Atlas viewer (visible in-game too): (1) the overlay toggle bar is a single HBoxContainer row — 17 chips at 42px min overflow the right screen edge (his 2000px window cut the bar mid-BAS); positioning also measured the bar''s own width with a 520px fallback, aggravating the overflow. Fix: HFlowContainer with ALIGNMENT_END, viewer gives it everything right of the header (OVERLAY_BAR_HEADER_RESERVE) and rows wrap right-aligned. (2) OVERLAY_DEFS tooltips leaked dev provenance into player-facing text — ticket ids (#960, T-960, T-1046, T-1118, T-1119) and decision ids (D-181, D-226, D-243) mean nothing in-game; stripped to plain language keeping the semantic content (visibility-ladder wording like observable/needs corporate contact stays). His third point — planetary map fixed + rectangle-cursor click-through entry — is a design revision recorded on D-226 (T-1124 SS5 entry revision) and folded into T-1138, not this bug.', 'in_progress', 'medium', NULL, 'client', NULL, '2026-07-21 06:58:38.890', '2026-07-21 06:58:48.125', NULL, '1944f85140fa14cfe8a5676b6452bdf2', 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 ('06FR6Y1CNA5JJ68RN897RM7FBC', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Companion Atlas friction — overlay bar overflows right edge; dev ticket ids in tooltips', 'Jeroen''s first make atlas hands-on (2026-07-21). Two UI defects on the shared Atlas viewer (visible in-game too): (1) the overlay toggle bar is a single HBoxContainer row — 17 chips at 42px min overflow the right screen edge (his 2000px window cut the bar mid-BAS); positioning also measured the bar''s own width with a 520px fallback, aggravating the overflow. Fix: HFlowContainer with ALIGNMENT_END, viewer gives it everything right of the header (OVERLAY_BAR_HEADER_RESERVE) and rows wrap right-aligned. (2) OVERLAY_DEFS tooltips leaked dev provenance into player-facing text — ticket ids (#960, T-960, T-1046, T-1118, T-1119) and decision ids (D-181, D-226, D-243) mean nothing in-game; stripped to plain language keeping the semantic content (visibility-ladder wording like observable/needs corporate contact stays). His third point — planetary map fixed + rectangle-cursor click-through entry — is a design revision recorded on D-226 (T-1124 SS5 entry revision) and folded into T-1138, not this bug.', 'review', 'medium', NULL, 'client', NULL, '2026-07-21 06:58:38.890', '2026-07-21 07:16:21.984', NULL, '9f48ccd62c0fde1c731362c426f82e79', 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 ('06FR6Y1CNA5JJ68RN897RM7FBC', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Companion Atlas friction — overlay bar overflows right edge; dev ticket ids in tooltips', 'Jeroen''s first make atlas hands-on (2026-07-21). Two UI defects on the shared Atlas viewer (visible in-game too): (1) the overlay toggle bar is a single HBoxContainer row — 17 chips at 42px min overflow the right screen edge (his 2000px window cut the bar mid-BAS); positioning also measured the bar''s own width with a 520px fallback, aggravating the overflow. Fix: HFlowContainer with ALIGNMENT_END, viewer gives it everything right of the header (OVERLAY_BAR_HEADER_RESERVE) and rows wrap right-aligned. (2) OVERLAY_DEFS tooltips leaked dev provenance into player-facing text — ticket ids (#960, T-960, T-1046, T-1118, T-1119) and decision ids (D-181, D-226, D-243) mean nothing in-game; stripped to plain language keeping the semantic content (visibility-ladder wording like observable/needs corporate contact stays). His third point — planetary map fixed + rectangle-cursor click-through entry — is a design revision recorded on D-226 (T-1124 SS5 entry revision) and folded into T-1138, not this bug.', 'done', 'medium', NULL, 'client', NULL, '2026-07-21 06:58:38.890', '2026-07-21 07:30:24.988', NULL, 'eaebb38c8c0ddc70007694a9efb01dca', 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 ('06FQ9B8F39QD7PGPG4W35AWCRG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire windowed district-resolution Atlas layer — server half (D-226 T-1124 amendment)', 'Implement the D-226 T-1124 amendment SS1-SS4 (server half). (1) AtlasLayerRequest gains window_center: Option-DistrictPos + window_n: u32, both serde(default) — absent = whole-body, wire back-compat. (2) AtlasLayerResponse gains district_window: Option-DistrictWindowLayer per the amendment''s exact field list (echoed center/n + six parallel arrays: morphology u8, elev_q u8, temp_dc i16 with the RegionGridLayer sentinel, moisture_q u8, vegetation u8 incl Marine=6, glaciation u8). (3) handle_atlas_request gains a window-serving branch calling derive_district per cell (district_profile.rs:1415), clamping window_n to 1..=DISTRICT_WINDOW_MAX_N=64 server-side (never trust the wire).
MUST RESOLVE FIRST — the TerrainAnalysis availability decision (named in the amendment + T-1124 report): BodyWorldState does NOT retain TerrainAnalysis after cascade completion (transient ~2 MB, dropped at cascade.rs:141 per D-203 memory budget), but derive_district needs a reference to it. Either (a) cache TerrainAnalysis alongside BodyWorldState (quantify the ~2 MB/body cost against BodyWorldStateCache LRU capacity) or (b) re-derive via run_layer1 per cache miss (quantify the O(w*h) whole-body re-analysis as a window-request tax — aliveness_probe''s current workaround). Decide with numbers, record the choice in the PR.
(4) Tests mirroring build_region_grid/build_district_grid precedents: empty/clamped-window edges, msgpack round-trip, and a determinism spot-check (two full passes byte-identical — promoting the T-1123 probe-only proof to a real test, since this is the first served layer built from the on-demand derive path).
Design change from the PR #185 Troblum review (binding, folded into the amendment): the window-serving branch must NOT call derive_district inline in serve_atlas_requests/PreInput — window derives ride the SAME Rayon background queue + Pending-response pattern the whole-body cache-miss path uses (response on a later tick; the client''s SS5 border-fade covers the wait). Recommended: per-connection coalescing — a newly queued window request supersedes an unserved older one for the same body. The SS4 numbers (~7ms n=32 / ~29ms n=64 debug) are background-queue latency, not tick-thread cost. Also: the amendment now pins a windowed-family ceiling — district_window is the ONLY windowed-query field this struct may carry; a second windowed query is a dedicated response message by rule.', 'in_progress', 'high', NULL, 'server', NULL, '2026-07-18 10:02:06.490', '2026-07-21 09:51:17.157', NULL, 'eb8bfc795d16fd6273916dfdb14c9975', 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 ('06FQ9B977KGH3BWP1YQTN32TYM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas regional map screen — client half (D-226 T-1124 amendment SS5)', 'Implement the D-226 T-1124 amendment SS5 (client half). protocol.gd passthrough: encode_atlas_layer_request gains the window params (serde-default absent = whole-body); atlas_response_from_raw gains district_window passthrough (the doc comment near protocol.gd:801 names this the one client-side spot). New regional viewing mode per SS5: zoom-threshold LOD swap in the existing AtlasViewer (new DISTRICT_WINDOW_MIN_ZOOM past SETTLEMENT_LABEL_MIN_ZOOM — not a new screen, not a nav push); morphology base layer lightness-modulated by elev_q (probe MORPHOLOGY_RGB hues); gen_dw_temp/gen_dw_moisture/gen_dw_veg toggle overlays (temp reuses the T-1118 region-ramp colorizer EXACTLY — one colorizer across both zoom levels; Marine=6 renders transparent; glaciation is an always-on ice-tint modifier, not a toggle); GENERATION_LEGEND entries with the 17-zone base folded to ~5 family rows. Client window cache keyed (body_id, center, n), LRU-evict only (D-227 determinism — no freshness check). Re-request on PAN ONLY when the view passes the held window''s edge (zoom never refetches); 150ms post-drag-release debounce; windows float on the pan center (not grid-snapped). Implant chrome: ImplantHeader anchor name + extent subtitle; map palettes stay out of theme accent roles (ACCENT_ACTIVE gold is the settlement marker''s).
Implementation note (Tyre, T-1124 report): DISTRICT_WINDOW_MIN_ZOOM proposed at 6.0 but atlas_viewer.gd caps _view_zoom at MAX_ZOOM = 8.0 — only a 6.0-8.0 band for the whole in-window-zoom detail story the SS5 legibility argument depends on. Tunable client constant, not wire contract: either lower the swap threshold or raise MAX_ZOOM; decide with on-screen-scale math at implementation.
SUPERSEDED ENTRY MECHANIC (Jeroen, 2026-07-21 companion hands-on — recorded as the D-226 T-1124 SS5 entry revision): NO zoom-threshold LOD swap and NO DISTRICT_WINDOW_MIN_ZOOM — the earlier zoom-headroom note is moot. The planetary heightmap view becomes FIXED: remove drag-pan/wheel-zoom of the planetary canvas IN THIS TICKET, same change as the replacement (never strand close inspection), and update the header hint text accordingly. Entry: hovering the planetary heightmap shows a rectangle cursor representing the regional-mode bounds; click descends into the regional map centered on the click point''s derived DistrictPos (float-on-center rules carry over with pan-center read as click-point). In-mode behavior unchanged (pan-only refetch, 150ms debounce, D-227 cache, border-fade). Morph transition between modes explicitly deferred — the descent may cut. Open design point: the rectangle cursor is an affordance, not to scale (n=64 = ~131km = a few px on a planetary canvas) — resolve the honest representation (true-extent rectangle + zoom-in cut, or not-to-scale reticle with the real extent labeled) without implying more coverage than real. Note: set_view/get_view_offset (T-1120 capture API) must survive for the visual-golden harness even after user pan/zoom is removed.', 'in_progress', 'high', NULL, 'client', NULL, '2026-07-18 10:02:12.668', '2026-07-21 09:51:17.164', NULL, '1b04077f985d7dbf089652c892930536', 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 ('06FR8WNW3MY9X5NZ3VZFKKH1HR', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Regional-window header: nearest-settlement location label + atlas_standalone doc-line fix', 'Two small follow-ups from T-1138 (flagged in-code by Stig). (1) AtlasWindowViewer''s ImplantHeader location label always falls back to a coordinate/region label — the D-226 T-1124 SS5 intent is ''nearest settlement name when the window is over/near one'', but the district window carries no settlement join (that data lives on the planetary gen_l3_settlements layer, a different dataset). Options: pass the planetary settlements list into the district screen at descent time (client-side nearest lookup against the click DistrictPos — cheap, no wire change), or add a nearest_settlement field to DistrictWindowLayer (wire change — weigh against the windowed-family ceiling; likely unnecessary). (2) atlas_standalone.gd''s doc comment ''atlas_app.gd is never modified'' is imprecise since T-1138 touched that file — the specific mechanism it documents (reach-screen KEY_M/KEY_ESCAPE to HudGroups.close_app) is intact; reword the blanket claim to name the mechanism. Also note atlas_viewer.gd sits at 999/1000 gdlint lines — the next touch likely needs an extraction.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-21 11:32:19.614', '2026-07-21 11:32:19.614', NULL, 'c0e9d78ffcf918475ff84c5e077ff531', 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 ('06FQ9B8F39QD7PGPG4W35AWCRG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire windowed district-resolution Atlas layer — server half (D-226 T-1124 amendment)', 'Implement the D-226 T-1124 amendment SS1-SS4 (server half). (1) AtlasLayerRequest gains window_center: Option-DistrictPos + window_n: u32, both serde(default) — absent = whole-body, wire back-compat. (2) AtlasLayerResponse gains district_window: Option-DistrictWindowLayer per the amendment''s exact field list (echoed center/n + six parallel arrays: morphology u8, elev_q u8, temp_dc i16 with the RegionGridLayer sentinel, moisture_q u8, vegetation u8 incl Marine=6, glaciation u8). (3) handle_atlas_request gains a window-serving branch calling derive_district per cell (district_profile.rs:1415), clamping window_n to 1..=DISTRICT_WINDOW_MAX_N=64 server-side (never trust the wire).
MUST RESOLVE FIRST — the TerrainAnalysis availability decision (named in the amendment + T-1124 report): BodyWorldState does NOT retain TerrainAnalysis after cascade completion (transient ~2 MB, dropped at cascade.rs:141 per D-203 memory budget), but derive_district needs a reference to it. Either (a) cache TerrainAnalysis alongside BodyWorldState (quantify the ~2 MB/body cost against BodyWorldStateCache LRU capacity) or (b) re-derive via run_layer1 per cache miss (quantify the O(w*h) whole-body re-analysis as a window-request tax — aliveness_probe''s current workaround). Decide with numbers, record the choice in the PR.
(4) Tests mirroring build_region_grid/build_district_grid precedents: empty/clamped-window edges, msgpack round-trip, and a determinism spot-check (two full passes byte-identical — promoting the T-1123 probe-only proof to a real test, since this is the first served layer built from the on-demand derive path).
Design change from the PR #185 Troblum review (binding, folded into the amendment): the window-serving branch must NOT call derive_district inline in serve_atlas_requests/PreInput — window derives ride the SAME Rayon background queue + Pending-response pattern the whole-body cache-miss path uses (response on a later tick; the client''s SS5 border-fade covers the wait). Recommended: per-connection coalescing — a newly queued window request supersedes an unserved older one for the same body. The SS4 numbers (~7ms n=32 / ~29ms n=64 debug) are background-queue latency, not tick-thread cost. Also: the amendment now pins a windowed-family ceiling — district_window is the ONLY windowed-query field this struct may carry; a second windowed query is a dedicated response message by rule.', 'review', 'high', NULL, 'server', NULL, '2026-07-18 10:02:06.490', '2026-07-21 11:36:01.469', NULL, '2ce935c0d9afa256a82ad1ea51b18a20', 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 ('06FQ9B977KGH3BWP1YQTN32TYM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas regional map screen — client half (D-226 T-1124 amendment SS5)', 'Implement the D-226 T-1124 amendment SS5 (client half). protocol.gd passthrough: encode_atlas_layer_request gains the window params (serde-default absent = whole-body); atlas_response_from_raw gains district_window passthrough (the doc comment near protocol.gd:801 names this the one client-side spot). New regional viewing mode per SS5: zoom-threshold LOD swap in the existing AtlasViewer (new DISTRICT_WINDOW_MIN_ZOOM past SETTLEMENT_LABEL_MIN_ZOOM — not a new screen, not a nav push); morphology base layer lightness-modulated by elev_q (probe MORPHOLOGY_RGB hues); gen_dw_temp/gen_dw_moisture/gen_dw_veg toggle overlays (temp reuses the T-1118 region-ramp colorizer EXACTLY — one colorizer across both zoom levels; Marine=6 renders transparent; glaciation is an always-on ice-tint modifier, not a toggle); GENERATION_LEGEND entries with the 17-zone base folded to ~5 family rows. Client window cache keyed (body_id, center, n), LRU-evict only (D-227 determinism — no freshness check). Re-request on PAN ONLY when the view passes the held window''s edge (zoom never refetches); 150ms post-drag-release debounce; windows float on the pan center (not grid-snapped). Implant chrome: ImplantHeader anchor name + extent subtitle; map palettes stay out of theme accent roles (ACCENT_ACTIVE gold is the settlement marker''s).
Implementation note (Tyre, T-1124 report): DISTRICT_WINDOW_MIN_ZOOM proposed at 6.0 but atlas_viewer.gd caps _view_zoom at MAX_ZOOM = 8.0 — only a 6.0-8.0 band for the whole in-window-zoom detail story the SS5 legibility argument depends on. Tunable client constant, not wire contract: either lower the swap threshold or raise MAX_ZOOM; decide with on-screen-scale math at implementation.
SUPERSEDED ENTRY MECHANIC (Jeroen, 2026-07-21 companion hands-on — recorded as the D-226 T-1124 SS5 entry revision): NO zoom-threshold LOD swap and NO DISTRICT_WINDOW_MIN_ZOOM — the earlier zoom-headroom note is moot. The planetary heightmap view becomes FIXED: remove drag-pan/wheel-zoom of the planetary canvas IN THIS TICKET, same change as the replacement (never strand close inspection), and update the header hint text accordingly. Entry: hovering the planetary heightmap shows a rectangle cursor representing the regional-mode bounds; click descends into the regional map centered on the click point''s derived DistrictPos (float-on-center rules carry over with pan-center read as click-point). In-mode behavior unchanged (pan-only refetch, 150ms debounce, D-227 cache, border-fade). Morph transition between modes explicitly deferred — the descent may cut. Open design point: the rectangle cursor is an affordance, not to scale (n=64 = ~131km = a few px on a planetary canvas) — resolve the honest representation (true-extent rectangle + zoom-in cut, or not-to-scale reticle with the real extent labeled) without implying more coverage than real. Note: set_view/get_view_offset (T-1120 capture API) must survive for the visual-golden harness even after user pan/zoom is removed.', 'review', 'high', NULL, 'client', NULL, '2026-07-18 10:02:12.668', '2026-07-21 11:36:01.472', NULL, 'e3cf8bdf3f3dc4bd31ed10e75a26bce8', 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 ('06FQ9B8F39QD7PGPG4W35AWCRG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wire windowed district-resolution Atlas layer — server half (D-226 T-1124 amendment)', 'Implement the D-226 T-1124 amendment SS1-SS4 (server half). (1) AtlasLayerRequest gains window_center: Option-DistrictPos + window_n: u32, both serde(default) — absent = whole-body, wire back-compat. (2) AtlasLayerResponse gains district_window: Option-DistrictWindowLayer per the amendment''s exact field list (echoed center/n + six parallel arrays: morphology u8, elev_q u8, temp_dc i16 with the RegionGridLayer sentinel, moisture_q u8, vegetation u8 incl Marine=6, glaciation u8). (3) handle_atlas_request gains a window-serving branch calling derive_district per cell (district_profile.rs:1415), clamping window_n to 1..=DISTRICT_WINDOW_MAX_N=64 server-side (never trust the wire).
MUST RESOLVE FIRST — the TerrainAnalysis availability decision (named in the amendment + T-1124 report): BodyWorldState does NOT retain TerrainAnalysis after cascade completion (transient ~2 MB, dropped at cascade.rs:141 per D-203 memory budget), but derive_district needs a reference to it. Either (a) cache TerrainAnalysis alongside BodyWorldState (quantify the ~2 MB/body cost against BodyWorldStateCache LRU capacity) or (b) re-derive via run_layer1 per cache miss (quantify the O(w*h) whole-body re-analysis as a window-request tax — aliveness_probe''s current workaround). Decide with numbers, record the choice in the PR.
(4) Tests mirroring build_region_grid/build_district_grid precedents: empty/clamped-window edges, msgpack round-trip, and a determinism spot-check (two full passes byte-identical — promoting the T-1123 probe-only proof to a real test, since this is the first served layer built from the on-demand derive path).
Design change from the PR #185 Troblum review (binding, folded into the amendment): the window-serving branch must NOT call derive_district inline in serve_atlas_requests/PreInput — window derives ride the SAME Rayon background queue + Pending-response pattern the whole-body cache-miss path uses (response on a later tick; the client''s SS5 border-fade covers the wait). Recommended: per-connection coalescing — a newly queued window request supersedes an unserved older one for the same body. The SS4 numbers (~7ms n=32 / ~29ms n=64 debug) are background-queue latency, not tick-thread cost. Also: the amendment now pins a windowed-family ceiling — district_window is the ONLY windowed-query field this struct may carry; a second windowed query is a dedicated response message by rule.', 'done', 'high', NULL, 'server', NULL, '2026-07-18 10:02:06.490', '2026-07-21 12:03:40.352', NULL, '0f551bd63f094944905fef724bf7f25f', 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 ('06FQ9B977KGH3BWP1YQTN32TYM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas regional map screen — client half (D-226 T-1124 amendment SS5)', 'Implement the D-226 T-1124 amendment SS5 (client half). protocol.gd passthrough: encode_atlas_layer_request gains the window params (serde-default absent = whole-body); atlas_response_from_raw gains district_window passthrough (the doc comment near protocol.gd:801 names this the one client-side spot). New regional viewing mode per SS5: zoom-threshold LOD swap in the existing AtlasViewer (new DISTRICT_WINDOW_MIN_ZOOM past SETTLEMENT_LABEL_MIN_ZOOM — not a new screen, not a nav push); morphology base layer lightness-modulated by elev_q (probe MORPHOLOGY_RGB hues); gen_dw_temp/gen_dw_moisture/gen_dw_veg toggle overlays (temp reuses the T-1118 region-ramp colorizer EXACTLY — one colorizer across both zoom levels; Marine=6 renders transparent; glaciation is an always-on ice-tint modifier, not a toggle); GENERATION_LEGEND entries with the 17-zone base folded to ~5 family rows. Client window cache keyed (body_id, center, n), LRU-evict only (D-227 determinism — no freshness check). Re-request on PAN ONLY when the view passes the held window''s edge (zoom never refetches); 150ms post-drag-release debounce; windows float on the pan center (not grid-snapped). Implant chrome: ImplantHeader anchor name + extent subtitle; map palettes stay out of theme accent roles (ACCENT_ACTIVE gold is the settlement marker''s).
Implementation note (Tyre, T-1124 report): DISTRICT_WINDOW_MIN_ZOOM proposed at 6.0 but atlas_viewer.gd caps _view_zoom at MAX_ZOOM = 8.0 — only a 6.0-8.0 band for the whole in-window-zoom detail story the SS5 legibility argument depends on. Tunable client constant, not wire contract: either lower the swap threshold or raise MAX_ZOOM; decide with on-screen-scale math at implementation.
SUPERSEDED ENTRY MECHANIC (Jeroen, 2026-07-21 companion hands-on — recorded as the D-226 T-1124 SS5 entry revision): NO zoom-threshold LOD swap and NO DISTRICT_WINDOW_MIN_ZOOM — the earlier zoom-headroom note is moot. The planetary heightmap view becomes FIXED: remove drag-pan/wheel-zoom of the planetary canvas IN THIS TICKET, same change as the replacement (never strand close inspection), and update the header hint text accordingly. Entry: hovering the planetary heightmap shows a rectangle cursor representing the regional-mode bounds; click descends into the regional map centered on the click point''s derived DistrictPos (float-on-center rules carry over with pan-center read as click-point). In-mode behavior unchanged (pan-only refetch, 150ms debounce, D-227 cache, border-fade). Morph transition between modes explicitly deferred — the descent may cut. Open design point: the rectangle cursor is an affordance, not to scale (n=64 = ~131km = a few px on a planetary canvas) — resolve the honest representation (true-extent rectangle + zoom-in cut, or not-to-scale reticle with the real extent labeled) without implying more coverage than real. Note: set_view/get_view_offset (T-1120 capture API) must survive for the visual-golden harness even after user pan/zoom is removed.', 'done', 'high', NULL, 'client', NULL, '2026-07-18 10:02:12.668', '2026-07-21 12:03:40.355', NULL, 'fa260128491e3d8fae6b3cb991b9afe6', 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 ('06FR97BZHA6DV68S1BFG0HBN8G', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Descent allowed outside the map texture — letterbox clicks produce impossible DistrictPos; window opens as unfitted postage stamp', 'Jeroen''s first hands-on descent (2026-07-21): clicked in the letterbox beside the fixed planetary map and landed at DistrictPos (12276, 3021) — an IMPOSSIBLE column (largest terrestrial body with terrain is 7324.5 km, max valid col ~11236). Root cause: _update_hover sets _hover_active unconditionally and neither the reticle guard nor the descend fall-through checks that the cursor is on the heightmap texture — the 2:1 map letterboxed in a wide viewport leaves dead zones where the reticle promises descent and the click derives beyond the planet edge (clamped sampling = the uniform green he saw). Second defect: atlas_window_viewer.enter() resets zoom 1.0 / offset ZERO with no fit — an n=32 composite renders 512px native in a ~1900px viewport (postage stamp, not a filled map). Fixes: (1) client bounds gate — reticle hidden and descent inert unless screen_to_canvas(pos) is inside [0,tex_w)x[0,tex_h); (2) fit-and-center on enter + first window arrival + resize (zoom = ~0.9 x viewport/composite clamped to MIN/MAX_ZOOM, offset centers); (3) district header gains the body proper name (cheap half of T-1141 pulled forward — pass the body dict into enter); (4) SERVER: validate window_center like window_n — wrap columns modulo the body circumference, clamp rows to +/-rows_half (never trust the wire; today an out-of-range center derives clamped garbage and caches it); (5) live full-chain verification this time — the T-1138 live check requested a known-good pos directly over the wire, bypassing the click chain entirely, which is exactly where this hid. Also verify drag-pan works in the fitted view.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-21 12:19:02.154', '2026-07-21 12:19:02.154', NULL, '7fc78c537e3d9a9c64e644e0cfb6f5db', 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 ('06FR97DPEHANCYX2AC4021BSJC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Regional window legibility on quiet interiors — window-local elevation contrast', 'Follow-on from Jeroen''s first descent (2026-07-21): even with T-1142 fixed, many interior windows are genuinely one morphology zone across 66km, and the composite''s absolute lightness modulation (0.7 + 0.3 x elev_q/100, D-226 T-1124 SS5) spans only ~1-3% lightness over a window whose local elev range is small — the window reads as a dead flat fill rather than terrain. Proposal to evaluate WITH Araminta (SS5 is her encoding): window-local contrast stretch — normalize lightness within the window''s own elev min..max (fixed band, e.g. 0.62..0.92) with a degenerate floor (single-value window stays honestly flat), deterministic per window, mirroring how the aliveness_probe elev panel achieved its 12-level local texture. Alternative/complement: a faint deterministic per-cell variation or contour hinting. Any change to the recorded SS5 encoding gets a one-line amendment note. Assess AFTER T-1142 lands — real clicks will then land on real terrain and the residual flatness can be judged on coastal vs interior windows.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-21 12:19:16.212', '2026-07-21 12:19:16.212', NULL, '51e0232b3c75b03f994638ff86943956', 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 ('06FR97BZHA6DV68S1BFG0HBN8G', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Descent allowed outside the map texture — letterbox clicks produce impossible DistrictPos; window opens as unfitted postage stamp', 'Jeroen''s first hands-on descent (2026-07-21): clicked in the letterbox beside the fixed planetary map and landed at DistrictPos (12276, 3021) — an IMPOSSIBLE column (largest terrestrial body with terrain is 7324.5 km, max valid col ~11236). Root cause: _update_hover sets _hover_active unconditionally and neither the reticle guard nor the descend fall-through checks that the cursor is on the heightmap texture — the 2:1 map letterboxed in a wide viewport leaves dead zones where the reticle promises descent and the click derives beyond the planet edge (clamped sampling = the uniform green he saw). Second defect: atlas_window_viewer.enter() resets zoom 1.0 / offset ZERO with no fit — an n=32 composite renders 512px native in a ~1900px viewport (postage stamp, not a filled map). Fixes: (1) client bounds gate — reticle hidden and descent inert unless screen_to_canvas(pos) is inside [0,tex_w)x[0,tex_h); (2) fit-and-center on enter + first window arrival + resize (zoom = ~0.9 x viewport/composite clamped to MIN/MAX_ZOOM, offset centers); (3) district header gains the body proper name (cheap half of T-1141 pulled forward — pass the body dict into enter); (4) SERVER: validate window_center like window_n — wrap columns modulo the body circumference, clamp rows to +/-rows_half (never trust the wire; today an out-of-range center derives clamped garbage and caches it); (5) live full-chain verification this time — the T-1138 live check requested a known-good pos directly over the wire, bypassing the click chain entirely, which is exactly where this hid. Also verify drag-pan works in the fitted view.', 'in_progress', 'high', NULL, 'client', NULL, '2026-07-21 12:19:02.154', '2026-07-21 12:20:47.187', NULL, '5c7743dea68b35e327f890d3a7d96c61', 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 ('06FR9F9WH7SXVB14XWFDR0XEZ8', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'atlas_viewer.gd structural extraction — pinned at the gdlint 1000-line cap two rounds running', 'Flagged by Stig in the T-1142 fix round: atlas_viewer.gd has landed at exactly the 1000-line gdlint cap in two consecutive fix rounds (T-1138 fix round and T-1142), absorbed each time by compressing comments rather than restructuring. A real extraction is due — candidates on the established delegate/companion precedent (atlas_generation_state/proxy, atlas_map_protocol, atlas_overlay_colors): the city panel + sidebar block, the descend reticle + hover block, or the header/hint chrome. Public API must be preserved exactly (tests construct bare AtlasViewer.new() — mind the field-default vs _ready construction rule documented in the file). Do it as its own small PR, not smuggled into a feature round.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-21 12:53:42.153', '2026-07-21 12:53:42.153', NULL, 'eb3101765db2c6b21287a13ea8670510', 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 ('06FR97BZHA6DV68S1BFG0HBN8G', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Descent allowed outside the map texture — letterbox clicks produce impossible DistrictPos; window opens as unfitted postage stamp', 'Jeroen''s first hands-on descent (2026-07-21): clicked in the letterbox beside the fixed planetary map and landed at DistrictPos (12276, 3021) — an IMPOSSIBLE column (largest terrestrial body with terrain is 7324.5 km, max valid col ~11236). Root cause: _update_hover sets _hover_active unconditionally and neither the reticle guard nor the descend fall-through checks that the cursor is on the heightmap texture — the 2:1 map letterboxed in a wide viewport leaves dead zones where the reticle promises descent and the click derives beyond the planet edge (clamped sampling = the uniform green he saw). Second defect: atlas_window_viewer.enter() resets zoom 1.0 / offset ZERO with no fit — an n=32 composite renders 512px native in a ~1900px viewport (postage stamp, not a filled map). Fixes: (1) client bounds gate — reticle hidden and descent inert unless screen_to_canvas(pos) is inside [0,tex_w)x[0,tex_h); (2) fit-and-center on enter + first window arrival + resize (zoom = ~0.9 x viewport/composite clamped to MIN/MAX_ZOOM, offset centers); (3) district header gains the body proper name (cheap half of T-1141 pulled forward — pass the body dict into enter); (4) SERVER: validate window_center like window_n — wrap columns modulo the body circumference, clamp rows to +/-rows_half (never trust the wire; today an out-of-range center derives clamped garbage and caches it); (5) live full-chain verification this time — the T-1138 live check requested a known-good pos directly over the wire, bypassing the click chain entirely, which is exactly where this hid. Also verify drag-pan works in the fitted view.', 'review', 'high', NULL, 'client', NULL, '2026-07-21 12:19:02.154', '2026-07-21 13:11:49.373', NULL, 'dcf7c3d9e243090d02e0d8337f0945ec', 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 ('06FR97BZHA6DV68S1BFG0HBN8G', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Descent allowed outside the map texture — letterbox clicks produce impossible DistrictPos; window opens as unfitted postage stamp', 'Jeroen''s first hands-on descent (2026-07-21): clicked in the letterbox beside the fixed planetary map and landed at DistrictPos (12276, 3021) — an IMPOSSIBLE column (largest terrestrial body with terrain is 7324.5 km, max valid col ~11236). Root cause: _update_hover sets _hover_active unconditionally and neither the reticle guard nor the descend fall-through checks that the cursor is on the heightmap texture — the 2:1 map letterboxed in a wide viewport leaves dead zones where the reticle promises descent and the click derives beyond the planet edge (clamped sampling = the uniform green he saw). Second defect: atlas_window_viewer.enter() resets zoom 1.0 / offset ZERO with no fit — an n=32 composite renders 512px native in a ~1900px viewport (postage stamp, not a filled map). Fixes: (1) client bounds gate — reticle hidden and descent inert unless screen_to_canvas(pos) is inside [0,tex_w)x[0,tex_h); (2) fit-and-center on enter + first window arrival + resize (zoom = ~0.9 x viewport/composite clamped to MIN/MAX_ZOOM, offset centers); (3) district header gains the body proper name (cheap half of T-1141 pulled forward — pass the body dict into enter); (4) SERVER: validate window_center like window_n — wrap columns modulo the body circumference, clamp rows to +/-rows_half (never trust the wire; today an out-of-range center derives clamped garbage and caches it); (5) live full-chain verification this time — the T-1138 live check requested a known-good pos directly over the wire, bypassing the click chain entirely, which is exactly where this hid. Also verify drag-pan works in the fitted view.', 'done', 'high', NULL, 'client', NULL, '2026-07-21 12:19:02.154', '2026-07-21 13:26:53.797', NULL, '6e472936d0897314433d078f4bbbde95', 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 ('06FRA2DAZ2PDWE2YH76YBSQ2DW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Regional window round-2 friction — cover-fit, WASD plus edge-scroll pan, smoothed composite', 'Jeroen''s second hands-on descent (2026-07-21, KALLAST window): three items. (1) COVER-FIT: fit_window_view contains (min viewport dim) leaving large side margins in a wide viewport — change to cover (max viewport dim; the composite fills the screen edge to edge, data extends past the shorter axis into pan-space; the existing pan-edge refetch handles the view crossing window edges — verify no refetch churn at rest, i.e. the at-rest view inside one window must not trigger refetch when the window edge merely touches the viewport edge). (2) INPUT MODEL RULING: remove LMB-drag panning from the regional window entirely — clicks are reserved for map objects (settlements etc. will land in the window later). Pan = WASD + arrow keys (continuous, frame-rate independent, speed in screen px/s scaled by zoom) + EDGE SCROLLING (cursor within ~24px of a viewport edge pans toward it; disabled while the cursor is over UI). Wheel zoom unchanged. WASD/edge/zoom all set _user_adjusted. Esc still exits (keep the existing key path; W/A/S/D must not conflict with any current implant binding — check B/M etc. registry). Pole wall + wrap semantics unchanged and must keep working under the new pan inputs (extend the existing tests to drive WASD instead of drag; drag tests are REPLACED, not kept). (3) SMOOTHED COMPOSITE (interim presentation, pending the T-1143 design): render the window as an n x n Image/ImageTexture (one pixel per district, colors from the existing per-cell pipeline incl. overlays and ice tint) drawn scaled with LINEAR filtering instead of per-cell flat rects — GPU bilinear reads as terrain, not blocks; same treatment the planetary heightmap gets. Keep the per-cell path available if the overlay needs crisp mode later (a const toggle is fine). Record all three as one-line D-226 T-1124 SS5 amendment notes in the same PR (cover-fit supersedes the contain wording; input model supersedes drag-pan; smoothing noted as interim presentation pending T-1143). Live full-chain re-drive after (lead): WASD pan across a window edge, edge-scroll, cover assertion (no side margins at 16:9).', 'backlog', 'high', NULL, 'client', NULL, '2026-07-21 14:17:11.161', '2026-07-21 14:17:11.161', NULL, '2a46ecf6c880acaac6ec48d5f77b7b22', 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 ('06FR97DPEHANCYX2AC4021BSJC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Regional window legibility on quiet interiors — window-local elevation contrast', 'Follow-on from Jeroen''s first descent (2026-07-21): even with T-1142 fixed, many interior windows are genuinely one morphology zone across 66km, and the composite''s absolute lightness modulation (0.7 + 0.3 x elev_q/100, D-226 T-1124 SS5) spans only ~1-3% lightness over a window whose local elev range is small — the window reads as a dead flat fill rather than terrain. Proposal to evaluate WITH Araminta (SS5 is her encoding): window-local contrast stretch — normalize lightness within the window''s own elev min..max (fixed band, e.g. 0.62..0.92) with a degenerate floor (single-value window stays honestly flat), deterministic per window, mirroring how the aliveness_probe elev panel achieved its 12-level local texture. Alternative/complement: a faint deterministic per-cell variation or contour hinting. Any change to the recorded SS5 encoding gets a one-line amendment note. Assess AFTER T-1142 lands — real clicks will then land on real terrain and the residual flatness can be judged on coastal vs interior windows.
Scope expansion (Jeroen round-2, 2026-07-21): beyond window-local contrast, evaluate SUB-DISTRICT SAMPLING to de-block the composite at the data level — the D-227 invention fields (invent_primitives: warped envelope, scatter, character) are continuous at metre resolution and quarter granularity (512m, 4x4 per district) sits WITHIN the D-226(d) Atlas ceiling (the cap rules out chunk/tile mapping, not quarters). But classification (derive_district) is per-district today — finer sampling means a new derivation mode (e.g. sample the continuous primitives at quarter spacing and reuse the district''s classification for hue while modulating lightness/moisture per quarter, or classify per quarter at 16x the derive cost — n=32 window = 16384 quarter cells x ~7us = ~115ms background). Needs Dudley/Tyre on data feasibility + cost, Araminta on encoding. INTERIM already shipping in T-1145: linear-filtered texture rendering (presentation smoothing, no data change) — judge the residual need after both T-1145 and real-aim clicking land.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-21 12:19:16.212', '2026-07-21 14:17:24.701', NULL, '9a7dfdb0681020cf965ab27d33842298', 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 ('06FRA2DAZ2PDWE2YH76YBSQ2DW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Regional window round-2 friction — cover-fit, WASD plus edge-scroll pan, smoothed composite', 'Jeroen''s second hands-on descent (2026-07-21, KALLAST window): three items. (1) COVER-FIT: fit_window_view contains (min viewport dim) leaving large side margins in a wide viewport — change to cover (max viewport dim; the composite fills the screen edge to edge, data extends past the shorter axis into pan-space; the existing pan-edge refetch handles the view crossing window edges — verify no refetch churn at rest, i.e. the at-rest view inside one window must not trigger refetch when the window edge merely touches the viewport edge). (2) INPUT MODEL RULING: remove LMB-drag panning from the regional window entirely — clicks are reserved for map objects (settlements etc. will land in the window later). Pan = WASD + arrow keys (continuous, frame-rate independent, speed in screen px/s scaled by zoom) + EDGE SCROLLING (cursor within ~24px of a viewport edge pans toward it; disabled while the cursor is over UI). Wheel zoom unchanged. WASD/edge/zoom all set _user_adjusted. Esc still exits (keep the existing key path; W/A/S/D must not conflict with any current implant binding — check B/M etc. registry). Pole wall + wrap semantics unchanged and must keep working under the new pan inputs (extend the existing tests to drive WASD instead of drag; drag tests are REPLACED, not kept). (3) SMOOTHED COMPOSITE (interim presentation, pending the T-1143 design): render the window as an n x n Image/ImageTexture (one pixel per district, colors from the existing per-cell pipeline incl. overlays and ice tint) drawn scaled with LINEAR filtering instead of per-cell flat rects — GPU bilinear reads as terrain, not blocks; same treatment the planetary heightmap gets. Keep the per-cell path available if the overlay needs crisp mode later (a const toggle is fine). Record all three as one-line D-226 T-1124 SS5 amendment notes in the same PR (cover-fit supersedes the contain wording; input model supersedes drag-pan; smoothing noted as interim presentation pending T-1143). Live full-chain re-drive after (lead): WASD pan across a window edge, edge-scroll, cover assertion (no side margins at 16:9).', 'in_progress', 'high', NULL, 'client', NULL, '2026-07-21 14:17:11.161', '2026-07-21 14:17:33.958', NULL, '9d113fad367f24edc73d1cfbe5094b24', 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 ('06FRA9Z84VKCZ3ECQDEGDW4MSW', 'bug', '06FBPPMZNNEV052DBYYY3A897C', 'Gameplay movement input leaks to the server while an implant screen is open', 'Found by Stig during T-1145 (2026-07-21), pre-existing: InputMapper._process() polls the D-054 move_north/west/south/east actions unconditionally and the queue flush path (input_mapper.gd -> main.gd flush_queue -> sim_bridge.send_input) never checks HudGroups.gameplay_occluded / is_app_active — so pressing WASD while ANY implant screen is open (Atlas, browser, wiki...) queues real movement commands to the server and walks the character blind behind the fullscreen overlay. Affects every implant screen equally; the companion is immune only because readers cannot send inputs (T-1130 matrix). Fix shape: gate the InputMapper poll (or the queue flush) on not HudGroups.gameplay_occluded — decide whether stance/other gameplay actions need the same gate while there (they do — audit the full polled action set). Regression test: occluded -> WASD poll queues nothing; unoccluded -> queues normally. Note: T-1145''s regional-window pan deliberately reads raw physical keycodes, architecturally independent of this fix.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-21 14:50:12.902', '2026-07-21 14:50:12.902', NULL, '38bedb4bdf8179318f8049a22220256f', 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 ('06FRACDBETDV1MGJGPPC4V55ZM', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Flaky gate: fog-shader perf test asserts a margin-less 0.5ms wall-clock ceiling', 'test_fog_shader.gd test_visibility_texture_update_performance asserts elapsed < 0.5ms with zero headroom — failed the pre-push gate at 0.501ms (2026-07-21, atlas-regional-polish push; branch touches only atlas window files, unrelated). Wall-clock perf assertions in the gate suite are load-sensitive (concurrent builds/drives on the same box make 0.2 percent overruns routine). Fix options: (a) median-of-N runs against the same ceiling, (b) headroom to a documented budget multiple (e.g. 2x the target with the true target logged), (c) move hard perf ceilings out of the blocking gate tier into a perf-report tier. Audit the suite for siblings (test_full_fog_update_under_1ms passed at 4ms wall on the same run — check what it actually measures). Do not silently raise thresholds without recording the true budget intent.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-21 15:00:52.726', '2026-07-21 15:00:52.726', NULL, 'c4596a7e6479d8c4b540c66881a44ad9', 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 ('06FRA2DAZ2PDWE2YH76YBSQ2DW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Regional window round-2 friction — cover-fit, WASD plus edge-scroll pan, smoothed composite', 'Jeroen''s second hands-on descent (2026-07-21, KALLAST window): three items. (1) COVER-FIT: fit_window_view contains (min viewport dim) leaving large side margins in a wide viewport — change to cover (max viewport dim; the composite fills the screen edge to edge, data extends past the shorter axis into pan-space; the existing pan-edge refetch handles the view crossing window edges — verify no refetch churn at rest, i.e. the at-rest view inside one window must not trigger refetch when the window edge merely touches the viewport edge). (2) INPUT MODEL RULING: remove LMB-drag panning from the regional window entirely — clicks are reserved for map objects (settlements etc. will land in the window later). Pan = WASD + arrow keys (continuous, frame-rate independent, speed in screen px/s scaled by zoom) + EDGE SCROLLING (cursor within ~24px of a viewport edge pans toward it; disabled while the cursor is over UI). Wheel zoom unchanged. WASD/edge/zoom all set _user_adjusted. Esc still exits (keep the existing key path; W/A/S/D must not conflict with any current implant binding — check B/M etc. registry). Pole wall + wrap semantics unchanged and must keep working under the new pan inputs (extend the existing tests to drive WASD instead of drag; drag tests are REPLACED, not kept). (3) SMOOTHED COMPOSITE (interim presentation, pending the T-1143 design): render the window as an n x n Image/ImageTexture (one pixel per district, colors from the existing per-cell pipeline incl. overlays and ice tint) drawn scaled with LINEAR filtering instead of per-cell flat rects — GPU bilinear reads as terrain, not blocks; same treatment the planetary heightmap gets. Keep the per-cell path available if the overlay needs crisp mode later (a const toggle is fine). Record all three as one-line D-226 T-1124 SS5 amendment notes in the same PR (cover-fit supersedes the contain wording; input model supersedes drag-pan; smoothing noted as interim presentation pending T-1143). Live full-chain re-drive after (lead): WASD pan across a window edge, edge-scroll, cover assertion (no side margins at 16:9).', 'review', 'high', NULL, 'client', NULL, '2026-07-21 14:17:11.161', '2026-07-21 15:03:29.644', NULL, '2978c9f04b4e03ed4063c9bce640dcff', 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 ('06FRA2DAZ2PDWE2YH76YBSQ2DW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Regional window round-2 friction — cover-fit, WASD plus edge-scroll pan, smoothed composite', 'Jeroen''s second hands-on descent (2026-07-21, KALLAST window): three items. (1) COVER-FIT: fit_window_view contains (min viewport dim) leaving large side margins in a wide viewport — change to cover (max viewport dim; the composite fills the screen edge to edge, data extends past the shorter axis into pan-space; the existing pan-edge refetch handles the view crossing window edges — verify no refetch churn at rest, i.e. the at-rest view inside one window must not trigger refetch when the window edge merely touches the viewport edge). (2) INPUT MODEL RULING: remove LMB-drag panning from the regional window entirely — clicks are reserved for map objects (settlements etc. will land in the window later). Pan = WASD + arrow keys (continuous, frame-rate independent, speed in screen px/s scaled by zoom) + EDGE SCROLLING (cursor within ~24px of a viewport edge pans toward it; disabled while the cursor is over UI). Wheel zoom unchanged. WASD/edge/zoom all set _user_adjusted. Esc still exits (keep the existing key path; W/A/S/D must not conflict with any current implant binding — check B/M etc. registry). Pole wall + wrap semantics unchanged and must keep working under the new pan inputs (extend the existing tests to drive WASD instead of drag; drag tests are REPLACED, not kept). (3) SMOOTHED COMPOSITE (interim presentation, pending the T-1143 design): render the window as an n x n Image/ImageTexture (one pixel per district, colors from the existing per-cell pipeline incl. overlays and ice tint) drawn scaled with LINEAR filtering instead of per-cell flat rects — GPU bilinear reads as terrain, not blocks; same treatment the planetary heightmap gets. Keep the per-cell path available if the overlay needs crisp mode later (a const toggle is fine). Record all three as one-line D-226 T-1124 SS5 amendment notes in the same PR (cover-fit supersedes the contain wording; input model supersedes drag-pan; smoothing noted as interim presentation pending T-1143). Live full-chain re-drive after (lead): WASD pan across a window edge, edge-scroll, cover assertion (no side margins at 16:9).', 'done', 'high', NULL, 'client', NULL, '2026-07-21 14:17:11.161', '2026-07-21 15:08:57.829', NULL, 'f73aaf4a99a9b689490b82604723de89', 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 ('06FRA9Z84VKCZ3ECQDEGDW4MSW', 'bug', '06FBPPMZNNEV052DBYYY3A897C', 'Gameplay movement input leaks to the server while an implant screen is open', 'Found by Stig during T-1145 (2026-07-21), pre-existing: InputMapper._process() polls the D-054 move_north/west/south/east actions unconditionally and the queue flush path (input_mapper.gd -> main.gd flush_queue -> sim_bridge.send_input) never checks HudGroups.gameplay_occluded / is_app_active — so pressing WASD while ANY implant screen is open (Atlas, browser, wiki...) queues real movement commands to the server and walks the character blind behind the fullscreen overlay. Affects every implant screen equally; the companion is immune only because readers cannot send inputs (T-1130 matrix). Fix shape: gate the InputMapper poll (or the queue flush) on not HudGroups.gameplay_occluded — decide whether stance/other gameplay actions need the same gate while there (they do — audit the full polled action set). Regression test: occluded -> WASD poll queues nothing; unoccluded -> queues normally. Note: T-1145''s regional-window pan deliberately reads raw physical keycodes, architecturally independent of this fix.', 'in_progress', 'high', NULL, 'client', NULL, '2026-07-21 14:50:12.902', '2026-07-21 16:55:45.245', NULL, '446193b1e6cabf995a806614fbc77144', 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 ('06FR97DPEHANCYX2AC4021BSJC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Regional window legibility on quiet interiors — window-local elevation contrast', 'Follow-on from Jeroen''s first descent (2026-07-21): even with T-1142 fixed, many interior windows are genuinely one morphology zone across 66km, and the composite''s absolute lightness modulation (0.7 + 0.3 x elev_q/100, D-226 T-1124 SS5) spans only ~1-3% lightness over a window whose local elev range is small — the window reads as a dead flat fill rather than terrain. Proposal to evaluate WITH Araminta (SS5 is her encoding): window-local contrast stretch — normalize lightness within the window''s own elev min..max (fixed band, e.g. 0.62..0.92) with a degenerate floor (single-value window stays honestly flat), deterministic per window, mirroring how the aliveness_probe elev panel achieved its 12-level local texture. Alternative/complement: a faint deterministic per-cell variation or contour hinting. Any change to the recorded SS5 encoding gets a one-line amendment note. Assess AFTER T-1142 lands — real clicks will then land on real terrain and the residual flatness can be judged on coastal vs interior windows.
Scope expansion (Jeroen round-2, 2026-07-21): beyond window-local contrast, evaluate SUB-DISTRICT SAMPLING to de-block the composite at the data level — the D-227 invention fields (invent_primitives: warped envelope, scatter, character) are continuous at metre resolution and quarter granularity (512m, 4x4 per district) sits WITHIN the D-226(d) Atlas ceiling (the cap rules out chunk/tile mapping, not quarters). But classification (derive_district) is per-district today — finer sampling means a new derivation mode (e.g. sample the continuous primitives at quarter spacing and reuse the district''s classification for hue while modulating lightness/moisture per quarter, or classify per quarter at 16x the derive cost — n=32 window = 16384 quarter cells x ~7us = ~115ms background). Needs Dudley/Tyre on data feasibility + cost, Araminta on encoding. INTERIM already shipping in T-1145: linear-filtered texture rendering (presentation smoothing, no data change) — judge the residual need after both T-1145 and real-aim clicking land.
RULING (Jeroen, 2026-07-21, round-3 direction): the smooth-gradient rendering is INTERIM ONLY — he expects not to like it as the destination. The destination is SMALLER DETERMINISTICALLY-CALCULATED PIXELS (sub-district sampling, the sketch above), and the larger vision: SCROLL-ZOOM AS CONTINUOUS INFORMATION LoD — zooming in from the regional level progressively refines the sampled resolution seamlessly (no mode transition), ''zoom in to tile size from this level'' in his words. The design pass must therefore produce: (a) the granularity ladder the zoom traverses (district 2048m -> quarter 512m -> ? ), with derivation modes + per-window costs at each rung; (b) wire implications — finer windows multiply cell counts (quarter = 16x district cells/window); weigh against the windowed-family ceiling and the SS4 budget model (likely per-rung window requests reusing the same district_window carrier at different granularity, or a granularity param — design call); (c) THE FLOOR QUESTION back to Jeroen explicitly: ''tile size'' intersects the D-226(d) ceiling (chunk/tile output never Atlas-mapped; quarter is the current legal floor) — the pass presents the options (stop at quarter vs supersede D-226(d) for the window view) rather than assuming; (d) Araminta: encoding continuity across rungs (one colorizer family, hue stability as resolution refines). The COMPOSITE_SMOOTH linear-filter path ships until this lands, then is expected to be retired.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-21 12:19:16.212', '2026-07-21 17:00:31.999', NULL, 'dac8dd77af72f24a78d7ae8e15057504', 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 ('06FRBK72KQBDCBPEV4CF9M0PA0', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Hunt leaked scene-tree pollers contaminating gdUnit full-suite runs', 'Evidence from the T-1146 fix round (branch implant-input-gate, commit 4af3dd260): at least one node leaked into the persistent gdUnit scene tree by an earlier suite (a) consumes SimBridge.poll_snapshot() every frame — starving any later live test that polls directly (poll_snapshot is consume-and-clear), and (b) while SimBridge sits in ambient test_mode=true + CONNECTED, applies TestHarness mock snapshots into GameState.current_tick every frame for the REST OF THE RUN (observed current_tick ~15-18 before the input-gate suites even connected; a freshly spawned server then panicked with ''InputQueue: tick ordering violated, last=16 new=3''). The T-1146 live suite now defends itself (signal capture + SimBridge neutralization in before_test, see test_input_gate_live.gd comments), but the leak itself remains: it burns CPU across ~3.4k tests and will bite the next live-pipeline suite that doesn''t know to defend. Hunt: instrument the gdUnit root''s children between suites (a one-off print of get_children() after each suite, or a before_test tripwire asserting no unexpected siblings) to identify which suite leaks the polling node — candidates are suites instantiating main.tscn or harness-driven scenes (test_checklist, test_client_p2/p3, test_free_camera, test_audio_sprint13, test_p0_regressions, test_hub_teleport, test_camera_anchor). Fix at the source (auto_free/queue_free discipline or explicit teardown), then consider removing the now-redundant defensive layers or keeping them as belt-and-braces (document either way).', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-21 17:50:24.925', '2026-07-21 17:50:24.925', NULL, 'f071dd295ce09bdd1070dcd6d8c724e9', 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 ('06FRA9Z84VKCZ3ECQDEGDW4MSW', 'bug', '06FBPPMZNNEV052DBYYY3A897C', 'Gameplay movement input leaks to the server while an implant screen is open', 'Found by Stig during T-1145 (2026-07-21), pre-existing: InputMapper._process() polls the D-054 move_north/west/south/east actions unconditionally and the queue flush path (input_mapper.gd -> main.gd flush_queue -> sim_bridge.send_input) never checks HudGroups.gameplay_occluded / is_app_active — so pressing WASD while ANY implant screen is open (Atlas, browser, wiki...) queues real movement commands to the server and walks the character blind behind the fullscreen overlay. Affects every implant screen equally; the companion is immune only because readers cannot send inputs (T-1130 matrix). Fix shape: gate the InputMapper poll (or the queue flush) on not HudGroups.gameplay_occluded — decide whether stance/other gameplay actions need the same gate while there (they do — audit the full polled action set). Regression test: occluded -> WASD poll queues nothing; unoccluded -> queues normally. Note: T-1145''s regional-window pan deliberately reads raw physical keycodes, architecturally independent of this fix.', 'review', 'high', NULL, 'client', NULL, '2026-07-21 14:50:12.902', '2026-07-21 17:52:07.607', NULL, '7ad09fbe0ce12206745a1d08785822e8', 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 ('06FRA9Z84VKCZ3ECQDEGDW4MSW', 'bug', '06FBPPMZNNEV052DBYYY3A897C', 'Gameplay movement input leaks to the server while an implant screen is open', 'Found by Stig during T-1145 (2026-07-21), pre-existing: InputMapper._process() polls the D-054 move_north/west/south/east actions unconditionally and the queue flush path (input_mapper.gd -> main.gd flush_queue -> sim_bridge.send_input) never checks HudGroups.gameplay_occluded / is_app_active — so pressing WASD while ANY implant screen is open (Atlas, browser, wiki...) queues real movement commands to the server and walks the character blind behind the fullscreen overlay. Affects every implant screen equally; the companion is immune only because readers cannot send inputs (T-1130 matrix). Fix shape: gate the InputMapper poll (or the queue flush) on not HudGroups.gameplay_occluded — decide whether stance/other gameplay actions need the same gate while there (they do — audit the full polled action set). Regression test: occluded -> WASD poll queues nothing; unoccluded -> queues normally. Note: T-1145''s regional-window pan deliberately reads raw physical keycodes, architecturally independent of this fix.', 'done', 'high', NULL, 'client', NULL, '2026-07-21 14:50:12.902', '2026-07-21 18:02:45.248', NULL, '4d07095ff6a24a0243c850c607d97929', 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 ('06FR97DPEHANCYX2AC4021BSJC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Regional window legibility on quiet interiors — window-local elevation contrast', 'Follow-on from Jeroen''s first descent (2026-07-21): even with T-1142 fixed, many interior windows are genuinely one morphology zone across 66km, and the composite''s absolute lightness modulation (0.7 + 0.3 x elev_q/100, D-226 T-1124 SS5) spans only ~1-3% lightness over a window whose local elev range is small — the window reads as a dead flat fill rather than terrain. Proposal to evaluate WITH Araminta (SS5 is her encoding): window-local contrast stretch — normalize lightness within the window''s own elev min..max (fixed band, e.g. 0.62..0.92) with a degenerate floor (single-value window stays honestly flat), deterministic per window, mirroring how the aliveness_probe elev panel achieved its 12-level local texture. Alternative/complement: a faint deterministic per-cell variation or contour hinting. Any change to the recorded SS5 encoding gets a one-line amendment note. Assess AFTER T-1142 lands — real clicks will then land on real terrain and the residual flatness can be judged on coastal vs interior windows.
Scope expansion (Jeroen round-2, 2026-07-21): beyond window-local contrast, evaluate SUB-DISTRICT SAMPLING to de-block the composite at the data level — the D-227 invention fields (invent_primitives: warped envelope, scatter, character) are continuous at metre resolution and quarter granularity (512m, 4x4 per district) sits WITHIN the D-226(d) Atlas ceiling (the cap rules out chunk/tile mapping, not quarters). But classification (derive_district) is per-district today — finer sampling means a new derivation mode (e.g. sample the continuous primitives at quarter spacing and reuse the district''s classification for hue while modulating lightness/moisture per quarter, or classify per quarter at 16x the derive cost — n=32 window = 16384 quarter cells x ~7us = ~115ms background). Needs Dudley/Tyre on data feasibility + cost, Araminta on encoding. INTERIM already shipping in T-1145: linear-filtered texture rendering (presentation smoothing, no data change) — judge the residual need after both T-1145 and real-aim clicking land.
RULING (Jeroen, 2026-07-21, round-3 direction): the smooth-gradient rendering is INTERIM ONLY — he expects not to like it as the destination. The destination is SMALLER DETERMINISTICALLY-CALCULATED PIXELS (sub-district sampling, the sketch above), and the larger vision: SCROLL-ZOOM AS CONTINUOUS INFORMATION LoD — zooming in from the regional level progressively refines the sampled resolution seamlessly (no mode transition), ''zoom in to tile size from this level'' in his words. The design pass must therefore produce: (a) the granularity ladder the zoom traverses (district 2048m -> quarter 512m -> ? ), with derivation modes + per-window costs at each rung; (b) wire implications — finer windows multiply cell counts (quarter = 16x district cells/window); weigh against the windowed-family ceiling and the SS4 budget model (likely per-rung window requests reusing the same district_window carrier at different granularity, or a granularity param — design call); (c) THE FLOOR QUESTION back to Jeroen explicitly: ''tile size'' intersects the D-226(d) ceiling (chunk/tile output never Atlas-mapped; quarter is the current legal floor) — the pass presents the options (stop at quarter vs supersede D-226(d) for the window view) rather than assuming; (d) Araminta: encoding continuity across rungs (one colorizer family, hue stability as resolution refines). The COMPOSITE_SMOOTH linear-filter path ships until this lands, then is expected to be retired.
D-166 amendment 2026-07-21 (Jeroen, from the Groombridge/Lendel zoom captures): the seamless zoom ladder this ticket designs is now a HARD Phase-4 deliverable condition — T-750 does not close until the ladder stands (planetary map -> tile scale, every level deterministically derived at native granularity, never magnified interpolation; BHAG itself unchanged). Evidence baked into the amendment: at Lendel scale one heightmap px spans ~19 districts, so the current 16-district window is sub-pixel vs its source — beyond fit-zoom the viewer can only magnify the same composite, and the real waterline sat ~36 districts from the pixel-edge estimate (all window-scale-and-below information is D-227 invented detail and must be derived, not smoothed). This raises the design pass''s stakes: the granularity-rung ladder, wire budget, and the D-226(d) floor question are now phase-gating.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-21 12:19:16.212', '2026-07-21 19:01:45.386', NULL, '25333e2ddba335263d66e44c12e48c09', 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 ('06FR97DPEHANCYX2AC4021BSJC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Regional window legibility on quiet interiors — window-local elevation contrast', 'Follow-on from Jeroen''s first descent (2026-07-21): even with T-1142 fixed, many interior windows are genuinely one morphology zone across 66km, and the composite''s absolute lightness modulation (0.7 + 0.3 x elev_q/100, D-226 T-1124 SS5) spans only ~1-3% lightness over a window whose local elev range is small — the window reads as a dead flat fill rather than terrain. Proposal to evaluate WITH Araminta (SS5 is her encoding): window-local contrast stretch — normalize lightness within the window''s own elev min..max (fixed band, e.g. 0.62..0.92) with a degenerate floor (single-value window stays honestly flat), deterministic per window, mirroring how the aliveness_probe elev panel achieved its 12-level local texture. Alternative/complement: a faint deterministic per-cell variation or contour hinting. Any change to the recorded SS5 encoding gets a one-line amendment note. Assess AFTER T-1142 lands — real clicks will then land on real terrain and the residual flatness can be judged on coastal vs interior windows.
Scope expansion (Jeroen round-2, 2026-07-21): beyond window-local contrast, evaluate SUB-DISTRICT SAMPLING to de-block the composite at the data level — the D-227 invention fields (invent_primitives: warped envelope, scatter, character) are continuous at metre resolution and quarter granularity (512m, 4x4 per district) sits WITHIN the D-226(d) Atlas ceiling (the cap rules out chunk/tile mapping, not quarters). But classification (derive_district) is per-district today — finer sampling means a new derivation mode (e.g. sample the continuous primitives at quarter spacing and reuse the district''s classification for hue while modulating lightness/moisture per quarter, or classify per quarter at 16x the derive cost — n=32 window = 16384 quarter cells x ~7us = ~115ms background). Needs Dudley/Tyre on data feasibility + cost, Araminta on encoding. INTERIM already shipping in T-1145: linear-filtered texture rendering (presentation smoothing, no data change) — judge the residual need after both T-1145 and real-aim clicking land.
RULING (Jeroen, 2026-07-21, round-3 direction): the smooth-gradient rendering is INTERIM ONLY — he expects not to like it as the destination. The destination is SMALLER DETERMINISTICALLY-CALCULATED PIXELS (sub-district sampling, the sketch above), and the larger vision: SCROLL-ZOOM AS CONTINUOUS INFORMATION LoD — zooming in from the regional level progressively refines the sampled resolution seamlessly (no mode transition), ''zoom in to tile size from this level'' in his words. The design pass must therefore produce: (a) the granularity ladder the zoom traverses (district 2048m -> quarter 512m -> ? ), with derivation modes + per-window costs at each rung; (b) wire implications — finer windows multiply cell counts (quarter = 16x district cells/window); weigh against the windowed-family ceiling and the SS4 budget model (likely per-rung window requests reusing the same district_window carrier at different granularity, or a granularity param — design call); (c) THE FLOOR QUESTION back to Jeroen explicitly: ''tile size'' intersects the D-226(d) ceiling (chunk/tile output never Atlas-mapped; quarter is the current legal floor) — the pass presents the options (stop at quarter vs supersede D-226(d) for the window view) rather than assuming; (d) Araminta: encoding continuity across rungs (one colorizer family, hue stability as resolution refines). The COMPOSITE_SMOOTH linear-filter path ships until this lands, then is expected to be retired.
D-166 amendment 2026-07-21 (Jeroen, from the Groombridge/Lendel zoom captures): the seamless zoom ladder this ticket designs is now a HARD Phase-4 deliverable condition — T-750 does not close until the ladder stands (planetary map -> tile scale, every level deterministically derived at native granularity, never magnified interpolation; BHAG itself unchanged). Evidence baked into the amendment: at Lendel scale one heightmap px spans ~19 districts, so the current 16-district window is sub-pixel vs its source — beyond fit-zoom the viewer can only magnify the same composite, and the real waterline sat ~36 districts from the pixel-edge estimate (all window-scale-and-below information is D-227 invented detail and must be derived, not smoothed). This raises the design pass''s stakes: the granularity-rung ladder, wire budget, and the D-226(d) floor question are now phase-gating.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-21 12:19:16.212', '2026-07-21 19:01:54.239', NULL, '6b60196e54ea21056fef844d4655176b', 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 ('06FR97DPEHANCYX2AC4021BSJC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Regional window legibility on quiet interiors — window-local elevation contrast', 'Follow-on from Jeroen''s first descent (2026-07-21): even with T-1142 fixed, many interior windows are genuinely one morphology zone across 66km, and the composite''s absolute lightness modulation (0.7 + 0.3 x elev_q/100, D-226 T-1124 SS5) spans only ~1-3% lightness over a window whose local elev range is small — the window reads as a dead flat fill rather than terrain. Proposal to evaluate WITH Araminta (SS5 is her encoding): window-local contrast stretch — normalize lightness within the window''s own elev min..max (fixed band, e.g. 0.62..0.92) with a degenerate floor (single-value window stays honestly flat), deterministic per window, mirroring how the aliveness_probe elev panel achieved its 12-level local texture. Alternative/complement: a faint deterministic per-cell variation or contour hinting. Any change to the recorded SS5 encoding gets a one-line amendment note. Assess AFTER T-1142 lands — real clicks will then land on real terrain and the residual flatness can be judged on coastal vs interior windows.
Scope expansion (Jeroen round-2, 2026-07-21): beyond window-local contrast, evaluate SUB-DISTRICT SAMPLING to de-block the composite at the data level — the D-227 invention fields (invent_primitives: warped envelope, scatter, character) are continuous at metre resolution and quarter granularity (512m, 4x4 per district) sits WITHIN the D-226(d) Atlas ceiling (the cap rules out chunk/tile mapping, not quarters). But classification (derive_district) is per-district today — finer sampling means a new derivation mode (e.g. sample the continuous primitives at quarter spacing and reuse the district''s classification for hue while modulating lightness/moisture per quarter, or classify per quarter at 16x the derive cost — n=32 window = 16384 quarter cells x ~7us = ~115ms background). Needs Dudley/Tyre on data feasibility + cost, Araminta on encoding. INTERIM already shipping in T-1145: linear-filtered texture rendering (presentation smoothing, no data change) — judge the residual need after both T-1145 and real-aim clicking land.
RULING (Jeroen, 2026-07-21, round-3 direction): the smooth-gradient rendering is INTERIM ONLY — he expects not to like it as the destination. The destination is SMALLER DETERMINISTICALLY-CALCULATED PIXELS (sub-district sampling, the sketch above), and the larger vision: SCROLL-ZOOM AS CONTINUOUS INFORMATION LoD — zooming in from the regional level progressively refines the sampled resolution seamlessly (no mode transition), ''zoom in to tile size from this level'' in his words. The design pass must therefore produce: (a) the granularity ladder the zoom traverses (district 2048m -> quarter 512m -> ? ), with derivation modes + per-window costs at each rung; (b) wire implications — finer windows multiply cell counts (quarter = 16x district cells/window); weigh against the windowed-family ceiling and the SS4 budget model (likely per-rung window requests reusing the same district_window carrier at different granularity, or a granularity param — design call); (c) THE FLOOR QUESTION back to Jeroen explicitly: ''tile size'' intersects the D-226(d) ceiling (chunk/tile output never Atlas-mapped; quarter is the current legal floor) — the pass presents the options (stop at quarter vs supersede D-226(d) for the window view) rather than assuming; (d) Araminta: encoding continuity across rungs (one colorizer family, hue stability as resolution refines). The COMPOSITE_SMOOTH linear-filter path ships until this lands, then is expected to be retired.
D-166 amendment 2026-07-21 (Jeroen, from the Groombridge/Lendel zoom captures): the seamless zoom ladder this ticket designs is now a HARD Phase-4 deliverable condition — T-750 does not close until the ladder stands (planetary map -> tile scale, every level deterministically derived at native granularity, never magnified interpolation; BHAG itself unchanged). Evidence baked into the amendment: at Lendel scale one heightmap px spans ~19 districts, so the current 16-district window is sub-pixel vs its source — beyond fit-zoom the viewer can only magnify the same composite, and the real waterline sat ~36 districts from the pixel-edge estimate (all window-scale-and-below information is D-227 invented detail and must be derived, not smoothed). This raises the design pass''s stakes: the granularity-rung ladder, wire budget, and the D-226(d) floor question are now phase-gating.
Corollary to the D-166 2026-07-21 amendment (Jeroen, same day): heightmap PNGs are demoted to INVISIBLE derivation input — no zoom level displays them, including orbital/planetary, which must itself be FULLY DERIVED. Display at every rung samples the derivation at CANVAS RESOLUTION (continuous field, not fixed rasters) — this drops zoom over- and undersampling by construction and makes the map adapt to any viewport size. Design-pass additions: (e) the planetary/orbital rung becomes derived output — spec its derivation mode + cost (whole-planet at canvas res; today''s AtlasViewer texture path retires when it lands); (f) canvas-resolution-adaptive sampling replaces fixed per-rung display granularity — the rung ladder defines DERIVATION granularity available at each scale, while sampling density tracks the viewport; (g) import_heightmaps stays as input storage only.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-21 12:19:16.212', '2026-07-21 19:06:30.921', NULL, '9db3036c671f4b1425a74f52c87b3b98', 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 ('06FR97DPEHANCYX2AC4021BSJC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Regional window legibility on quiet interiors — window-local elevation contrast', 'Follow-on from Jeroen''s first descent (2026-07-21): even with T-1142 fixed, many interior windows are genuinely one morphology zone across 66km, and the composite''s absolute lightness modulation (0.7 + 0.3 x elev_q/100, D-226 T-1124 SS5) spans only ~1-3% lightness over a window whose local elev range is small — the window reads as a dead flat fill rather than terrain. Proposal to evaluate WITH Araminta (SS5 is her encoding): window-local contrast stretch — normalize lightness within the window''s own elev min..max (fixed band, e.g. 0.62..0.92) with a degenerate floor (single-value window stays honestly flat), deterministic per window, mirroring how the aliveness_probe elev panel achieved its 12-level local texture. Alternative/complement: a faint deterministic per-cell variation or contour hinting. Any change to the recorded SS5 encoding gets a one-line amendment note. Assess AFTER T-1142 lands — real clicks will then land on real terrain and the residual flatness can be judged on coastal vs interior windows.
Scope expansion (Jeroen round-2, 2026-07-21): beyond window-local contrast, evaluate SUB-DISTRICT SAMPLING to de-block the composite at the data level — the D-227 invention fields (invent_primitives: warped envelope, scatter, character) are continuous at metre resolution and quarter granularity (512m, 4x4 per district) sits WITHIN the D-226(d) Atlas ceiling (the cap rules out chunk/tile mapping, not quarters). But classification (derive_district) is per-district today — finer sampling means a new derivation mode (e.g. sample the continuous primitives at quarter spacing and reuse the district''s classification for hue while modulating lightness/moisture per quarter, or classify per quarter at 16x the derive cost — n=32 window = 16384 quarter cells x ~7us = ~115ms background). Needs Dudley/Tyre on data feasibility + cost, Araminta on encoding. INTERIM already shipping in T-1145: linear-filtered texture rendering (presentation smoothing, no data change) — judge the residual need after both T-1145 and real-aim clicking land.
RULING (Jeroen, 2026-07-21, round-3 direction): the smooth-gradient rendering is INTERIM ONLY — he expects not to like it as the destination. The destination is SMALLER DETERMINISTICALLY-CALCULATED PIXELS (sub-district sampling, the sketch above), and the larger vision: SCROLL-ZOOM AS CONTINUOUS INFORMATION LoD — zooming in from the regional level progressively refines the sampled resolution seamlessly (no mode transition), ''zoom in to tile size from this level'' in his words. The design pass must therefore produce: (a) the granularity ladder the zoom traverses (district 2048m -> quarter 512m -> ? ), with derivation modes + per-window costs at each rung; (b) wire implications — finer windows multiply cell counts (quarter = 16x district cells/window); weigh against the windowed-family ceiling and the SS4 budget model (likely per-rung window requests reusing the same district_window carrier at different granularity, or a granularity param — design call); (c) THE FLOOR QUESTION back to Jeroen explicitly: ''tile size'' intersects the D-226(d) ceiling (chunk/tile output never Atlas-mapped; quarter is the current legal floor) — the pass presents the options (stop at quarter vs supersede D-226(d) for the window view) rather than assuming; (d) Araminta: encoding continuity across rungs (one colorizer family, hue stability as resolution refines). The COMPOSITE_SMOOTH linear-filter path ships until this lands, then is expected to be retired.
D-166 amendment 2026-07-21 (Jeroen, from the Groombridge/Lendel zoom captures): the seamless zoom ladder this ticket designs is now a HARD Phase-4 deliverable condition — T-750 does not close until the ladder stands (planetary map -> tile scale, every level deterministically derived at native granularity, never magnified interpolation; BHAG itself unchanged). Evidence baked into the amendment: at Lendel scale one heightmap px spans ~19 districts, so the current 16-district window is sub-pixel vs its source — beyond fit-zoom the viewer can only magnify the same composite, and the real waterline sat ~36 districts from the pixel-edge estimate (all window-scale-and-below information is D-227 invented detail and must be derived, not smoothed). This raises the design pass''s stakes: the granularity-rung ladder, wire budget, and the D-226(d) floor question are now phase-gating.
Corollary to the D-166 2026-07-21 amendment (Jeroen, same day): heightmap PNGs are demoted to INVISIBLE derivation input — no zoom level displays them, including orbital/planetary, which must itself be FULLY DERIVED. Display at every rung samples the derivation at CANVAS RESOLUTION (continuous field, not fixed rasters) — this drops zoom over- and undersampling by construction and makes the map adapt to any viewport size. Design-pass additions: (e) the planetary/orbital rung becomes derived output — spec its derivation mode + cost (whole-planet at canvas res; today''s AtlasViewer texture path retires when it lands); (f) canvas-resolution-adaptive sampling replaces fixed per-rung display granularity — the rung ladder defines DERIVATION granularity available at each scale, while sampling density tracks the viewport; (g) import_heightmaps stays as input storage only.', 'in_progress', 'high', NULL, 'client', NULL, '2026-07-21 12:19:16.212', '2026-07-21 19:10:46.889', NULL, 'ba1c5f1583066ebba07766b39cffcaa4', 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 ('06FRCET0RKD7ED9D9RY5Q99RQG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'derive_at_metres extraction + min_wavelength octave cutoff in enveloped_fbm', 'Zoom-ladder migration step 1 (docs/architecture/atlas-zoom-ladder-t1143.md SS8; keystone for every rung). Extract derive_district''s fractional-metres interior (district_profile.rs:1426+ already computes px/py/world_x_m/world_y_m/lat_deg from the integer DistrictPos) into derive_at_metres(seed, body, wx, wy, min_wl); derive_district becomes a thin integer-quantizing wrapper — bit-identical output for existing callers is the acceptance test (golden harness must not move). Add min_wavelength_m: f64 to enveloped_fbm (detail_scatter.rs:137-176) and the terrain_detail/voxel_relief const-octave paths: truncate octaves below the cutoff; evaluate Dudley''s amplitude-fade-near-cutoff refinement vs hard truncate (pop risk as sampling density crosses an octave boundary) and pick with a visual A/B. Re-benchmark derive cost with the cutoff active at district and quarter spacings — the design doc''s numbers are for the UNMODIFIED pipeline and are marked contested (SS2 status note + Troblum R-findings).', 'backlog', 'high', NULL, 'server', 'D-226', '2026-07-21 19:50:57.988', '2026-07-21 19:50:57.988', NULL, '1bdbf75be57bb3c9c117650378782188', 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 ('06FRCEV8Q32C2FW9Y6733419DG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'window_granularity + window_min_wl_m on the district_window carrier (five touch points + aliasing test)', 'Zoom-ladder migration step 3 (docs/architecture/atlas-zoom-ladder-t1143.md SS3). serde-default window_granularity: u32 (1=district, 4=quarter) and window_min_wl_m: u32 (0=off) on AtlasLayerRequest — the T-1124 SS1 additive pattern, no sixth demux shape. The design pass flags this as a five-touch-point coordinated change and mandates the test: server DistrictWindowLayer echo tuple (center, n, granularity), server FIFO-256 window cache key (layer_proxy.rs:308), server per-connection coalescing key (gen_queue.rs:221-224), client request struct, client 24-entry LRU key (atlas_window_cache.gd:33-38). MANDATORY regression test: granularity-4 and granularity-1 requests at identical (center, n) must produce DISTINCT cache entries on BOTH ends — a missed key (worst: client) silently aliases quarter data onto district slots with no error. Quarter derivation mode = Option B full reclassification at 512m spacing (design SS2), served through the same queue; per-request cell cap per the capped-density rule.', 'backlog', 'high', NULL, 'server', 'D-226', '2026-07-21 19:51:08.216', '2026-07-21 19:51:08.216', NULL, '223d6d026e39f34dbcb873c10ac123b2', 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 ('06FRCEWBS5RC5YET0SMSPZ1E9W', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Chunked par_iter for build_district_window_layer', 'Zoom-ladder migration step 2 (docs/architecture/atlas-zoom-ladder-t1143.md SS8). build_district_window_layer''s cell loop (layer_proxy.rs:388) is serial today; parallelize with row/column chunked par_iter on the existing Rayon pool (the GenerationQueue already runs windows on Rayon — this parallelizes WITHIN a window). Needed before quarter windows: n=32-extent at quarter granularity is 16,384 cells (~20-23ms serial uncapped, design SS2 table). Determinism: chunk boundaries must not change output — cells are independent by construction (derive_at_metres is pure), assert with a serial-vs-parallel golden comparison.', 'backlog', 'medium', NULL, 'server', 'D-226', '2026-07-21 19:51:17.193', '2026-07-21 19:51:17.193', NULL, 'fa5a115d44400e7191be794d7335b90b', 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 ('06FRCEXHYP8RVBPB8QNZHTNTR0', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Derived planetary rung — progressive capped-density tiling; AtlasViewer heightmap texture retires', 'Zoom-ladder migration step 4 (docs/architecture/atlas-zoom-ladder-t1143.md SS4/SS8) under Jeroen''s carrier ruling (2026-07-21): the orbital view becomes fully derived output served as PROGRESSIVE CAPPED-DENSITY TILING on the generalized district_window carrier at coarse granularity (region-band octaves, region_baseline_at_district blend path — no invent_primitives at orbital wavelengths) — no dense raster wire shape, no tagged-envelope migration. Client: AtlasViewer''s direct heightmap-texture display path retires IN THE SAME CHANGE that the derived orbital view lands (never strand the view); heightmap PNGs/systems.db raster become invisible derivation input only (D-166 corollary; import_heightmaps stays as input storage). Carries design risks R1 (planetary cost story is stacked unmeasured extrapolations — measure first), R2 (stepped categorical fields at orbital scale), R5 (granularity param cannot express coarser-than-district — needs the enum/log-scale redesign before this rung). Blocked by the granularity carrier and derive_at_metres steps.', 'backlog', 'high', NULL, 'server', 'D-226', '2026-07-21 19:51:26.966', '2026-07-21 19:51:26.966', NULL, '5d00f9ebf2ef16ebe4e7d4d5724edb9a', 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 ('06FRCEZ47J16E3XKE957HGR8H4', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Continuous cursor-anchored zoom ladder in the client — rung selection, progressive refinement, seam zoom with full-zoom-out reset', 'Zoom-ladder migration step 5 (docs/architecture/atlas-zoom-ladder-t1143.md SS5/SS8) under Jeroen''s seam ruling (2026-07-21): wheel zoom is CONTINUOUS and CURSOR-ANCHORED from the orbital frame down through regional granularities (supersedes the click-through-only entry; D-013''s ''zoom gesture owns spatial descent'' restored for this seam), with the HARD CONDITION that a full zoom-out resets to the original canonical planetary frame and location (the fixed orbital framing is the ladder''s top rest state — no drifted pan state at the top). Rung-selection rule: sample spacing E/C (world extent per canvas px); request the coarsest rung whose spacing <= 2x(E/C). Progressive refinement: hold the coarse composite, fetch finer granularity, sharpen in place (Araminta SS6: no hue step, no layout jump, coastline stable across rungs — acceptance criteria in the doc). Request coalescing across zoom gestures; T-1138''s click-through + reticle retire in the same change. Encoding continuity per SS6 (one colorizer family; hue-average rule ties to the reclassification mode). Blocked by the granularity carrier; the planetary top rung can land before or after (degrade gracefully to current fixed orbital view).', 'backlog', 'high', NULL, 'client', 'D-226', '2026-07-21 19:51:39.836', '2026-07-21 19:51:39.836', NULL, '4c3d09a884f926248e30a96fc05c71d5', 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 ('06FRCF0CX1Y5DTD3ER2JCDT27G', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Below-quarter measurement pass — block/tile rung costs and wire (gate for the opened (d) floor)', 'Gate ticket from Jeroen''s floor ruling (2026-07-21, D-226 amendment: ''we set a new BHAG so old restrictions are up for debate'' — the Atlas ladder may descend below quarter toward tile, implementation-gated on THIS pass). Measure what the design pass could not (docs/architecture/atlas-zoom-ladder-t1143.md SS2 marks block/tile ''not estimated''): per-cell derive cost at block (128m) and tile-adjacent spacings with the min_wavelength cutoff active; realistic window cell counts and wire bytes at those granularities vs the SS4 budget; memory/caching behavior; whether voxel_relief/voxel_mosaic math is reusable as-is for Atlas sampling or needs an extraction like derive_at_metres. Output: a costs appendix to the design doc + go/no-go recommendation per rung. Constraint preserved by the ruling: chunk/tile output never appears as a whole-body planetary layer — windowed viewport only. Blocked by derive_at_metres (the cutoff must exist to measure with it).', 'backlog', 'medium', NULL, 'server', 'D-226', '2026-07-21 19:51:50.248', '2026-07-21 19:51:50.248', NULL, 'febcd5cf37559ab0035868c37624a605', 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 ('06FRCF1A0K41K6S85X0HTXACHM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Retire COMPOSITE_SMOOTH once quarter windows are default at close zoom', 'Zoom-ladder migration step 6 (docs/architecture/atlas-zoom-ladder-t1143.md SS8). The linear-filter interim (atlas_window_overlay.gd:60, T-1145) retires once granularity-4 quarter windows are the default at close zoom — real classified quarter cells replace GPU bilinear smoothing as the source of sub-district texture (Jeroen''s ruling: smoothing was never the destination). Remove the compile-time const and the crisp/smooth dual path; keep one render path. Blocked by the client zoom ladder.', 'backlog', 'low', NULL, 'client', 'D-226', '2026-07-21 19:51:57.700', '2026-07-21 19:51:57.700', NULL, '9f78be3b6bdaafe665c0f21354b2118e', 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 ('06FR97DPEHANCYX2AC4021BSJC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Regional window legibility on quiet interiors — window-local elevation contrast', 'Follow-on from Jeroen''s first descent (2026-07-21): even with T-1142 fixed, many interior windows are genuinely one morphology zone across 66km, and the composite''s absolute lightness modulation (0.7 + 0.3 x elev_q/100, D-226 T-1124 SS5) spans only ~1-3% lightness over a window whose local elev range is small — the window reads as a dead flat fill rather than terrain. Proposal to evaluate WITH Araminta (SS5 is her encoding): window-local contrast stretch — normalize lightness within the window''s own elev min..max (fixed band, e.g. 0.62..0.92) with a degenerate floor (single-value window stays honestly flat), deterministic per window, mirroring how the aliveness_probe elev panel achieved its 12-level local texture. Alternative/complement: a faint deterministic per-cell variation or contour hinting. Any change to the recorded SS5 encoding gets a one-line amendment note. Assess AFTER T-1142 lands — real clicks will then land on real terrain and the residual flatness can be judged on coastal vs interior windows.
Scope expansion (Jeroen round-2, 2026-07-21): beyond window-local contrast, evaluate SUB-DISTRICT SAMPLING to de-block the composite at the data level — the D-227 invention fields (invent_primitives: warped envelope, scatter, character) are continuous at metre resolution and quarter granularity (512m, 4x4 per district) sits WITHIN the D-226(d) Atlas ceiling (the cap rules out chunk/tile mapping, not quarters). But classification (derive_district) is per-district today — finer sampling means a new derivation mode (e.g. sample the continuous primitives at quarter spacing and reuse the district''s classification for hue while modulating lightness/moisture per quarter, or classify per quarter at 16x the derive cost — n=32 window = 16384 quarter cells x ~7us = ~115ms background). Needs Dudley/Tyre on data feasibility + cost, Araminta on encoding. INTERIM already shipping in T-1145: linear-filtered texture rendering (presentation smoothing, no data change) — judge the residual need after both T-1145 and real-aim clicking land.
RULING (Jeroen, 2026-07-21, round-3 direction): the smooth-gradient rendering is INTERIM ONLY — he expects not to like it as the destination. The destination is SMALLER DETERMINISTICALLY-CALCULATED PIXELS (sub-district sampling, the sketch above), and the larger vision: SCROLL-ZOOM AS CONTINUOUS INFORMATION LoD — zooming in from the regional level progressively refines the sampled resolution seamlessly (no mode transition), ''zoom in to tile size from this level'' in his words. The design pass must therefore produce: (a) the granularity ladder the zoom traverses (district 2048m -> quarter 512m -> ? ), with derivation modes + per-window costs at each rung; (b) wire implications — finer windows multiply cell counts (quarter = 16x district cells/window); weigh against the windowed-family ceiling and the SS4 budget model (likely per-rung window requests reusing the same district_window carrier at different granularity, or a granularity param — design call); (c) THE FLOOR QUESTION back to Jeroen explicitly: ''tile size'' intersects the D-226(d) ceiling (chunk/tile output never Atlas-mapped; quarter is the current legal floor) — the pass presents the options (stop at quarter vs supersede D-226(d) for the window view) rather than assuming; (d) Araminta: encoding continuity across rungs (one colorizer family, hue stability as resolution refines). The COMPOSITE_SMOOTH linear-filter path ships until this lands, then is expected to be retired.
D-166 amendment 2026-07-21 (Jeroen, from the Groombridge/Lendel zoom captures): the seamless zoom ladder this ticket designs is now a HARD Phase-4 deliverable condition — T-750 does not close until the ladder stands (planetary map -> tile scale, every level deterministically derived at native granularity, never magnified interpolation; BHAG itself unchanged). Evidence baked into the amendment: at Lendel scale one heightmap px spans ~19 districts, so the current 16-district window is sub-pixel vs its source — beyond fit-zoom the viewer can only magnify the same composite, and the real waterline sat ~36 districts from the pixel-edge estimate (all window-scale-and-below information is D-227 invented detail and must be derived, not smoothed). This raises the design pass''s stakes: the granularity-rung ladder, wire budget, and the D-226(d) floor question are now phase-gating.
Corollary to the D-166 2026-07-21 amendment (Jeroen, same day): heightmap PNGs are demoted to INVISIBLE derivation input — no zoom level displays them, including orbital/planetary, which must itself be FULLY DERIVED. Display at every rung samples the derivation at CANVAS RESOLUTION (continuous field, not fixed rasters) — this drops zoom over- and undersampling by construction and makes the map adapt to any viewport size. Design-pass additions: (e) the planetary/orbital rung becomes derived output — spec its derivation mode + cost (whole-planet at canvas res; today''s AtlasViewer texture path retires when it lands); (f) canvas-resolution-adaptive sampling replaces fixed per-rung display granularity — the rung ladder defines DERIVATION granularity available at each scale, while sampling density tracks the viewport; (g) import_heightmaps stays as input storage only.
DESIGN PASS DELIVERED (2026-07-21): docs/architecture/atlas-zoom-ladder-t1143.md — 9-agent pass (Tyre/Dudley/Araminta design, Troblum adversarial + completeness critique, Qatux synthesis). Jeroen''s three rulings recorded as a D-226 amendment: (1) floor OPENED — ''we set a new BHAG so old restrictions are up for debate''; ladder may descend below quarter, gated on the T-1154 measurement pass; (2) planetary carrier = progressive capped-density tiling on the generalized district_window carrier; (3) entry seam = continuous cursor-anchored zoom with full-zoom-out reset to the canonical planetary frame (supersedes the click-through-only cut). Implementation chain created: T-1149 (derive_at_metres + octave cutoff, keystone) -> T-1150 (granularity carrier, five touch points + aliasing test) + T-1151 (window par_iter) -> T-1152 (derived planetary rung) + T-1153 (client continuous ladder) -> T-1155 (COMPOSITE_SMOOTH retirement); T-1154 (below-quarter measurement gate). Original scope items resolved by the ladder: window-local contrast stretch judged obsolete under quarter sampling (SS6), COMPOSITE_SMOOTH interim retires at T-1155.', 'in_progress', 'high', NULL, 'client', NULL, '2026-07-21 12:19:16.212', '2026-07-21 19:52:27.615', NULL, '56256a6146f8800e4181f79ac8f14a6b', 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 ('06FR97DPEHANCYX2AC4021BSJC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Regional window legibility on quiet interiors — window-local elevation contrast', 'Follow-on from Jeroen''s first descent (2026-07-21): even with T-1142 fixed, many interior windows are genuinely one morphology zone across 66km, and the composite''s absolute lightness modulation (0.7 + 0.3 x elev_q/100, D-226 T-1124 SS5) spans only ~1-3% lightness over a window whose local elev range is small — the window reads as a dead flat fill rather than terrain. Proposal to evaluate WITH Araminta (SS5 is her encoding): window-local contrast stretch — normalize lightness within the window''s own elev min..max (fixed band, e.g. 0.62..0.92) with a degenerate floor (single-value window stays honestly flat), deterministic per window, mirroring how the aliveness_probe elev panel achieved its 12-level local texture. Alternative/complement: a faint deterministic per-cell variation or contour hinting. Any change to the recorded SS5 encoding gets a one-line amendment note. Assess AFTER T-1142 lands — real clicks will then land on real terrain and the residual flatness can be judged on coastal vs interior windows.
Scope expansion (Jeroen round-2, 2026-07-21): beyond window-local contrast, evaluate SUB-DISTRICT SAMPLING to de-block the composite at the data level — the D-227 invention fields (invent_primitives: warped envelope, scatter, character) are continuous at metre resolution and quarter granularity (512m, 4x4 per district) sits WITHIN the D-226(d) Atlas ceiling (the cap rules out chunk/tile mapping, not quarters). But classification (derive_district) is per-district today — finer sampling means a new derivation mode (e.g. sample the continuous primitives at quarter spacing and reuse the district''s classification for hue while modulating lightness/moisture per quarter, or classify per quarter at 16x the derive cost — n=32 window = 16384 quarter cells x ~7us = ~115ms background). Needs Dudley/Tyre on data feasibility + cost, Araminta on encoding. INTERIM already shipping in T-1145: linear-filtered texture rendering (presentation smoothing, no data change) — judge the residual need after both T-1145 and real-aim clicking land.
RULING (Jeroen, 2026-07-21, round-3 direction): the smooth-gradient rendering is INTERIM ONLY — he expects not to like it as the destination. The destination is SMALLER DETERMINISTICALLY-CALCULATED PIXELS (sub-district sampling, the sketch above), and the larger vision: SCROLL-ZOOM AS CONTINUOUS INFORMATION LoD — zooming in from the regional level progressively refines the sampled resolution seamlessly (no mode transition), ''zoom in to tile size from this level'' in his words. The design pass must therefore produce: (a) the granularity ladder the zoom traverses (district 2048m -> quarter 512m -> ? ), with derivation modes + per-window costs at each rung; (b) wire implications — finer windows multiply cell counts (quarter = 16x district cells/window); weigh against the windowed-family ceiling and the SS4 budget model (likely per-rung window requests reusing the same district_window carrier at different granularity, or a granularity param — design call); (c) THE FLOOR QUESTION back to Jeroen explicitly: ''tile size'' intersects the D-226(d) ceiling (chunk/tile output never Atlas-mapped; quarter is the current legal floor) — the pass presents the options (stop at quarter vs supersede D-226(d) for the window view) rather than assuming; (d) Araminta: encoding continuity across rungs (one colorizer family, hue stability as resolution refines). The COMPOSITE_SMOOTH linear-filter path ships until this lands, then is expected to be retired.
D-166 amendment 2026-07-21 (Jeroen, from the Groombridge/Lendel zoom captures): the seamless zoom ladder this ticket designs is now a HARD Phase-4 deliverable condition — T-750 does not close until the ladder stands (planetary map -> tile scale, every level deterministically derived at native granularity, never magnified interpolation; BHAG itself unchanged). Evidence baked into the amendment: at Lendel scale one heightmap px spans ~19 districts, so the current 16-district window is sub-pixel vs its source — beyond fit-zoom the viewer can only magnify the same composite, and the real waterline sat ~36 districts from the pixel-edge estimate (all window-scale-and-below information is D-227 invented detail and must be derived, not smoothed). This raises the design pass''s stakes: the granularity-rung ladder, wire budget, and the D-226(d) floor question are now phase-gating.
Corollary to the D-166 2026-07-21 amendment (Jeroen, same day): heightmap PNGs are demoted to INVISIBLE derivation input — no zoom level displays them, including orbital/planetary, which must itself be FULLY DERIVED. Display at every rung samples the derivation at CANVAS RESOLUTION (continuous field, not fixed rasters) — this drops zoom over- and undersampling by construction and makes the map adapt to any viewport size. Design-pass additions: (e) the planetary/orbital rung becomes derived output — spec its derivation mode + cost (whole-planet at canvas res; today''s AtlasViewer texture path retires when it lands); (f) canvas-resolution-adaptive sampling replaces fixed per-rung display granularity — the rung ladder defines DERIVATION granularity available at each scale, while sampling density tracks the viewport; (g) import_heightmaps stays as input storage only.
DESIGN PASS DELIVERED (2026-07-21): docs/architecture/atlas-zoom-ladder-t1143.md — 9-agent pass (Tyre/Dudley/Araminta design, Troblum adversarial + completeness critique, Qatux synthesis). Jeroen''s three rulings recorded as a D-226 amendment: (1) floor OPENED — ''we set a new BHAG so old restrictions are up for debate''; ladder may descend below quarter, gated on the T-1154 measurement pass; (2) planetary carrier = progressive capped-density tiling on the generalized district_window carrier; (3) entry seam = continuous cursor-anchored zoom with full-zoom-out reset to the canonical planetary frame (supersedes the click-through-only cut). Implementation chain created: T-1149 (derive_at_metres + octave cutoff, keystone) -> T-1150 (granularity carrier, five touch points + aliasing test) + T-1151 (window par_iter) -> T-1152 (derived planetary rung) + T-1153 (client continuous ladder) -> T-1155 (COMPOSITE_SMOOTH retirement); T-1154 (below-quarter measurement gate). Original scope items resolved by the ladder: window-local contrast stretch judged obsolete under quarter sampling (SS6), COMPOSITE_SMOOTH interim retires at T-1155.', 'done', 'high', NULL, 'client', NULL, '2026-07-21 12:19:16.212', '2026-07-21 19:52:32.105', NULL, 'bde71c887bb5dcfeb867591b98b59285', 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 ('06FRCET0RKD7ED9D9RY5Q99RQG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'derive_at_metres extraction + min_wavelength octave cutoff in enveloped_fbm', 'Zoom-ladder migration step 1 (docs/architecture/atlas-zoom-ladder-t1143.md SS8; keystone for every rung). Extract derive_district''s fractional-metres interior (district_profile.rs:1426+ already computes px/py/world_x_m/world_y_m/lat_deg from the integer DistrictPos) into derive_at_metres(seed, body, wx, wy, min_wl); derive_district becomes a thin integer-quantizing wrapper — bit-identical output for existing callers is the acceptance test (golden harness must not move). Add min_wavelength_m: f64 to enveloped_fbm (detail_scatter.rs:137-176) and the terrain_detail/voxel_relief const-octave paths: truncate octaves below the cutoff; evaluate Dudley''s amplitude-fade-near-cutoff refinement vs hard truncate (pop risk as sampling density crosses an octave boundary) and pick with a visual A/B. Re-benchmark derive cost with the cutoff active at district and quarter spacings — the design doc''s numbers are for the UNMODIFIED pipeline and are marked contested (SS2 status note + Troblum R-findings).', 'in_progress', 'high', NULL, 'server', 'D-226', '2026-07-21 19:50:57.988', '2026-07-21 21:50:03.771', NULL, '5dc17566fd018673e86de991a69a46a8', 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 ('06FRCEV8Q32C2FW9Y6733419DG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'window_granularity + window_min_wl_m on the district_window carrier (five touch points + aliasing test)', 'Zoom-ladder migration step 3 (docs/architecture/atlas-zoom-ladder-t1143.md SS3). serde-default window_granularity: u32 (1=district, 4=quarter) and window_min_wl_m: u32 (0=off) on AtlasLayerRequest — the T-1124 SS1 additive pattern, no sixth demux shape. The design pass flags this as a five-touch-point coordinated change and mandates the test: server DistrictWindowLayer echo tuple (center, n, granularity), server FIFO-256 window cache key (layer_proxy.rs:308), server per-connection coalescing key (gen_queue.rs:221-224), client request struct, client 24-entry LRU key (atlas_window_cache.gd:33-38). MANDATORY regression test: granularity-4 and granularity-1 requests at identical (center, n) must produce DISTINCT cache entries on BOTH ends — a missed key (worst: client) silently aliases quarter data onto district slots with no error. Quarter derivation mode = Option B full reclassification at 512m spacing (design SS2), served through the same queue; per-request cell cap per the capped-density rule.', 'in_progress', 'high', NULL, 'server', 'D-226', '2026-07-21 19:51:08.216', '2026-07-21 21:50:03.778', NULL, '4aea6c43010a1cf6582e5e2e9d80b109', 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 ('06FRCEWBS5RC5YET0SMSPZ1E9W', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Chunked par_iter for build_district_window_layer', 'Zoom-ladder migration step 2 (docs/architecture/atlas-zoom-ladder-t1143.md SS8). build_district_window_layer''s cell loop (layer_proxy.rs:388) is serial today; parallelize with row/column chunked par_iter on the existing Rayon pool (the GenerationQueue already runs windows on Rayon — this parallelizes WITHIN a window). Needed before quarter windows: n=32-extent at quarter granularity is 16,384 cells (~20-23ms serial uncapped, design SS2 table). Determinism: chunk boundaries must not change output — cells are independent by construction (derive_at_metres is pure), assert with a serial-vs-parallel golden comparison.', 'in_progress', 'medium', NULL, 'server', 'D-226', '2026-07-21 19:51:17.193', '2026-07-21 21:50:03.779', NULL, '83dd5b89f5243a55ee7db80084b600a1', 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 ('06FRCET0RKD7ED9D9RY5Q99RQG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'derive_at_metres extraction + min_wavelength octave cutoff in enveloped_fbm', 'Zoom-ladder migration step 1 (docs/architecture/atlas-zoom-ladder-t1143.md SS8; keystone for every rung). Extract derive_district''s fractional-metres interior (district_profile.rs:1426+ already computes px/py/world_x_m/world_y_m/lat_deg from the integer DistrictPos) into derive_at_metres(seed, body, wx, wy, min_wl); derive_district becomes a thin integer-quantizing wrapper — bit-identical output for existing callers is the acceptance test (golden harness must not move). Add min_wavelength_m: f64 to enveloped_fbm (detail_scatter.rs:137-176) and the terrain_detail/voxel_relief const-octave paths: truncate octaves below the cutoff; evaluate Dudley''s amplitude-fade-near-cutoff refinement vs hard truncate (pop risk as sampling density crosses an octave boundary) and pick with a visual A/B. Re-benchmark derive cost with the cutoff active at district and quarter spacings — the design doc''s numbers are for the UNMODIFIED pipeline and are marked contested (SS2 status note + Troblum R-findings).', 'review', 'high', NULL, 'server', 'D-226', '2026-07-21 19:50:57.988', '2026-07-21 22:28:45.442', NULL, '7957a3bbe9d925acf2712b7abee454c9', 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 ('06FRCEV8Q32C2FW9Y6733419DG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'window_granularity + window_min_wl_m on the district_window carrier (five touch points + aliasing test)', 'Zoom-ladder migration step 3 (docs/architecture/atlas-zoom-ladder-t1143.md SS3). serde-default window_granularity: u32 (1=district, 4=quarter) and window_min_wl_m: u32 (0=off) on AtlasLayerRequest — the T-1124 SS1 additive pattern, no sixth demux shape. The design pass flags this as a five-touch-point coordinated change and mandates the test: server DistrictWindowLayer echo tuple (center, n, granularity), server FIFO-256 window cache key (layer_proxy.rs:308), server per-connection coalescing key (gen_queue.rs:221-224), client request struct, client 24-entry LRU key (atlas_window_cache.gd:33-38). MANDATORY regression test: granularity-4 and granularity-1 requests at identical (center, n) must produce DISTINCT cache entries on BOTH ends — a missed key (worst: client) silently aliases quarter data onto district slots with no error. Quarter derivation mode = Option B full reclassification at 512m spacing (design SS2), served through the same queue; per-request cell cap per the capped-density rule.', 'review', 'high', NULL, 'server', 'D-226', '2026-07-21 19:51:08.216', '2026-07-21 22:28:45.449', NULL, 'cb129733c2898a543d78bbc128502406', 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 ('06FRCEWBS5RC5YET0SMSPZ1E9W', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Chunked par_iter for build_district_window_layer', 'Zoom-ladder migration step 2 (docs/architecture/atlas-zoom-ladder-t1143.md SS8). build_district_window_layer''s cell loop (layer_proxy.rs:388) is serial today; parallelize with row/column chunked par_iter on the existing Rayon pool (the GenerationQueue already runs windows on Rayon — this parallelizes WITHIN a window). Needed before quarter windows: n=32-extent at quarter granularity is 16,384 cells (~20-23ms serial uncapped, design SS2 table). Determinism: chunk boundaries must not change output — cells are independent by construction (derive_at_metres is pure), assert with a serial-vs-parallel golden comparison.', 'review', 'medium', NULL, 'server', 'D-226', '2026-07-21 19:51:17.193', '2026-07-21 22:28:45.450', NULL, '22ca4bb2fcdce1e1bf6b53e33d3069f8', 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 ('06FRCET0RKD7ED9D9RY5Q99RQG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'derive_at_metres extraction + min_wavelength octave cutoff in enveloped_fbm', 'Zoom-ladder migration step 1 (docs/architecture/atlas-zoom-ladder-t1143.md SS8; keystone for every rung). Extract derive_district''s fractional-metres interior (district_profile.rs:1426+ already computes px/py/world_x_m/world_y_m/lat_deg from the integer DistrictPos) into derive_at_metres(seed, body, wx, wy, min_wl); derive_district becomes a thin integer-quantizing wrapper — bit-identical output for existing callers is the acceptance test (golden harness must not move). Add min_wavelength_m: f64 to enveloped_fbm (detail_scatter.rs:137-176) and the terrain_detail/voxel_relief const-octave paths: truncate octaves below the cutoff; evaluate Dudley''s amplitude-fade-near-cutoff refinement vs hard truncate (pop risk as sampling density crosses an octave boundary) and pick with a visual A/B. Re-benchmark derive cost with the cutoff active at district and quarter spacings — the design doc''s numbers are for the UNMODIFIED pipeline and are marked contested (SS2 status note + Troblum R-findings).', 'done', 'high', NULL, 'server', 'D-226', '2026-07-21 19:50:57.988', '2026-07-21 22:54:32.382', NULL, 'cc114d00e8ac81437b94964cae9c2d50', 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 ('06FRCEV8Q32C2FW9Y6733419DG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'window_granularity + window_min_wl_m on the district_window carrier (five touch points + aliasing test)', 'Zoom-ladder migration step 3 (docs/architecture/atlas-zoom-ladder-t1143.md SS3). serde-default window_granularity: u32 (1=district, 4=quarter) and window_min_wl_m: u32 (0=off) on AtlasLayerRequest — the T-1124 SS1 additive pattern, no sixth demux shape. The design pass flags this as a five-touch-point coordinated change and mandates the test: server DistrictWindowLayer echo tuple (center, n, granularity), server FIFO-256 window cache key (layer_proxy.rs:308), server per-connection coalescing key (gen_queue.rs:221-224), client request struct, client 24-entry LRU key (atlas_window_cache.gd:33-38). MANDATORY regression test: granularity-4 and granularity-1 requests at identical (center, n) must produce DISTINCT cache entries on BOTH ends — a missed key (worst: client) silently aliases quarter data onto district slots with no error. Quarter derivation mode = Option B full reclassification at 512m spacing (design SS2), served through the same queue; per-request cell cap per the capped-density rule.', 'done', 'high', NULL, 'server', 'D-226', '2026-07-21 19:51:08.216', '2026-07-21 22:54:32.389', NULL, '425a3cfffcc90cc3563b1e8382466828', 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 ('06FRCEWBS5RC5YET0SMSPZ1E9W', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Chunked par_iter for build_district_window_layer', 'Zoom-ladder migration step 2 (docs/architecture/atlas-zoom-ladder-t1143.md SS8). build_district_window_layer''s cell loop (layer_proxy.rs:388) is serial today; parallelize with row/column chunked par_iter on the existing Rayon pool (the GenerationQueue already runs windows on Rayon — this parallelizes WITHIN a window). Needed before quarter windows: n=32-extent at quarter granularity is 16,384 cells (~20-23ms serial uncapped, design SS2 table). Determinism: chunk boundaries must not change output — cells are independent by construction (derive_at_metres is pure), assert with a serial-vs-parallel golden comparison.', 'done', 'medium', NULL, 'server', 'D-226', '2026-07-21 19:51:17.193', '2026-07-21 22:54:32.390', NULL, 'e9c9613ad25cfbe7762ecc8393d7ecf6', 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 ('06FRCEXHYP8RVBPB8QNZHTNTR0', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Derived planetary rung — progressive capped-density tiling; AtlasViewer heightmap texture retires', 'Zoom-ladder migration step 4 (docs/architecture/atlas-zoom-ladder-t1143.md SS4/SS8) under Jeroen''s carrier ruling (2026-07-21): the orbital view becomes fully derived output served as PROGRESSIVE CAPPED-DENSITY TILING on the generalized district_window carrier at coarse granularity (region-band octaves, region_baseline_at_district blend path — no invent_primitives at orbital wavelengths) — no dense raster wire shape, no tagged-envelope migration. Client: AtlasViewer''s direct heightmap-texture display path retires IN THE SAME CHANGE that the derived orbital view lands (never strand the view); heightmap PNGs/systems.db raster become invisible derivation input only (D-166 corollary; import_heightmaps stays as input storage). Carries design risks R1 (planetary cost story is stacked unmeasured extrapolations — measure first), R2 (stepped categorical fields at orbital scale), R5 (granularity param cannot express coarser-than-district — needs the enum/log-scale redesign before this rung). Blocked by the granularity carrier and derive_at_metres steps.', 'in_progress', 'high', NULL, 'server', 'D-226', '2026-07-21 19:51:26.966', '2026-07-22 08:00:52.300', NULL, '76b4f0da254fcfc6782fc8929ae09c61', 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 ('06FRCEZ47J16E3XKE957HGR8H4', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Continuous cursor-anchored zoom ladder in the client — rung selection, progressive refinement, seam zoom with full-zoom-out reset', 'Zoom-ladder migration step 5 (docs/architecture/atlas-zoom-ladder-t1143.md SS5/SS8) under Jeroen''s seam ruling (2026-07-21): wheel zoom is CONTINUOUS and CURSOR-ANCHORED from the orbital frame down through regional granularities (supersedes the click-through-only entry; D-013''s ''zoom gesture owns spatial descent'' restored for this seam), with the HARD CONDITION that a full zoom-out resets to the original canonical planetary frame and location (the fixed orbital framing is the ladder''s top rest state — no drifted pan state at the top). Rung-selection rule: sample spacing E/C (world extent per canvas px); request the coarsest rung whose spacing <= 2x(E/C). Progressive refinement: hold the coarse composite, fetch finer granularity, sharpen in place (Araminta SS6: no hue step, no layout jump, coastline stable across rungs — acceptance criteria in the doc). Request coalescing across zoom gestures; T-1138''s click-through + reticle retire in the same change. Encoding continuity per SS6 (one colorizer family; hue-average rule ties to the reclassification mode). Blocked by the granularity carrier; the planetary top rung can land before or after (degrade gracefully to current fixed orbital view).', 'in_progress', 'high', NULL, 'client', 'D-226', '2026-07-21 19:51:39.836', '2026-07-22 08:00:52.307', NULL, '604a735cc5c9629c6a06599ff0c7a9cf', 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 ('06FRJD57678SVWD7XTSV9E3MSW', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Port whole-body Atlas overlays onto the zoom ladder (markers, city sidebar, POL/POP/gen overlays)', 'T-1153/T-1152 retired the AtlasViewer heightmap screen as the atlas entry; its 17-entry overlay surface did NOT carry to the Region-rung ladder view (AtlasWindowViewer has only gen_dw_temp/moisture/veg). Deferred wholesale by Stig with rationale: these overlays key off markers.json/city-names/road-graph/settlement data the DistrictWindowLayer wire shape does not carry — porting is a per-overlay design + wire question (at what rung does a settlement marker fade in? does POL make sense over a Region composite?), not a mechanical copy. Deferred set: city markers + click-to-inspect city data sidebar; roads/rail/settlements/quarter-footprint generation overlays (gen_l1_rivers/basins/attractors, gen_district, gen_l2_roads, gen_l3_settlements, gen_region_grid, gen_l4_quarters); political zones, population density, corp presence, production/stockpile toggles, gate markers, named features. The retired code survives intact and unreachable on atlas_viewer.gd — reuse it or retire it explicitly per overlay. Needs Araminta (per-rung affordance/fade rules) + Tyre/Dudley (which overlays need new wire fields vs client-side joins). Until this lands the ladder view is terrain-only — a real capability regression vs the old planetary screen, accepted knowingly in the T-1152/T-1153 batch.', 'backlog', 'high', NULL, 'client', 'D-226', '2026-07-22 09:42:37.105', '2026-07-22 09:42:37.105', NULL, 'b87a9969d7d2b1c22e56cf5fe8942c78', 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 ('06FRJD6EKPBQE08NA8VHP692W8', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Redesign the atlas visual-capture goldens for the zoom ladder (visual_scenarios/visual_capture)', 'T-1153 broke the T-1120/T-1121 golden capture harness: client/tests/visual_scenarios.gd and visual_capture.gd call app._regional_screen._viewer.get_heightmap_texture()/show_body() — both crash against AtlasWindowViewer (regional_screen now wraps the ladder viewer; AtlasViewer is unreachable from nav). Confirmed NOT wired into tests/run-godot or the pre-push hook (no reference in either), so nothing blocks, but the 12 curated atlas_* goldens and the --atlas-matrix capture path are dead until redesigned for the ladder (capture at defined rungs/zooms instead of the fixed heightmap frame; the new canonical orbital frame is the natural replacement for the old whole-body shot). Also decide the fate of the retired AtlasViewer code these harnesses were the last live consumers of — likely deletable once T-1156 (overlay port) settles what carries forward.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-22 09:42:47.197', '2026-07-22 09:42:47.197', NULL, '5393a8aede7343b10dddc1edd36ac8c0', 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 ('06FRJD6EKPBQE08NA8VHP692W8', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Redesign the atlas visual-capture goldens for the zoom ladder (visual_scenarios/visual_capture)', 'T-1153 broke the T-1120/T-1121 golden capture harness: client/tests/visual_scenarios.gd and visual_capture.gd call app._regional_screen._viewer.get_heightmap_texture()/show_body() — both crash against AtlasWindowViewer (regional_screen now wraps the ladder viewer; AtlasViewer is unreachable from nav). Confirmed NOT wired into tests/run-godot or the pre-push hook (no reference in either), so nothing blocks, but the 12 curated atlas_* goldens and the --atlas-matrix capture path are dead until redesigned for the ladder (capture at defined rungs/zooms instead of the fixed heightmap frame; the new canonical orbital frame is the natural replacement for the old whole-body shot). Also decide the fate of the retired AtlasViewer code these harnesses were the last live consumers of — likely deletable once T-1156 (overlay port) settles what carries forward.
Live round 4 addendum: client/tests/test_atlas_window_overlay_draw_smoke.gd (3 scenarios -> now 2 after removing a flaky harness-sanity check: single-window draw + tile-mosaic draw, both asserting real composited pixels via SubViewport render) is a standalone gdUnit file with its own headless-skip guard, added because this redesign hadn''t landed yet. When this ticket lands, migrate its 2 scenarios into the redesigned tests/visual.json harness (folding the standalone file away) for consistency with the rest of the real-rendering test suite — see docs/DEVOPS.md''s ''Real-rendering test exception'' note for the interim rationale.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-22 09:42:47.197', '2026-07-22 12:37:50.448', NULL, '68dc774b820b4edcf41f6dee568844e0', 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 ('06FRCEXHYP8RVBPB8QNZHTNTR0', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Derived planetary rung — progressive capped-density tiling; AtlasViewer heightmap texture retires', 'Zoom-ladder migration step 4 (docs/architecture/atlas-zoom-ladder-t1143.md SS4/SS8) under Jeroen''s carrier ruling (2026-07-21): the orbital view becomes fully derived output served as PROGRESSIVE CAPPED-DENSITY TILING on the generalized district_window carrier at coarse granularity (region-band octaves, region_baseline_at_district blend path — no invent_primitives at orbital wavelengths) — no dense raster wire shape, no tagged-envelope migration. Client: AtlasViewer''s direct heightmap-texture display path retires IN THE SAME CHANGE that the derived orbital view lands (never strand the view); heightmap PNGs/systems.db raster become invisible derivation input only (D-166 corollary; import_heightmaps stays as input storage). Carries design risks R1 (planetary cost story is stacked unmeasured extrapolations — measure first), R2 (stepped categorical fields at orbital scale), R5 (granularity param cannot express coarser-than-district — needs the enum/log-scale redesign before this rung). Blocked by the granularity carrier and derive_at_metres steps.', 'review', 'high', NULL, 'server', 'D-226', '2026-07-21 19:51:26.966', '2026-07-22 14:50:50.780', NULL, '5b3e98c8a8d968ad822f0e013df654f6', 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 ('06FRCEZ47J16E3XKE957HGR8H4', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Continuous cursor-anchored zoom ladder in the client — rung selection, progressive refinement, seam zoom with full-zoom-out reset', 'Zoom-ladder migration step 5 (docs/architecture/atlas-zoom-ladder-t1143.md SS5/SS8) under Jeroen''s seam ruling (2026-07-21): wheel zoom is CONTINUOUS and CURSOR-ANCHORED from the orbital frame down through regional granularities (supersedes the click-through-only entry; D-013''s ''zoom gesture owns spatial descent'' restored for this seam), with the HARD CONDITION that a full zoom-out resets to the original canonical planetary frame and location (the fixed orbital framing is the ladder''s top rest state — no drifted pan state at the top). Rung-selection rule: sample spacing E/C (world extent per canvas px); request the coarsest rung whose spacing <= 2x(E/C). Progressive refinement: hold the coarse composite, fetch finer granularity, sharpen in place (Araminta SS6: no hue step, no layout jump, coastline stable across rungs — acceptance criteria in the doc). Request coalescing across zoom gestures; T-1138''s click-through + reticle retire in the same change. Encoding continuity per SS6 (one colorizer family; hue-average rule ties to the reclassification mode). Blocked by the granularity carrier; the planetary top rung can land before or after (degrade gracefully to current fixed orbital view).', 'review', 'high', NULL, 'client', 'D-226', '2026-07-21 19:51:39.836', '2026-07-22 14:50:50.784', NULL, '95ddb5bb1bf7f46e88848a9aacba1fef', 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 ('06FRMPSM9GSWK586N5GBP2N22G', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Decompose atlas_window_viewer.gd — extract the canonical-frame + rung-reselection cluster', 'PR #192 review (Tyre): the viewer sits at exactly gdlint''s 1000-line cap, twice trimmed to fit, and fuses three separable responsibilities: (a) input handling (_gui_input/_process/_apply_pan_delta/edge-scroll), (b) request orchestration + rung reselection (_maybe_reselect_rung/_maybe_refloat_window), (c) the canonical-frame state machine (_zoom_at zoom floor/_is_at_canonical_frame/_maybe_reset_to_canonical_frame/_canonical_fit_zoom). All six live fix rounds of the T-1152/T-1153 batch traced to held-state/transform/lifetime coupling in this file — extract cluster (c) (+ likely (b)) into its own class BEFORE the next change breaches the cap and forces either a deadline extraction or comment-gutting that loses the hard-won live-round rationale comments. The tile-set (atlas_window_tile_set.gd) and geometry (atlas_window_geometry.gd) extractions are the precedent. Behavior-preserving refactor: the zoom-ladder suites (44+122+20 tests) are the safety net.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-22 15:04:20.812', '2026-07-22 15:04:20.812', NULL, '5a2367963b9e7bbe2e29bdf6def061f2', 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 ('06FRMPTY517G7ZBMPAPMMCCMK8', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Retire the legacy window_granularity u32 wire field', 'PR #192 review (Tyre): window_granularity_v2 (the WindowGranularity enum) fully shadows the legacy u32 field — the server always echoes both, and the only conceivable legacy readers are pre-T-1152 clients, which do not exist (single-repo client/server pair, no external clients). Retire the u32 field + the WINDOW_GRANULARITY_REGION_KEY sentinel machinery from AtlasLayerRequest/DistrictWindowLayer once confirmed nothing reads it: server resolve_window_granularity legacy path, client encode path, the sentinel echo, and the five-touch-point key components that still carry the legacy value alongside the enum. Scheduled in the D-226 amendment refinement-semantics note (PR #192). Do AFTER T-1153''s follow-ups settle — no urgency, the dual-echo is cheap; the win is contract simplicity.', 'backlog', 'low', NULL, 'server', NULL, '2026-07-22 15:04:31.528', '2026-07-22 15:04:31.528', NULL, '48f7426374e9306223f2ea12dc606a9f', 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 ('06FRMPWFRCD3M8282XJ01YWGX0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Orbital coastline-position audit — derive_orbital_at_metres skips coast_warp_px', 'PR #192 review (Araminta, follow-up she rated real-not-hypothetical): derive_orbital_at_metres (district_profile.rs:1617-1692) samples the raw heightmap ocean mask via bilinear/bilinear_bool with NO coast_warp_px, while District/Quarter both apply coast_warp_px on the same mask (district_profile.rs:1139). The orbital rung therefore draws a STRUCTURALLY DIFFERENT coastline position than every finer rung — not a coarser sample of the same line — echoing the mandate''s own Lendel evidence clause (the D-166 amendment''s headline failure mode) at the orbital-to-district seam. Design doc §9 R1/R3 flagged the class as unresolved; §6 never gated T-1152/T-1153 on it, hence follow-up not blocker. Audit: quantify the divergence at real coasts (the wrap-warped line vs raw-bilinear line at region spacing), then either apply coast_warp_px at orbital sampling positions (cost: one warp eval per coastal cell — measure) or record an accepted-divergence rationale with Araminta''s sign-off. Related: Araminta also flagged the capture set never demonstrates Quarter-vs-District visual differentiation (flat plains read flat, sanctioned) — the audit''s verification captures should use a coastal/mountainous Quarter zoom to validate the finest rung at the same time.', 'backlog', 'high', NULL, 'server', 'D-226', '2026-07-22 15:04:44.227', '2026-07-22 15:04:44.227', NULL, '699464753c6a0c03bc014ce1f16e653f', 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 ('06FRMPY76TYY76FSBV2Y5P8QHM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Per-rung composite filter policy — NEAREST at sparse rungs, LINEAR where density earns it', 'PR #192 review (Araminta, follow-up): COMPOSITE_SMOOTH''s GPU bilinear stretch at Region rung reads as a near-featureless soft gradient at 204.8 km/cell (capture 02-region-mid.png) — technically honest LoD (interpolating BETWEEN real derived samples, letter-compliant with ''never magnified interpolation'') but visually indistinguishable from the coarse-composite-stretched experience the mandate was written to kill: the spirit is violated at sparse rungs. Implement a per-rung filter policy: crisp NEAREST at Region/orbital (the COMPOSITE_SMOOTH := false path already exists byte-compatible per the file''s own doc — make it per-rung runtime instead of compile-time), keep LINEAR at District/Quarter where cell density reads as texture rather than smoothing-over-absence. Araminta owns the rung-by-rung call — get her sign-off on which rungs get which filter (and whether the threshold should key on on-screen px-per-cell rather than rung identity). Supersedes the remaining half of the old T-1155 COMPOSITE_SMOOTH-retirement framing — reconcile with that ticket when picking this up.', 'backlog', 'medium', NULL, 'client', 'D-226', '2026-07-22 15:04:58.422', '2026-07-22 15:04:58.422', NULL, '8c26e2fc6d557e675f04ca67b19f4c3a', 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 ('06FRCEXHYP8RVBPB8QNZHTNTR0', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Derived planetary rung — progressive capped-density tiling; AtlasViewer heightmap texture retires', 'Zoom-ladder migration step 4 (docs/architecture/atlas-zoom-ladder-t1143.md SS4/SS8) under Jeroen''s carrier ruling (2026-07-21): the orbital view becomes fully derived output served as PROGRESSIVE CAPPED-DENSITY TILING on the generalized district_window carrier at coarse granularity (region-band octaves, region_baseline_at_district blend path — no invent_primitives at orbital wavelengths) — no dense raster wire shape, no tagged-envelope migration. Client: AtlasViewer''s direct heightmap-texture display path retires IN THE SAME CHANGE that the derived orbital view lands (never strand the view); heightmap PNGs/systems.db raster become invisible derivation input only (D-166 corollary; import_heightmaps stays as input storage). Carries design risks R1 (planetary cost story is stacked unmeasured extrapolations — measure first), R2 (stepped categorical fields at orbital scale), R5 (granularity param cannot express coarser-than-district — needs the enum/log-scale redesign before this rung). Blocked by the granularity carrier and derive_at_metres steps.', 'done', 'high', NULL, 'server', 'D-226', '2026-07-21 19:51:26.966', '2026-07-22 15:40:43.466', NULL, '37248f58b4d8756f0d428174bc057658', 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 ('06FRCEZ47J16E3XKE957HGR8H4', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Continuous cursor-anchored zoom ladder in the client — rung selection, progressive refinement, seam zoom with full-zoom-out reset', 'Zoom-ladder migration step 5 (docs/architecture/atlas-zoom-ladder-t1143.md SS5/SS8) under Jeroen''s seam ruling (2026-07-21): wheel zoom is CONTINUOUS and CURSOR-ANCHORED from the orbital frame down through regional granularities (supersedes the click-through-only entry; D-013''s ''zoom gesture owns spatial descent'' restored for this seam), with the HARD CONDITION that a full zoom-out resets to the original canonical planetary frame and location (the fixed orbital framing is the ladder''s top rest state — no drifted pan state at the top). Rung-selection rule: sample spacing E/C (world extent per canvas px); request the coarsest rung whose spacing <= 2x(E/C). Progressive refinement: hold the coarse composite, fetch finer granularity, sharpen in place (Araminta SS6: no hue step, no layout jump, coastline stable across rungs — acceptance criteria in the doc). Request coalescing across zoom gestures; T-1138''s click-through + reticle retire in the same change. Encoding continuity per SS6 (one colorizer family; hue-average rule ties to the reclassification mode). Blocked by the granularity carrier; the planetary top rung can land before or after (degrade gracefully to current fixed orbital view).', 'done', 'high', NULL, 'client', 'D-226', '2026-07-21 19:51:39.836', '2026-07-22 15:40:43.468', NULL, 'dddccda1b100736b34f6f67b93470d00', 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 ('06FRNAHD35YPZHJWMJCRZK62Z4', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Extend deterministic invention octaves into the Atlas window path — coastline crinkle and terrain texture at rung scale', 'Jeroen, 2026-07-22, after first hands-on with the shipped ladder: ''zero detail on lower levels, just a green field, and the green-blue transition is a fade — it should be a more detailed coastline.'' Root cause confirmed by octave audit: the window classification consumes terrain_detail whose octaves span 32768-4096m (OCTAVE_WAVELENGTHS_M, detail_scatter.rs:33) and coast_invention whose warp octaves span 262144-16384m (WARP_OCTAVE_WAVELENGTHS_M, coast_invention.rs:56) — so at Quarter spacing (512m) the sampled field is genuinely smooth (~8 cells per finest terrain wave; ~32 per finest coast wave). The ladder''s TRANSPORT reaches 512m but its CONTENT floor is 4km/16km. The finer bands already exist (VOXEL_OCTAVE_WAVELENGTHS_M 1024-128m ''the rolling hills a walking character navigates by''; MOSAIC 64-8m) but feed only the Phase-5 voxel path — never the window derivation. Work: (a) extend coast_warp octaves below 16km down through quarter-visible wavelengths (bays/headlands/islets appearing as you zoom — THE headline visual ask) with min_wavelength_m cutoff discipline per rung; (b) feed the sub-district relief band into Quarter-rung classification (derive_at_metres already takes the cutoff — decide which bands activate per rung and re-bench; the T-1149 bench harness is the tool); (c) re-tune MIN_WL_BANDS_M to match the extended ladder; (d) determinism goldens updated deliberately (this CHANGES derived output — a golden-regeneration commit with rationale, not silent). Costs measured before merge per the T-1149 discipline. Visual acceptance: a coastal Quarter capture must show structurally more coastline detail than the same coast at District — the differentiation Araminta flagged as never yet demonstrated. Pairs with T-1161 (NEAREST at sparse rungs) — together these answer both of Jeroen''s specific complaints.', 'backlog', 'high', NULL, 'server', 'D-226', '2026-07-22 16:30:36.314', '2026-07-22 16:30:36.314', NULL, 'c7e5dc14d58898081690dda1c1f8b7a2', 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 ('06FRNAHD35YPZHJWMJCRZK62Z4', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Extend deterministic invention octaves into the Atlas window path — coastline crinkle and terrain texture at rung scale', 'Jeroen, 2026-07-22, after first hands-on with the shipped ladder: ''zero detail on lower levels, just a green field, and the green-blue transition is a fade — it should be a more detailed coastline.'' Root cause confirmed by octave audit: the window classification consumes terrain_detail whose octaves span 32768-4096m (OCTAVE_WAVELENGTHS_M, detail_scatter.rs:33) and coast_invention whose warp octaves span 262144-16384m (WARP_OCTAVE_WAVELENGTHS_M, coast_invention.rs:56) — so at Quarter spacing (512m) the sampled field is genuinely smooth (~8 cells per finest terrain wave; ~32 per finest coast wave). The ladder''s TRANSPORT reaches 512m but its CONTENT floor is 4km/16km. The finer bands already exist (VOXEL_OCTAVE_WAVELENGTHS_M 1024-128m ''the rolling hills a walking character navigates by''; MOSAIC 64-8m) but feed only the Phase-5 voxel path — never the window derivation. Work: (a) extend coast_warp octaves below 16km down through quarter-visible wavelengths (bays/headlands/islets appearing as you zoom — THE headline visual ask) with min_wavelength_m cutoff discipline per rung; (b) feed the sub-district relief band into Quarter-rung classification (derive_at_metres already takes the cutoff — decide which bands activate per rung and re-bench; the T-1149 bench harness is the tool); (c) re-tune MIN_WL_BANDS_M to match the extended ladder; (d) determinism goldens updated deliberately (this CHANGES derived output — a golden-regeneration commit with rationale, not silent). Costs measured before merge per the T-1149 discipline. Visual acceptance: a coastal Quarter capture must show structurally more coastline detail than the same coast at District — the differentiation Araminta flagged as never yet demonstrated. Pairs with T-1161 (NEAREST at sparse rungs) — together these answer both of Jeroen''s specific complaints.
SCOPE CLARIFICATION (Jeroen, 2026-07-22): the current aim is the NATURE layer — geo + wild growth — per D-166''s outside-in order; cities/settlements stay uncolored for now. This ticket''s octave extension therefore covers not just coast + relief but VEGETATION PATCHINESS as spatial content: forests as massifs at region scale resolving to distinct woods (district), copses and clearings (quarter), desert/steppe transitions as shapes — the invention fields (detail_scatter character/envelope) driving per-rung vegetation patterning in the BASE layer rather than uniform per-cell class tint. His words: ''even the nature layer should produce rivers, forests, copses, bushes, deserts, elevation.'' Rivers are T-1156''s nature half (see its re-scope); this ticket owns the continuous-field content (coastline, relief, vegetation/moisture patchiness).', 'backlog', 'high', NULL, 'server', 'D-226', '2026-07-22 16:30:36.314', '2026-07-22 16:32:47.938', NULL, '1fa1c188d3de50d1de34fd7446885327', 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 ('06FRJD57678SVWD7XTSV9E3MSW', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Port whole-body Atlas overlays onto the zoom ladder (markers, city sidebar, POL/POP/gen overlays)', 'T-1153/T-1152 retired the AtlasViewer heightmap screen as the atlas entry; its 17-entry overlay surface did NOT carry to the Region-rung ladder view (AtlasWindowViewer has only gen_dw_temp/moisture/veg). Deferred wholesale by Stig with rationale: these overlays key off markers.json/city-names/road-graph/settlement data the DistrictWindowLayer wire shape does not carry — porting is a per-overlay design + wire question (at what rung does a settlement marker fade in? does POL make sense over a Region composite?), not a mechanical copy. Deferred set: city markers + click-to-inspect city data sidebar; roads/rail/settlements/quarter-footprint generation overlays (gen_l1_rivers/basins/attractors, gen_district, gen_l2_roads, gen_l3_settlements, gen_region_grid, gen_l4_quarters); political zones, population density, corp presence, production/stockpile toggles, gate markers, named features. The retired code survives intact and unreachable on atlas_viewer.gd — reuse it or retire it explicitly per overlay. Needs Araminta (per-rung affordance/fade rules) + Tyre/Dudley (which overlays need new wire fields vs client-side joins). Until this lands the ladder view is terrain-only — a real capability regression vs the old planetary screen, accepted knowingly in the T-1152/T-1153 batch.
RE-SCOPED (Jeroen, 2026-07-22): ''we are aiming for not coloring in the cities at the moment — the nature layer (geo and wild growth) first,'' matching D-166''s outside-in order. SPLIT this ticket''s overlay set into two waves. WAVE 1 (this ticket, promoted): the NATURE overlays — gen_l1_rivers/basins/attractors onto the ladder (rivers as per-rung refining features: trunk rivers at Region, tributaries at District, streams at Quarter — the wire/derivation question of serving river skeletons windowed is the real work), plus named natural features (rivers/oceans/ranges labels) if cheap. WAVE 2 (deferred until the nature layer stands, per Jeroen): everything civilizational — city markers + sidebar, settlements/quarters footprints, roads/rail, POL/POP/CRP/gate markers. Do not port wave 2 with wave 1.', 'backlog', 'high', NULL, 'client', 'D-226', '2026-07-22 09:42:37.105', '2026-07-22 16:32:58.189', NULL, '0e72ac69b0183e078d966b92aa6cf5be', 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 ('06FRPCJ4KD6SB9TB8TH9SJH1E0', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Cold first launch starves: whole-response Pending never reaches the retry path', 'Jeroen''s make atlas launch showed a black map zooming into any body. Root cause (after four disproven theories, each killed by verification): a stone-cold body answers the first window request with whole-response status Pending (only the whole-body cache-hit branch sets Ready — layer_proxy.rs serve path), and atlas_window_request.gd on_response()''s first check (status != Ready -> return) swallowed it before the retry machinery could run — retries frozen at 0 forever, DERIVING state never resolves. Second connections worked by luck (first connection''s request warms the whole-body cache -> Ready -> healthy path), which also masked the bug in every warm-server verification drive. Affected BOTH tile-mosaic and single-window first descents. Fix (branch ladder-cold-start): status-first branching per the atlas_generation_proxy reference shape (Pending -> retry; NotFound/Error -> immediate give-up, replacing the elapsed-retries ceiling), deterministic exponential backoff + per-tile stagger, wire-accurate regressions through the real fan-out plus single-window mirrors, Dudley''s connection-zero bridge_tcp pin, self-healing tile repaint, ImplantApp re-entry guard + immediate-free panel clears, legend re-fit, visible DERIVING TERRAIN state, and make atlas moving to a RELEASE server via SR_SERVER_BIN. Cold acceptance verified live: first connection, fresh release server — DERIVING at 2s, terrain painting tile-by-tile by 5s, all six tiles converged, legend one-legend-tall.', 'backlog', 'critical', NULL, 'client', 'D-226', '2026-07-22 18:59:15.227', '2026-07-22 18:59:15.227', NULL, '413332e77799ba02262b7f18849d4add', 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 ('06FRPCJ4KD6SB9TB8TH9SJH1E0', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Cold first launch starves: whole-response Pending never reaches the retry path', 'Jeroen''s make atlas launch showed a black map zooming into any body. Root cause (after four disproven theories, each killed by verification): a stone-cold body answers the first window request with whole-response status Pending (only the whole-body cache-hit branch sets Ready — layer_proxy.rs serve path), and atlas_window_request.gd on_response()''s first check (status != Ready -> return) swallowed it before the retry machinery could run — retries frozen at 0 forever, DERIVING state never resolves. Second connections worked by luck (first connection''s request warms the whole-body cache -> Ready -> healthy path), which also masked the bug in every warm-server verification drive. Affected BOTH tile-mosaic and single-window first descents. Fix (branch ladder-cold-start): status-first branching per the atlas_generation_proxy reference shape (Pending -> retry; NotFound/Error -> immediate give-up, replacing the elapsed-retries ceiling), deterministic exponential backoff + per-tile stagger, wire-accurate regressions through the real fan-out plus single-window mirrors, Dudley''s connection-zero bridge_tcp pin, self-healing tile repaint, ImplantApp re-entry guard + immediate-free panel clears, legend re-fit, visible DERIVING TERRAIN state, and make atlas moving to a RELEASE server via SR_SERVER_BIN. Cold acceptance verified live: first connection, fresh release server — DERIVING at 2s, terrain painting tile-by-tile by 5s, all six tiles converged, legend one-legend-tall.', 'review', 'critical', NULL, 'client', 'D-226', '2026-07-22 18:59:15.227', '2026-07-22 18:59:22.418', NULL, '624cfd2b92d5dfdaf4626b47b160525e', 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 ('06FRPEGNSG7VVR602MTT4VDCEW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Tiled orbital terminal-recovery — exhausted DERIVING state has no retry path', 'PR #193 review (Tyre, finding 2): after MAX_RETRIES(30)/~110s of genuine unbroken Pending in the tiled orbital rest state, the player is permanently stuck at DERIVING TERRAIN: pan is a no-op while tiled (_maybe_refloat_window early-returns), zoom-out is floored at the canonical frame (edge-triggered reset never re-fires), and regional_screen.enter() no-ops same-body re-entry — recovery only via zooming IN across the Region boundary or visiting a different body. Pathological trigger (110s of continuous Pending) and the release-server make-atlas change makes it near-impossible on the sanctioned path, but it is a real dead-end. Fix options per the review: a manual retry affordance on the DERIVING state, or same-body re-entry re-issues tile requests when has_pending_tiles() and every tile request is exhausted-non-pending. Single-window is already fully recoverable (any pan/zoom resets the retry state) — this is tile-mode only.', 'backlog', 'medium', NULL, 'client', 'D-226', '2026-07-22 19:07:47.532', '2026-07-22 19:07:47.532', NULL, '92b47ce81c968400b47583826c7505b4', 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 ('06FRPEJ6TVFXRBEZPSP40677G4', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Root-cause the dropped tile-arrival repaint edge (self-heal poll is compensating)', 'PR #193 review (Tyre, finding 3): the cold-start fix ships a bounded self-healing _process redraw (atlas_window_viewer.gd ~887: redraw viewer+overlay every frame while _tile_mode and has_pending_tiles()) — architecturally acceptable (goes quiet the instant tiles complete, spy-tested) but honestly a poll compensating for a signal-driven repaint contract the team could not fully trust under cold-server timing: some queue_redraw edge was observed dropped in live rounds (black mosaic with correct data/geometry/textures) and was never exactly pinned. Root-cause the dropped edge (candidates from the live dossier: tile_ready ordering vs draw scheduling, overlay-vs-viewer redraw routing under deferred frees, RenderingServer timing on texture upload) so the poll can be retired and so other renderers do not copy the poll pattern as gospel. Keep the self-heal until the edge is pinned AND a regression exists.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-22 19:08:00.086', '2026-07-22 19:08:00.086', NULL, 'a7911cd3eedb2d309d5f128e8c108754', 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 ('06FRPF77A790HCWZH78AVZ7T8M', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Scripted cold-launch integration test tier (fresh server, connection zero, tiles arrive, non-black frame)', 'PR #193 review (Tyre, finding 1): T-1163 (every cold first launch black) was only findable by a human launching the real stack cold — ~3500 unit tests were green with the bug live, and every scripted verification drive used a warm server (second connection), the exact luck that masked the bug. File the missing test tier: a scripted cold-launch integration test that (1) spawns a FRESH release server process (never pre-warmed — build via make build-server-release, SR_SERVER_BIN override), (2) drives the first-ever client connection through the real Atlas descent into a body (SceneTree -s script, atlas_standalone.tscn, nav.push + _on_body_selected — the live-drive harness shape in tooling/, untyped autoload refs per the harness conventions), (3) asserts all tiles arrive within a deadline (tile_set.is_fully_arrived() or per-tile window non-null), and (4) asserts a non-black rendered frame (capture via root.get_texture().get_image(), assert mean luminance above a floor). Compose from existing pieces: the visual_capture/server_process helpers and the run-visual entry. This is the tier that would have caught T-1163 on day one; it also becomes the permanent home for the cold acceptance run /pr-review now performs by hand. Not a gate hook initially — a make target (make test-cold-launch) runnable on demand and in review.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-22 19:10:52.241', '2026-07-22 19:10:52.241', NULL, '0ad401e6728e37e8f499bc04b32cba9d', 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 ('06FRPCJ4KD6SB9TB8TH9SJH1E0', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Cold first launch starves: whole-response Pending never reaches the retry path', 'Jeroen''s make atlas launch showed a black map zooming into any body. Root cause (after four disproven theories, each killed by verification): a stone-cold body answers the first window request with whole-response status Pending (only the whole-body cache-hit branch sets Ready — layer_proxy.rs serve path), and atlas_window_request.gd on_response()''s first check (status != Ready -> return) swallowed it before the retry machinery could run — retries frozen at 0 forever, DERIVING state never resolves. Second connections worked by luck (first connection''s request warms the whole-body cache -> Ready -> healthy path), which also masked the bug in every warm-server verification drive. Affected BOTH tile-mosaic and single-window first descents. Fix (branch ladder-cold-start): status-first branching per the atlas_generation_proxy reference shape (Pending -> retry; NotFound/Error -> immediate give-up, replacing the elapsed-retries ceiling), deterministic exponential backoff + per-tile stagger, wire-accurate regressions through the real fan-out plus single-window mirrors, Dudley''s connection-zero bridge_tcp pin, self-healing tile repaint, ImplantApp re-entry guard + immediate-free panel clears, legend re-fit, visible DERIVING TERRAIN state, and make atlas moving to a RELEASE server via SR_SERVER_BIN. Cold acceptance verified live: first connection, fresh release server — DERIVING at 2s, terrain painting tile-by-tile by 5s, all six tiles converged, legend one-legend-tall.', 'done', 'critical', NULL, 'client', 'D-226', '2026-07-22 18:59:15.227', '2026-07-22 19:29:52.281', NULL, 'a106f9a9ccd78c7ccecc1e98e5e45862', 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 ('06FRNAHD35YPZHJWMJCRZK62Z4', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Extend deterministic invention octaves into the Atlas window path — coastline crinkle and terrain texture at rung scale', 'Jeroen, 2026-07-22, after first hands-on with the shipped ladder: ''zero detail on lower levels, just a green field, and the green-blue transition is a fade — it should be a more detailed coastline.'' Root cause confirmed by octave audit: the window classification consumes terrain_detail whose octaves span 32768-4096m (OCTAVE_WAVELENGTHS_M, detail_scatter.rs:33) and coast_invention whose warp octaves span 262144-16384m (WARP_OCTAVE_WAVELENGTHS_M, coast_invention.rs:56) — so at Quarter spacing (512m) the sampled field is genuinely smooth (~8 cells per finest terrain wave; ~32 per finest coast wave). The ladder''s TRANSPORT reaches 512m but its CONTENT floor is 4km/16km. The finer bands already exist (VOXEL_OCTAVE_WAVELENGTHS_M 1024-128m ''the rolling hills a walking character navigates by''; MOSAIC 64-8m) but feed only the Phase-5 voxel path — never the window derivation. Work: (a) extend coast_warp octaves below 16km down through quarter-visible wavelengths (bays/headlands/islets appearing as you zoom — THE headline visual ask) with min_wavelength_m cutoff discipline per rung; (b) feed the sub-district relief band into Quarter-rung classification (derive_at_metres already takes the cutoff — decide which bands activate per rung and re-bench; the T-1149 bench harness is the tool); (c) re-tune MIN_WL_BANDS_M to match the extended ladder; (d) determinism goldens updated deliberately (this CHANGES derived output — a golden-regeneration commit with rationale, not silent). Costs measured before merge per the T-1149 discipline. Visual acceptance: a coastal Quarter capture must show structurally more coastline detail than the same coast at District — the differentiation Araminta flagged as never yet demonstrated. Pairs with T-1161 (NEAREST at sparse rungs) — together these answer both of Jeroen''s specific complaints.
SCOPE CLARIFICATION (Jeroen, 2026-07-22): the current aim is the NATURE layer — geo + wild growth — per D-166''s outside-in order; cities/settlements stay uncolored for now. This ticket''s octave extension therefore covers not just coast + relief but VEGETATION PATCHINESS as spatial content: forests as massifs at region scale resolving to distinct woods (district), copses and clearings (quarter), desert/steppe transitions as shapes — the invention fields (detail_scatter character/envelope) driving per-rung vegetation patterning in the BASE layer rather than uniform per-cell class tint. His words: ''even the nature layer should produce rivers, forests, copses, bushes, deserts, elevation.'' Rivers are T-1156''s nature half (see its re-scope); this ticket owns the continuous-field content (coastline, relief, vegetation/moisture patchiness).', 'in_progress', 'high', NULL, 'server', 'D-226', '2026-07-22 16:30:36.314', '2026-07-22 20:24:43.745', NULL, '2db49e462687fc954e369b94f61f1e83', 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 ('06FRMPY76TYY76FSBV2Y5P8QHM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Per-rung composite filter policy — NEAREST at sparse rungs, LINEAR where density earns it', 'PR #192 review (Araminta, follow-up): COMPOSITE_SMOOTH''s GPU bilinear stretch at Region rung reads as a near-featureless soft gradient at 204.8 km/cell (capture 02-region-mid.png) — technically honest LoD (interpolating BETWEEN real derived samples, letter-compliant with ''never magnified interpolation'') but visually indistinguishable from the coarse-composite-stretched experience the mandate was written to kill: the spirit is violated at sparse rungs. Implement a per-rung filter policy: crisp NEAREST at Region/orbital (the COMPOSITE_SMOOTH := false path already exists byte-compatible per the file''s own doc — make it per-rung runtime instead of compile-time), keep LINEAR at District/Quarter where cell density reads as texture rather than smoothing-over-absence. Araminta owns the rung-by-rung call — get her sign-off on which rungs get which filter (and whether the threshold should key on on-screen px-per-cell rather than rung identity). Supersedes the remaining half of the old T-1155 COMPOSITE_SMOOTH-retirement framing — reconcile with that ticket when picking this up.', 'in_progress', 'medium', NULL, 'client', 'D-226', '2026-07-22 15:04:58.422', '2026-07-22 20:24:43.752', NULL, '1fccf6d354c85047254ac68098a44822', 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 ('06FRNAHD35YPZHJWMJCRZK62Z4', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Extend deterministic invention octaves into the Atlas window path — coastline crinkle and terrain texture at rung scale', 'Jeroen, 2026-07-22, after first hands-on with the shipped ladder: ''zero detail on lower levels, just a green field, and the green-blue transition is a fade — it should be a more detailed coastline.'' Root cause confirmed by octave audit: the window classification consumes terrain_detail whose octaves span 32768-4096m (OCTAVE_WAVELENGTHS_M, detail_scatter.rs:33) and coast_invention whose warp octaves span 262144-16384m (WARP_OCTAVE_WAVELENGTHS_M, coast_invention.rs:56) — so at Quarter spacing (512m) the sampled field is genuinely smooth (~8 cells per finest terrain wave; ~32 per finest coast wave). The ladder''s TRANSPORT reaches 512m but its CONTENT floor is 4km/16km. The finer bands already exist (VOXEL_OCTAVE_WAVELENGTHS_M 1024-128m ''the rolling hills a walking character navigates by''; MOSAIC 64-8m) but feed only the Phase-5 voxel path — never the window derivation. Work: (a) extend coast_warp octaves below 16km down through quarter-visible wavelengths (bays/headlands/islets appearing as you zoom — THE headline visual ask) with min_wavelength_m cutoff discipline per rung; (b) feed the sub-district relief band into Quarter-rung classification (derive_at_metres already takes the cutoff — decide which bands activate per rung and re-bench; the T-1149 bench harness is the tool); (c) re-tune MIN_WL_BANDS_M to match the extended ladder; (d) determinism goldens updated deliberately (this CHANGES derived output — a golden-regeneration commit with rationale, not silent). Costs measured before merge per the T-1149 discipline. Visual acceptance: a coastal Quarter capture must show structurally more coastline detail than the same coast at District — the differentiation Araminta flagged as never yet demonstrated. Pairs with T-1161 (NEAREST at sparse rungs) — together these answer both of Jeroen''s specific complaints.
SCOPE CLARIFICATION (Jeroen, 2026-07-22): the current aim is the NATURE layer — geo + wild growth — per D-166''s outside-in order; cities/settlements stay uncolored for now. This ticket''s octave extension therefore covers not just coast + relief but VEGETATION PATCHINESS as spatial content: forests as massifs at region scale resolving to distinct woods (district), copses and clearings (quarter), desert/steppe transitions as shapes — the invention fields (detail_scatter character/envelope) driving per-rung vegetation patterning in the BASE layer rather than uniform per-cell class tint. His words: ''even the nature layer should produce rivers, forests, copses, bushes, deserts, elevation.'' Rivers are T-1156''s nature half (see its re-scope); this ticket owns the continuous-field content (coastline, relief, vegetation/moisture patchiness).
REFINEMENT RESOLUTIONS (lead, 2026-07-22, from Si audit): (1) Bench baseline: none recorded anywhere — implementer runs server/tests/zoom_ladder_bench.rs (cargo test --release --test zoom_ladder_bench -- --ignored --nocapture) BEFORE any code change to establish the pre-change baseline, and again after; both sets of numbers go in the PR. (2) ''Goldens'' corrected: no golden file pins derive_at_metres/invent_primitives today (derivation_harness.json is voxel-chain; cascade_layer1.json is heightmap-river). Explicit scope: ADD a golden fixture for the window-derivation path (mirroring derivation_harness.rs''s pattern) so this and every future octave change gets a deliberate-regen commit discipline. (3) Vegetation patchiness confirmed NET-NEW, in scope (not split): today derive_vegetation (district_profile.rs:398-490) is pure per-cell classification with no positional field. Build the vegetation invention mechanism mirroring coast_invention.rs''s two-tier envelope+character model (Jeroen''s nature-first ruling is the direction; the tiered invention-field pattern is the established shape to imitate). Non-blocking notes: VOXEL octaves feeding Quarter classification does NOT conflict with D-226(d) — that ruling caps request granularity, not contributing wavelengths (coast warp already crosses scales the other way); MIN_WL_BANDS_M (layer_proxy.rs:415-421) needs new entries once the ladder extends, quantize_min_wl_m generalizes as-is.', 'in_progress', 'high', NULL, 'server', 'D-226', '2026-07-22 16:30:36.314', '2026-07-22 20:27:10.426', NULL, '8214e16a3533672f9be060e37410d066', 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 ('06FRQ9J882SWNAWV87YV393V78', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Clear the 27 pre-existing gdlint problems on main (client/scripts + client/ui)', 'Found during nature-octaves /pr-process (2026-07-22): gdlint over client/scripts/ + client/ui/ reports 27 problems on main (e.g. class-definitions-order in atlas screens like system_screen.gd:511-512). The pre-push gate runs the gdUnit suite but NOT gdlint, so this debt accumulates invisibly; /pr-process''s zero-warnings step only catches NEW warnings by diffing against main. Fix the 27 (or suppress with commented justification where intentional), then consider adding gdlint to the pre-push gate''s client path so the count stays at zero.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-22 21:05:58.336', '2026-07-22 21:05:58.336', NULL, '12ba8e9e1da015faf665138d7876ed54', 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 ('06FRNAHD35YPZHJWMJCRZK62Z4', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Extend deterministic invention octaves into the Atlas window path — coastline crinkle and terrain texture at rung scale', 'Jeroen, 2026-07-22, after first hands-on with the shipped ladder: ''zero detail on lower levels, just a green field, and the green-blue transition is a fade — it should be a more detailed coastline.'' Root cause confirmed by octave audit: the window classification consumes terrain_detail whose octaves span 32768-4096m (OCTAVE_WAVELENGTHS_M, detail_scatter.rs:33) and coast_invention whose warp octaves span 262144-16384m (WARP_OCTAVE_WAVELENGTHS_M, coast_invention.rs:56) — so at Quarter spacing (512m) the sampled field is genuinely smooth (~8 cells per finest terrain wave; ~32 per finest coast wave). The ladder''s TRANSPORT reaches 512m but its CONTENT floor is 4km/16km. The finer bands already exist (VOXEL_OCTAVE_WAVELENGTHS_M 1024-128m ''the rolling hills a walking character navigates by''; MOSAIC 64-8m) but feed only the Phase-5 voxel path — never the window derivation. Work: (a) extend coast_warp octaves below 16km down through quarter-visible wavelengths (bays/headlands/islets appearing as you zoom — THE headline visual ask) with min_wavelength_m cutoff discipline per rung; (b) feed the sub-district relief band into Quarter-rung classification (derive_at_metres already takes the cutoff — decide which bands activate per rung and re-bench; the T-1149 bench harness is the tool); (c) re-tune MIN_WL_BANDS_M to match the extended ladder; (d) determinism goldens updated deliberately (this CHANGES derived output — a golden-regeneration commit with rationale, not silent). Costs measured before merge per the T-1149 discipline. Visual acceptance: a coastal Quarter capture must show structurally more coastline detail than the same coast at District — the differentiation Araminta flagged as never yet demonstrated. Pairs with T-1161 (NEAREST at sparse rungs) — together these answer both of Jeroen''s specific complaints.
SCOPE CLARIFICATION (Jeroen, 2026-07-22): the current aim is the NATURE layer — geo + wild growth — per D-166''s outside-in order; cities/settlements stay uncolored for now. This ticket''s octave extension therefore covers not just coast + relief but VEGETATION PATCHINESS as spatial content: forests as massifs at region scale resolving to distinct woods (district), copses and clearings (quarter), desert/steppe transitions as shapes — the invention fields (detail_scatter character/envelope) driving per-rung vegetation patterning in the BASE layer rather than uniform per-cell class tint. His words: ''even the nature layer should produce rivers, forests, copses, bushes, deserts, elevation.'' Rivers are T-1156''s nature half (see its re-scope); this ticket owns the continuous-field content (coastline, relief, vegetation/moisture patchiness).
REFINEMENT RESOLUTIONS (lead, 2026-07-22, from Si audit): (1) Bench baseline: none recorded anywhere — implementer runs server/tests/zoom_ladder_bench.rs (cargo test --release --test zoom_ladder_bench -- --ignored --nocapture) BEFORE any code change to establish the pre-change baseline, and again after; both sets of numbers go in the PR. (2) ''Goldens'' corrected: no golden file pins derive_at_metres/invent_primitives today (derivation_harness.json is voxel-chain; cascade_layer1.json is heightmap-river). Explicit scope: ADD a golden fixture for the window-derivation path (mirroring derivation_harness.rs''s pattern) so this and every future octave change gets a deliberate-regen commit discipline. (3) Vegetation patchiness confirmed NET-NEW, in scope (not split): today derive_vegetation (district_profile.rs:398-490) is pure per-cell classification with no positional field. Build the vegetation invention mechanism mirroring coast_invention.rs''s two-tier envelope+character model (Jeroen''s nature-first ruling is the direction; the tiered invention-field pattern is the established shape to imitate). Non-blocking notes: VOXEL octaves feeding Quarter classification does NOT conflict with D-226(d) — that ruling caps request granularity, not contributing wavelengths (coast warp already crosses scales the other way); MIN_WL_BANDS_M (layer_proxy.rs:415-421) needs new entries once the ladder extends, quantize_min_wl_m generalizes as-is.', 'review', 'high', NULL, 'server', 'D-226', '2026-07-22 16:30:36.314', '2026-07-22 21:12:56.083', NULL, '23b7115954f41d981596fc7808db7913', 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 ('06FRMPY76TYY76FSBV2Y5P8QHM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Per-rung composite filter policy — NEAREST at sparse rungs, LINEAR where density earns it', 'PR #192 review (Araminta, follow-up): COMPOSITE_SMOOTH''s GPU bilinear stretch at Region rung reads as a near-featureless soft gradient at 204.8 km/cell (capture 02-region-mid.png) — technically honest LoD (interpolating BETWEEN real derived samples, letter-compliant with ''never magnified interpolation'') but visually indistinguishable from the coarse-composite-stretched experience the mandate was written to kill: the spirit is violated at sparse rungs. Implement a per-rung filter policy: crisp NEAREST at Region/orbital (the COMPOSITE_SMOOTH := false path already exists byte-compatible per the file''s own doc — make it per-rung runtime instead of compile-time), keep LINEAR at District/Quarter where cell density reads as texture rather than smoothing-over-absence. Araminta owns the rung-by-rung call — get her sign-off on which rungs get which filter (and whether the threshold should key on on-screen px-per-cell rather than rung identity). Supersedes the remaining half of the old T-1155 COMPOSITE_SMOOTH-retirement framing — reconcile with that ticket when picking this up.', 'review', 'medium', NULL, 'client', 'D-226', '2026-07-22 15:04:58.422', '2026-07-22 21:12:56.086', NULL, '46a96cf3fdb3f1e820a1e0d1a077e5b1', 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 ('06FRNAHD35YPZHJWMJCRZK62Z4', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Extend deterministic invention octaves into the Atlas window path — coastline crinkle and terrain texture at rung scale', 'Jeroen, 2026-07-22, after first hands-on with the shipped ladder: ''zero detail on lower levels, just a green field, and the green-blue transition is a fade — it should be a more detailed coastline.'' Root cause confirmed by octave audit: the window classification consumes terrain_detail whose octaves span 32768-4096m (OCTAVE_WAVELENGTHS_M, detail_scatter.rs:33) and coast_invention whose warp octaves span 262144-16384m (WARP_OCTAVE_WAVELENGTHS_M, coast_invention.rs:56) — so at Quarter spacing (512m) the sampled field is genuinely smooth (~8 cells per finest terrain wave; ~32 per finest coast wave). The ladder''s TRANSPORT reaches 512m but its CONTENT floor is 4km/16km. The finer bands already exist (VOXEL_OCTAVE_WAVELENGTHS_M 1024-128m ''the rolling hills a walking character navigates by''; MOSAIC 64-8m) but feed only the Phase-5 voxel path — never the window derivation. Work: (a) extend coast_warp octaves below 16km down through quarter-visible wavelengths (bays/headlands/islets appearing as you zoom — THE headline visual ask) with min_wavelength_m cutoff discipline per rung; (b) feed the sub-district relief band into Quarter-rung classification (derive_at_metres already takes the cutoff — decide which bands activate per rung and re-bench; the T-1149 bench harness is the tool); (c) re-tune MIN_WL_BANDS_M to match the extended ladder; (d) determinism goldens updated deliberately (this CHANGES derived output — a golden-regeneration commit with rationale, not silent). Costs measured before merge per the T-1149 discipline. Visual acceptance: a coastal Quarter capture must show structurally more coastline detail than the same coast at District — the differentiation Araminta flagged as never yet demonstrated. Pairs with T-1161 (NEAREST at sparse rungs) — together these answer both of Jeroen''s specific complaints.
SCOPE CLARIFICATION (Jeroen, 2026-07-22): the current aim is the NATURE layer — geo + wild growth — per D-166''s outside-in order; cities/settlements stay uncolored for now. This ticket''s octave extension therefore covers not just coast + relief but VEGETATION PATCHINESS as spatial content: forests as massifs at region scale resolving to distinct woods (district), copses and clearings (quarter), desert/steppe transitions as shapes — the invention fields (detail_scatter character/envelope) driving per-rung vegetation patterning in the BASE layer rather than uniform per-cell class tint. His words: ''even the nature layer should produce rivers, forests, copses, bushes, deserts, elevation.'' Rivers are T-1156''s nature half (see its re-scope); this ticket owns the continuous-field content (coastline, relief, vegetation/moisture patchiness).
REFINEMENT RESOLUTIONS (lead, 2026-07-22, from Si audit): (1) Bench baseline: none recorded anywhere — implementer runs server/tests/zoom_ladder_bench.rs (cargo test --release --test zoom_ladder_bench -- --ignored --nocapture) BEFORE any code change to establish the pre-change baseline, and again after; both sets of numbers go in the PR. (2) ''Goldens'' corrected: no golden file pins derive_at_metres/invent_primitives today (derivation_harness.json is voxel-chain; cascade_layer1.json is heightmap-river). Explicit scope: ADD a golden fixture for the window-derivation path (mirroring derivation_harness.rs''s pattern) so this and every future octave change gets a deliberate-regen commit discipline. (3) Vegetation patchiness confirmed NET-NEW, in scope (not split): today derive_vegetation (district_profile.rs:398-490) is pure per-cell classification with no positional field. Build the vegetation invention mechanism mirroring coast_invention.rs''s two-tier envelope+character model (Jeroen''s nature-first ruling is the direction; the tiered invention-field pattern is the established shape to imitate). Non-blocking notes: VOXEL octaves feeding Quarter classification does NOT conflict with D-226(d) — that ruling caps request granularity, not contributing wavelengths (coast warp already crosses scales the other way); MIN_WL_BANDS_M (layer_proxy.rs:415-421) needs new entries once the ladder extends, quantize_min_wl_m generalizes as-is.', 'done', 'high', NULL, 'server', 'D-226', '2026-07-22 16:30:36.314', '2026-07-22 21:39:18.433', NULL, '2db5e32429126f1d835884b43d1e94ec', 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 ('06FRMPY76TYY76FSBV2Y5P8QHM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Per-rung composite filter policy — NEAREST at sparse rungs, LINEAR where density earns it', 'PR #192 review (Araminta, follow-up): COMPOSITE_SMOOTH''s GPU bilinear stretch at Region rung reads as a near-featureless soft gradient at 204.8 km/cell (capture 02-region-mid.png) — technically honest LoD (interpolating BETWEEN real derived samples, letter-compliant with ''never magnified interpolation'') but visually indistinguishable from the coarse-composite-stretched experience the mandate was written to kill: the spirit is violated at sparse rungs. Implement a per-rung filter policy: crisp NEAREST at Region/orbital (the COMPOSITE_SMOOTH := false path already exists byte-compatible per the file''s own doc — make it per-rung runtime instead of compile-time), keep LINEAR at District/Quarter where cell density reads as texture rather than smoothing-over-absence. Araminta owns the rung-by-rung call — get her sign-off on which rungs get which filter (and whether the threshold should key on on-screen px-per-cell rather than rung identity). Supersedes the remaining half of the old T-1155 COMPOSITE_SMOOTH-retirement framing — reconcile with that ticket when picking this up.', 'done', 'medium', NULL, 'client', 'D-226', '2026-07-22 15:04:58.422', '2026-07-22 21:39:18.440', NULL, '29bb249967f8226bb7b5fbcd0c0de20a', 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 ('06FRCF1A0K41K6S85X0HTXACHM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Retire COMPOSITE_SMOOTH once quarter windows are default at close zoom', 'Zoom-ladder migration step 6 (docs/architecture/atlas-zoom-ladder-t1143.md SS8). The linear-filter interim (atlas_window_overlay.gd:60, T-1145) retires once granularity-4 quarter windows are the default at close zoom — real classified quarter cells replace GPU bilinear smoothing as the source of sub-district texture (Jeroen''s ruling: smoothing was never the destination). Remove the compile-time const and the crisp/smooth dual path; keep one render path. Blocked by the client zoom ladder.', 'cancelled', 'low', NULL, 'client', 'D-226', '2026-07-21 19:51:57.700', '2026-07-22 21:39:18.463', NULL, '5c4a13b7989afea19a37dd187b2ac474', 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 ('06FRCF1A0K41K6S85X0HTXACHM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Retire COMPOSITE_SMOOTH once quarter windows are default at close zoom', 'Zoom-ladder migration step 6 (docs/architecture/atlas-zoom-ladder-t1143.md SS8). The linear-filter interim (atlas_window_overlay.gd:60, T-1145) retires once granularity-4 quarter windows are the default at close zoom — real classified quarter cells replace GPU bilinear smoothing as the source of sub-district texture (Jeroen''s ruling: smoothing was never the destination). Remove the compile-time const and the crisp/smooth dual path; keep one render path. Blocked by the client zoom ladder.
CANCELLED 2026-07-22 (PR #194 merge): superseded by T-1161''s two-axes outcome — COMPOSITE_SMOOTH survives as the compile-time pipeline axis (crisp draw_rect path kept for debug/compare); the crispness-at-sparse-rungs requirement is delivered by the per-rung sampling-filter policy (_filter_for_granularity_v2) instead of deleting the const. Design doc §8 step 6 errata''d + D-226 filter-axis note added in the same PR. No partial version of this ticket survives that outcome (Si audit + Tyre''s Q4 ruling agree: close-both, not do-first).', 'cancelled', 'low', NULL, 'client', 'D-226', '2026-07-21 19:51:57.700', '2026-07-22 21:39:25.586', NULL, 'a6846cbfe866f51e747288980df54215', 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 ('06FRJD57678SVWD7XTSV9E3MSW', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Port whole-body Atlas overlays onto the zoom ladder (markers, city sidebar, POL/POP/gen overlays)', 'T-1153/T-1152 retired the AtlasViewer heightmap screen as the atlas entry; its 17-entry overlay surface did NOT carry to the Region-rung ladder view (AtlasWindowViewer has only gen_dw_temp/moisture/veg). Deferred wholesale by Stig with rationale: these overlays key off markers.json/city-names/road-graph/settlement data the DistrictWindowLayer wire shape does not carry — porting is a per-overlay design + wire question (at what rung does a settlement marker fade in? does POL make sense over a Region composite?), not a mechanical copy. Deferred set: city markers + click-to-inspect city data sidebar; roads/rail/settlements/quarter-footprint generation overlays (gen_l1_rivers/basins/attractors, gen_district, gen_l2_roads, gen_l3_settlements, gen_region_grid, gen_l4_quarters); political zones, population density, corp presence, production/stockpile toggles, gate markers, named features. The retired code survives intact and unreachable on atlas_viewer.gd — reuse it or retire it explicitly per overlay. Needs Araminta (per-rung affordance/fade rules) + Tyre/Dudley (which overlays need new wire fields vs client-side joins). Until this lands the ladder view is terrain-only — a real capability regression vs the old planetary screen, accepted knowingly in the T-1152/T-1153 batch.
RE-SCOPED (Jeroen, 2026-07-22): ''we are aiming for not coloring in the cities at the moment — the nature layer (geo and wild growth) first,'' matching D-166''s outside-in order. SPLIT this ticket''s overlay set into two waves. WAVE 1 (this ticket, promoted): the NATURE overlays — gen_l1_rivers/basins/attractors onto the ladder (rivers as per-rung refining features: trunk rivers at Region, tributaries at District, streams at Quarter — the wire/derivation question of serving river skeletons windowed is the real work), plus named natural features (rivers/oceans/ranges labels) if cheap. WAVE 2 (deferred until the nature layer stands, per Jeroen): everything civilizational — city markers + sidebar, settlements/quarters footprints, roads/rail, POL/POP/CRP/gate markers. Do not port wave 2 with wave 1.', 'in_progress', 'high', NULL, 'client', 'D-226', '2026-07-22 09:42:37.105', '2026-07-23 05:48:12.595', NULL, '5b0b8014cddb6e4526b314bbb4d6fd82', 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 ('06FRV224PD3NMWKR1AAC0J0D58', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Thread drainage skeleton into derive_at_metres as per-cell riparian signal (near_perennial_water)', 'derive_at_metres (district_profile.rs:1461-1469) hardcodes near_perennial_water = false into derive_vegetation, so district/quarter windows cannot render riparian bands (RiparianScrub/RiparianThicket) or the hyper-arid micro-oasis (:421) even where a trunk river crosses the cell. After T-1156 puts rivers on the ladder, this surfaces as a visible mismatch: a drawn river over vegetation painted barren. Fix: thread the Layer-1 river-cell set into the window derive and compute per-cell near_perennial_water as a ''within riparian-band distance of a river cell'' test. Rivers sample the drainage GEOGRAPHY; vegetation''s moisture influence rides T-1162''s perturbed moisture_q — never a re-derivation of base moisture (forward contract from T-1156 wave-1 ruling 3, Tyre 2026-07-23). Re-pin the believability/derivation golden harnesses. Gated: not wave 1.', 'backlog', 'medium', NULL, 'server', 'D-226', '2026-07-23 05:52:25.779', '2026-07-23 05:52:25.779', NULL, '6906c3392598a731e3587ec7b5efd3c1', 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 ('06FRV2EV5QK4BCZ1P9H85JH2YM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Natural-feature name labels on the ladder (wire attach_feature_names, populate atlas_feature_names, feature-names proxy)', 'T-1156 wave 1''s ''labels if cheap'' clause, split out after Si''s audit (2026-07-23): cheap but not free — three dormant pieces exist and nothing connects them. (1) layer1::attach_feature_names (layer1.rs:213-262) is unit-tested with zero production callers — wire it into run_layer1''s call path with name pools; (2) atlas_feature_names (systems-schema.sql:537-544) is created-but-never-populated — add the INSERT step at regen time from the D-223 names-only markers.json pools, mirroring populate_atlas_city_names (generator change: import_economics/atlas.py — watchlist/regen-db implications); (3) add a feature-names read proxy mirroring atlas_data_proxy.rs handle_city_names_request (D-236 pattern — its own message type, outside the AtlasLayerResponse ceilings) + client label draw on the ladder (the retired _draw_named_features at atlas_marker_overlay.gd:239-264 reads the pre-D-223 dict shape and would silently no-op — rewrite, don''t reactivate).', 'backlog', 'low', NULL, 'server', 'D-226', '2026-07-23 05:54:09.837', '2026-07-23 05:54:09.837', NULL, 'db3934783e20d14abbcc124d911d5d5f', 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 ('06FRV2GT1FQVATWX641KY9K66C', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Invent river courses below heightmap resolution — deterministic warped polylines between D8 cells', 'Named experience gap from T-1156 wave 1 (Si audit + lead, 2026-07-23): the D8 river skeleton lives at heightmap working-grid resolution (512x256 — ~76.6 km per river cell on Lendel). Wave 1''s per-rung class filtering makes planetary/Region river courses real, but at District (2 km cells, ~65 km window) and Quarter the geometry is SPARSER THAN THE WINDOW — a window typically contains zero or one river cell, so ''tributaries at District, streams at Quarter'' cannot materialize as visible courses from the stored skeleton alone. The fix is the linear-feature analog of T-1162''s coast crinkle: deterministically INVENT river course geometry between/below D8 cells — domain-warped polyline interpolation between adjacent river cells (seeded per body+cell pair, D-227 discipline, elevation-aware so courses prefer invented-terrain valleys where feasible), refining with rung the same way the coast warp refines the coastline. Also the natural home for reconciling rivers with T-1162''s invented relief (a wave-1 known gap: drawn rivers may cross invented hills — acceptable at 76 km dot spacing, glaring once courses are continuous). Depends on wave 1''s carrier (river_class on RiverNetwork) landing first.', 'backlog', 'medium', NULL, 'server', 'D-227', '2026-07-23 05:54:25.931', '2026-07-23 05:54:25.931', NULL, 'cd2fad68b3f4b4c85c7f5676bbdd9366', 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 ('06FRJD57678SVWD7XTSV9E3MSW', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Port whole-body Atlas overlays onto the zoom ladder (markers, city sidebar, POL/POP/gen overlays)', 'T-1153/T-1152 retired the AtlasViewer heightmap screen as the atlas entry; its 17-entry overlay surface did NOT carry to the Region-rung ladder view (AtlasWindowViewer has only gen_dw_temp/moisture/veg). Deferred wholesale by Stig with rationale: these overlays key off markers.json/city-names/road-graph/settlement data the DistrictWindowLayer wire shape does not carry — porting is a per-overlay design + wire question (at what rung does a settlement marker fade in? does POL make sense over a Region composite?), not a mechanical copy. Deferred set: city markers + click-to-inspect city data sidebar; roads/rail/settlements/quarter-footprint generation overlays (gen_l1_rivers/basins/attractors, gen_district, gen_l2_roads, gen_l3_settlements, gen_region_grid, gen_l4_quarters); political zones, population density, corp presence, production/stockpile toggles, gate markers, named features. The retired code survives intact and unreachable on atlas_viewer.gd — reuse it or retire it explicitly per overlay. Needs Araminta (per-rung affordance/fade rules) + Tyre/Dudley (which overlays need new wire fields vs client-side joins). Until this lands the ladder view is terrain-only — a real capability regression vs the old planetary screen, accepted knowingly in the T-1152/T-1153 batch.
RE-SCOPED (Jeroen, 2026-07-22): ''we are aiming for not coloring in the cities at the moment — the nature layer (geo and wild growth) first,'' matching D-166''s outside-in order. SPLIT this ticket''s overlay set into two waves. WAVE 1 (this ticket, promoted): the NATURE overlays — gen_l1_rivers/basins/attractors onto the ladder (rivers as per-rung refining features: trunk rivers at Region, tributaries at District, streams at Quarter — the wire/derivation question of serving river skeletons windowed is the real work), plus named natural features (rivers/oceans/ranges labels) if cheap. WAVE 2 (deferred until the nature layer stands, per Jeroen): everything civilizational — city markers + sidebar, settlements/quarters footprints, roads/rail, POL/POP/CRP/gate markers. Do not port wave 2 with wave 1.', 'review', 'high', NULL, 'client', 'D-226', '2026-07-22 09:42:37.105', '2026-07-23 07:03:09.251', NULL, '507ba12c13ff0785eeb760ffbf434e31', 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 ('06FRVMZYG7Z5YCKHCBS7XWJPM4', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Pole-adjacent District-rung entry can resolve to a Region window (canonicalization/reselect interplay)', 'Observed during T-1156 wave-1 acceptance drives (2026-07-23, lead): calling AtlasWindowViewer._enter_at_rung(body, system, district_center, 32, ''District'') at a pole-edge center (Lendel district (10466,-4784), the meridian edge) produced a REGION window in one run (header ''region window - 204.800 km/cell'', center canonicalized to (10461,-4773)) while an earlier run at the same nominal center rendered the requested district window (2.048 km/cell, center uncanonicalized (10466,-4784) exactly). Suspected interplay: pole-wall clamp / canonicalize_district_center vs the entry auto-fit and _maybe_reselect_rung''s coverage walk (a pole-clamped degenerate window''s computed extent may trip is_fully_zoomed_out or the coverage ceiling). Also on file: one drive process computed a sign-flipped latitude mapping (row 0 -> +wy) unreproducible afterward — suspected stale compiled-script cache serving a revert-verification''s temporarily-flipped state (godot-cold-parse wipes exactly this class of cache). Reproduce both at the pole edge, pin with tests. Extreme-edge case: normal navigation rarely enters District directly at a pole.', 'backlog', 'low', NULL, 'client', 'D-226', '2026-07-23 07:15:08.545', '2026-07-23 07:15:08.545', NULL, 'eb275c825d8f5167a36f6c4816cda1fd', 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 ('06FRV2GT1FQVATWX641KY9K66C', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Invent river courses below heightmap resolution — deterministic warped polylines between D8 cells', 'Named experience gap from T-1156 wave 1 (Si audit + lead, 2026-07-23): the D8 river skeleton lives at heightmap working-grid resolution (512x256 — ~76.6 km per river cell on Lendel). Wave 1''s per-rung class filtering makes planetary/Region river courses real, but at District (2 km cells, ~65 km window) and Quarter the geometry is SPARSER THAN THE WINDOW — a window typically contains zero or one river cell, so ''tributaries at District, streams at Quarter'' cannot materialize as visible courses from the stored skeleton alone. The fix is the linear-feature analog of T-1162''s coast crinkle: deterministically INVENT river course geometry between/below D8 cells — domain-warped polyline interpolation between adjacent river cells (seeded per body+cell pair, D-227 discipline, elevation-aware so courses prefer invented-terrain valleys where feasible), refining with rung the same way the coast warp refines the coastline. Also the natural home for reconciling rivers with T-1162''s invented relief (a wave-1 known gap: drawn rivers may cross invented hills — acceptable at 76 km dot spacing, glaring once courses are continuous). Depends on wave 1''s carrier (river_class on RiverNetwork) landing first.
T-1156 review forward-note (Tyre, PR #195 Q1): when this ticket redesigns the binning corner, consider PER-BASIN anchoring as an explicit option — every watershed gets its own trunk regardless of the body''s dominant river (the right eventual model once basins are player-inspectable), at the cost of a second normalization pass and a cross-basin legibility decision at Region scale (several equal-weight ''trunks'' of wildly different magnitude undersell the dominant drainage). The wave-1 global-per-body anchor is correct for ''show the main river system at a glance''. Single revisit points: classify_river_cell (server) and RIVER_CLASS_VISIBLE_BY_RUNG (client).', 'backlog', 'medium', NULL, 'server', 'D-227', '2026-07-23 05:54:25.931', '2026-07-23 07:15:25.347', NULL, '4f5c99fff846359be9faa494c92c600e', 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 ('06FRJD57678SVWD7XTSV9E3MSW', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Port whole-body Atlas overlays onto the zoom ladder (markers, city sidebar, POL/POP/gen overlays)', 'T-1153/T-1152 retired the AtlasViewer heightmap screen as the atlas entry; its 17-entry overlay surface did NOT carry to the Region-rung ladder view (AtlasWindowViewer has only gen_dw_temp/moisture/veg). Deferred wholesale by Stig with rationale: these overlays key off markers.json/city-names/road-graph/settlement data the DistrictWindowLayer wire shape does not carry — porting is a per-overlay design + wire question (at what rung does a settlement marker fade in? does POL make sense over a Region composite?), not a mechanical copy. Deferred set: city markers + click-to-inspect city data sidebar; roads/rail/settlements/quarter-footprint generation overlays (gen_l1_rivers/basins/attractors, gen_district, gen_l2_roads, gen_l3_settlements, gen_region_grid, gen_l4_quarters); political zones, population density, corp presence, production/stockpile toggles, gate markers, named features. The retired code survives intact and unreachable on atlas_viewer.gd — reuse it or retire it explicitly per overlay. Needs Araminta (per-rung affordance/fade rules) + Tyre/Dudley (which overlays need new wire fields vs client-side joins). Until this lands the ladder view is terrain-only — a real capability regression vs the old planetary screen, accepted knowingly in the T-1152/T-1153 batch.
RE-SCOPED (Jeroen, 2026-07-22): ''we are aiming for not coloring in the cities at the moment — the nature layer (geo and wild growth) first,'' matching D-166''s outside-in order. SPLIT this ticket''s overlay set into two waves. WAVE 1 (this ticket, promoted): the NATURE overlays — gen_l1_rivers/basins/attractors onto the ladder (rivers as per-rung refining features: trunk rivers at Region, tributaries at District, streams at Quarter — the wire/derivation question of serving river skeletons windowed is the real work), plus named natural features (rivers/oceans/ranges labels) if cheap. WAVE 2 (deferred until the nature layer stands, per Jeroen): everything civilizational — city markers + sidebar, settlements/quarters footprints, roads/rail, POL/POP/CRP/gate markers. Do not port wave 2 with wave 1.
WAVE-2 REFACTOR NOTE (Tyre, PR #195 Q3): when the civilizational overlays land, host the whole discrete-marker family (city markers, labels, gate glyphs, click targets — all screen-space-constant) on an UNSCALED screen-space sibling Node2D (transform positions into screen space, leave .scale at 1.0) and retire per-call zoom compensation (_zs/zoom_compensated_size) — screen-space-constant becomes correct by construction instead of a per-call-site discipline that silently degrades when one site is missed (PR #195 I1 was exactly that miss). The divide-by-zoom approach shipped in wave 1 is fine once compensated, but the sibling-layer shape is the standard ''HUD annotations over a zoomed canvas'' pattern and pays for itself at wave-2 marker volume. T-1158-adjacent.', 'review', 'high', NULL, 'client', 'D-226', '2026-07-22 09:42:37.105', '2026-07-23 07:15:25.374', NULL, '14a9f02f7911c6fb0ed125572aaaf551', 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 ('06FRJD57678SVWD7XTSV9E3MSW', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Port whole-body Atlas overlays onto the zoom ladder (markers, city sidebar, POL/POP/gen overlays)', 'T-1153/T-1152 retired the AtlasViewer heightmap screen as the atlas entry; its 17-entry overlay surface did NOT carry to the Region-rung ladder view (AtlasWindowViewer has only gen_dw_temp/moisture/veg). Deferred wholesale by Stig with rationale: these overlays key off markers.json/city-names/road-graph/settlement data the DistrictWindowLayer wire shape does not carry — porting is a per-overlay design + wire question (at what rung does a settlement marker fade in? does POL make sense over a Region composite?), not a mechanical copy. Deferred set: city markers + click-to-inspect city data sidebar; roads/rail/settlements/quarter-footprint generation overlays (gen_l1_rivers/basins/attractors, gen_district, gen_l2_roads, gen_l3_settlements, gen_region_grid, gen_l4_quarters); political zones, population density, corp presence, production/stockpile toggles, gate markers, named features. The retired code survives intact and unreachable on atlas_viewer.gd — reuse it or retire it explicitly per overlay. Needs Araminta (per-rung affordance/fade rules) + Tyre/Dudley (which overlays need new wire fields vs client-side joins). Until this lands the ladder view is terrain-only — a real capability regression vs the old planetary screen, accepted knowingly in the T-1152/T-1153 batch.
RE-SCOPED (Jeroen, 2026-07-22): ''we are aiming for not coloring in the cities at the moment — the nature layer (geo and wild growth) first,'' matching D-166''s outside-in order. SPLIT this ticket''s overlay set into two waves. WAVE 1 (this ticket, promoted): the NATURE overlays — gen_l1_rivers/basins/attractors onto the ladder (rivers as per-rung refining features: trunk rivers at Region, tributaries at District, streams at Quarter — the wire/derivation question of serving river skeletons windowed is the real work), plus named natural features (rivers/oceans/ranges labels) if cheap. WAVE 2 (deferred until the nature layer stands, per Jeroen): everything civilizational — city markers + sidebar, settlements/quarters footprints, roads/rail, POL/POP/CRP/gate markers. Do not port wave 2 with wave 1.
WAVE-2 REFACTOR NOTE (Tyre, PR #195 Q3): when the civilizational overlays land, host the whole discrete-marker family (city markers, labels, gate glyphs, click targets — all screen-space-constant) on an UNSCALED screen-space sibling Node2D (transform positions into screen space, leave .scale at 1.0) and retire per-call zoom compensation (_zs/zoom_compensated_size) — screen-space-constant becomes correct by construction instead of a per-call-site discipline that silently degrades when one site is missed (PR #195 I1 was exactly that miss). The divide-by-zoom approach shipped in wave 1 is fine once compensated, but the sibling-layer shape is the standard ''HUD annotations over a zoomed canvas'' pattern and pays for itself at wave-2 marker volume. T-1158-adjacent.', 'done', 'high', NULL, 'client', 'D-226', '2026-07-22 09:42:37.105', '2026-07-23 07:35:36.008', NULL, '29b4c320dbcc5d1568efd1f6f2a1a8dd', 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 ('06FRW2ABEG6HQHVKQ9F7NQ1K8R', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'River skeleton draws over drawn ocean — two-waterline disagreement (raw heightmap sea level vs invented/warped morphology coast)', 'Jeroen, 2026-07-23, first hands-on with PR #195''s merged captures: ''the rivers seem to be continuing under the waterline of the ocean.'' Mechanism confirmed by code inspection: river_cells/mouths are filtered against the RAW 512x256 heightmap sea level (drainage.rs:258-259, elevation[i] >= sea_level), but the drawn ocean is the derived MORPHOLOGY verdict (district_profile.rs:1464-1465 open_water) — which post-T-1162 includes the coast-warp invention (the drawn coastline is deterministically displaced from the heightmap coast) and at Region rung aggregates at 204.8 km cells. Anywhere the two waterlines disagree, a raw-land river dot renders over painted ocean; mouths are worst-case by definition (last land cell on the RAW coast — wherever the drawn coast is displaced inland, mouths render offshore). Long ocean-crossing dot trails = low-lying raw-land chains the derived morphology paints as water. This is the Q-100 dual-artifact class Si''s wave-1 audit pre-flagged. Fix-layer options for Tyre''s ruling: (A) client-side clip against the arrived composite''s per-cell water class (cheap, presentational, keeps two truths but hides disagreement; needs a mouth rule — snap-to-drawn-land vs suppress); (B) server-side reconciliation — classify skeleton cells against the WARPED/derived coast at extraction (one truthful waterline per D-166 honesty; couples drainage to the invention layer, golden + cost implications); (C) fold into T-1170 course invention (courses terminate at the invented coast naturally) and accept the artifact until then.', 'backlog', 'high', NULL, 'server', 'D-226', '2026-07-23 08:13:21.652', '2026-07-23 08:13:21.652', NULL, 'a8b7da7a6864d26e7ec43407f017f130', 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 ('06FRW2ABEG6HQHVKQ9F7NQ1K8R', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'River skeleton draws over drawn ocean — two-waterline disagreement (raw heightmap sea level vs invented/warped morphology coast)', 'Jeroen, 2026-07-23, first hands-on with PR #195''s merged captures: ''the rivers seem to be continuing under the waterline of the ocean.'' Mechanism confirmed by code inspection: river_cells/mouths are filtered against the RAW 512x256 heightmap sea level (drainage.rs:258-259, elevation[i] >= sea_level), but the drawn ocean is the derived MORPHOLOGY verdict (district_profile.rs:1464-1465 open_water) — which post-T-1162 includes the coast-warp invention (the drawn coastline is deterministically displaced from the heightmap coast) and at Region rung aggregates at 204.8 km cells. Anywhere the two waterlines disagree, a raw-land river dot renders over painted ocean; mouths are worst-case by definition (last land cell on the RAW coast — wherever the drawn coast is displaced inland, mouths render offshore). Long ocean-crossing dot trails = low-lying raw-land chains the derived morphology paints as water. This is the Q-100 dual-artifact class Si''s wave-1 audit pre-flagged. Fix-layer options for Tyre''s ruling: (A) client-side clip against the arrived composite''s per-cell water class (cheap, presentational, keeps two truths but hides disagreement; needs a mouth rule — snap-to-drawn-land vs suppress); (B) server-side reconciliation — classify skeleton cells against the WARPED/derived coast at extraction (one truthful waterline per D-166 honesty; couples drainage to the invention layer, golden + cost implications); (C) fold into T-1170 course invention (courses terminate at the invented coast naturally) and accept the artifact until then.', 'in_progress', 'high', NULL, 'server', 'D-226', '2026-07-23 08:13:21.652', '2026-07-23 08:16:50.646', NULL, '61c48cb05e8ff0cf3310ef90c39096ef', 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 ('06FRW2ABEG6HQHVKQ9F7NQ1K8R', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'River skeleton draws over drawn ocean — two-waterline disagreement (raw heightmap sea level vs invented/warped morphology coast)', 'Jeroen, 2026-07-23, first hands-on with PR #195''s merged captures: ''the rivers seem to be continuing under the waterline of the ocean.'' Mechanism confirmed by code inspection: river_cells/mouths are filtered against the RAW 512x256 heightmap sea level (drainage.rs:258-259, elevation[i] >= sea_level), but the drawn ocean is the derived MORPHOLOGY verdict (district_profile.rs:1464-1465 open_water) — which post-T-1162 includes the coast-warp invention (the drawn coastline is deterministically displaced from the heightmap coast) and at Region rung aggregates at 204.8 km cells. Anywhere the two waterlines disagree, a raw-land river dot renders over painted ocean; mouths are worst-case by definition (last land cell on the RAW coast — wherever the drawn coast is displaced inland, mouths render offshore). Long ocean-crossing dot trails = low-lying raw-land chains the derived morphology paints as water. This is the Q-100 dual-artifact class Si''s wave-1 audit pre-flagged. Fix-layer options for Tyre''s ruling: (A) client-side clip against the arrived composite''s per-cell water class (cheap, presentational, keeps two truths but hides disagreement; needs a mouth rule — snap-to-drawn-land vs suppress); (B) server-side reconciliation — classify skeleton cells against the WARPED/derived coast at extraction (one truthful waterline per D-166 honesty; couples drainage to the invention layer, golden + cost implications); (C) fold into T-1170 course invention (courses terminate at the invented coast naturally) and accept the artifact until then.
TYRE RULING (2026-07-23, full text in PR record): option (D) — client-side draw-time clip now, retired into T-1170. Mechanism sizing corrected: WARP_AMPLITUDE_CAP_PX=0.75 (sub-pixel, ~57km max on Lendel) proves the warp is fringe-only — the ocean-crossing trails are Region morphology AGGREGATION painting low raw-land shelves as OpenOcean. Decisive architecture point: the drawn coast is RUNG-INDEXED (warp_fbm''s cutoff admits more octaves at finer rungs — a family of curves, not one), so no single server-side waterline is well-defined; option (B) rejected on well-definedness, not cost. Rules: (1) clip river dots + confluences against the arrived composite''s per-cell morphology water verdict (OpenOcean/Lake) at the rung being painted — strict drop, no snap; (2) Region 205km-cell amputation of final coastal dots accepted (invisible vs the trails it kills); (3) mouths in drawn-water cells SUPPRESSED, never snapped (snapping invents an uncomputed landmark; suppressed mouths return with real geometry in T-1170) — flagged to Jeroen as the one taste call, proceeding on Tyre''s recommendation; (4) basin boundaries NOT clipped (faint watershed overlay, legitimately grazes offshore pour-points — out of scope); (5) dot-pop across rungs is the honest consequence of the rung-indexed coast, not a regression; (6) retirement marker at the clip site, retired in the same change as T-1170''s coast-terminating courses. D-226 two-waterline note text supplied (rides the fix branch).', 'in_progress', 'high', NULL, 'server', 'D-226', '2026-07-23 08:13:21.652', '2026-07-23 08:16:50.672', NULL, 'bab9194019631134d9c694c2607ae5d3', 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 ('06FRV2GT1FQVATWX641KY9K66C', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Invent river courses below heightmap resolution — deterministic warped polylines between D8 cells', 'Named experience gap from T-1156 wave 1 (Si audit + lead, 2026-07-23): the D8 river skeleton lives at heightmap working-grid resolution (512x256 — ~76.6 km per river cell on Lendel). Wave 1''s per-rung class filtering makes planetary/Region river courses real, but at District (2 km cells, ~65 km window) and Quarter the geometry is SPARSER THAN THE WINDOW — a window typically contains zero or one river cell, so ''tributaries at District, streams at Quarter'' cannot materialize as visible courses from the stored skeleton alone. The fix is the linear-feature analog of T-1162''s coast crinkle: deterministically INVENT river course geometry between/below D8 cells — domain-warped polyline interpolation between adjacent river cells (seeded per body+cell pair, D-227 discipline, elevation-aware so courses prefer invented-terrain valleys where feasible), refining with rung the same way the coast warp refines the coastline. Also the natural home for reconciling rivers with T-1162''s invented relief (a wave-1 known gap: drawn rivers may cross invented hills — acceptable at 76 km dot spacing, glaring once courses are continuous). Depends on wave 1''s carrier (river_class on RiverNetwork) landing first.
T-1156 review forward-note (Tyre, PR #195 Q1): when this ticket redesigns the binning corner, consider PER-BASIN anchoring as an explicit option — every watershed gets its own trunk regardless of the body''s dominant river (the right eventual model once basins are player-inspectable), at the cost of a second normalization pass and a cross-basin legibility decision at Region scale (several equal-weight ''trunks'' of wildly different magnitude undersell the dominant drainage). The wave-1 global-per-body anchor is correct for ''show the main river system at a glance''. Single revisit points: classify_river_cell (server) and RIVER_CLASS_VISIBLE_BY_RUNG (client).
T-1172 retirement dependency (Tyre''s clip ruling): when this ticket''s course invention terminates courses at the invented coast with continuous geometry, RETIRE the T-1172 draw-time water clip in the same change (marker comment at the clip site in atlas_window_nature_overlay.gd) — suppressed offshore mouths return with real coast-terminus geometry here. Also: the clip''s strict-drop behavior at Region 205km cells (accepted amputation of final coastal dots) is superseded by real course termini.', 'backlog', 'medium', NULL, 'server', 'D-227', '2026-07-23 05:54:25.931', '2026-07-23 08:16:58.391', NULL, '00f70fc3431eb438365cd5c6aef78ef0', 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 ('06FRW2ABEG6HQHVKQ9F7NQ1K8R', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'River skeleton draws over drawn ocean — two-waterline disagreement (raw heightmap sea level vs invented/warped morphology coast)', 'Jeroen, 2026-07-23, first hands-on with PR #195''s merged captures: ''the rivers seem to be continuing under the waterline of the ocean.'' Mechanism confirmed by code inspection: river_cells/mouths are filtered against the RAW 512x256 heightmap sea level (drainage.rs:258-259, elevation[i] >= sea_level), but the drawn ocean is the derived MORPHOLOGY verdict (district_profile.rs:1464-1465 open_water) — which post-T-1162 includes the coast-warp invention (the drawn coastline is deterministically displaced from the heightmap coast) and at Region rung aggregates at 204.8 km cells. Anywhere the two waterlines disagree, a raw-land river dot renders over painted ocean; mouths are worst-case by definition (last land cell on the RAW coast — wherever the drawn coast is displaced inland, mouths render offshore). Long ocean-crossing dot trails = low-lying raw-land chains the derived morphology paints as water. This is the Q-100 dual-artifact class Si''s wave-1 audit pre-flagged. Fix-layer options for Tyre''s ruling: (A) client-side clip against the arrived composite''s per-cell water class (cheap, presentational, keeps two truths but hides disagreement; needs a mouth rule — snap-to-drawn-land vs suppress); (B) server-side reconciliation — classify skeleton cells against the WARPED/derived coast at extraction (one truthful waterline per D-166 honesty; couples drainage to the invention layer, golden + cost implications); (C) fold into T-1170 course invention (courses terminate at the invented coast naturally) and accept the artifact until then.
TYRE RULING (2026-07-23, full text in PR record): option (D) — client-side draw-time clip now, retired into T-1170. Mechanism sizing corrected: WARP_AMPLITUDE_CAP_PX=0.75 (sub-pixel, ~57km max on Lendel) proves the warp is fringe-only — the ocean-crossing trails are Region morphology AGGREGATION painting low raw-land shelves as OpenOcean. Decisive architecture point: the drawn coast is RUNG-INDEXED (warp_fbm''s cutoff admits more octaves at finer rungs — a family of curves, not one), so no single server-side waterline is well-defined; option (B) rejected on well-definedness, not cost. Rules: (1) clip river dots + confluences against the arrived composite''s per-cell morphology water verdict (OpenOcean/Lake) at the rung being painted — strict drop, no snap; (2) Region 205km-cell amputation of final coastal dots accepted (invisible vs the trails it kills); (3) mouths in drawn-water cells SUPPRESSED, never snapped (snapping invents an uncomputed landmark; suppressed mouths return with real geometry in T-1170) — flagged to Jeroen as the one taste call, proceeding on Tyre''s recommendation; (4) basin boundaries NOT clipped (faint watershed overlay, legitimately grazes offshore pour-points — out of scope); (5) dot-pop across rungs is the honest consequence of the rung-indexed coast, not a regression; (6) retirement marker at the clip site, retired in the same change as T-1170''s coast-terminating courses. D-226 two-waterline note text supplied (rides the fix branch).', 'in_progress', 'high', NULL, 'client', 'D-226', '2026-07-23 08:13:21.652', '2026-07-23 08:16:58.417', NULL, 'c6ed407f012aa8039dbb33910ece2408', 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 ('06FRWP5YN5M4687E0F9PSFWG1M', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Client-only branches bounce off the push gate — orphan-leaking skips in binary-gated e2e suites', 'Found on the T-1172 waterline-clip push (2026-07-23): a client-only branch''s gate run FAILED at 3842/3842 passed, 0 failures. Chain: four legacy live/e2e suites (test_input_roundtrip, test_sprint2_proof, +2) self-skip via fuzzer-arg _do_skip when server/target/debug/settled-reach-server is absent; each skip leaks one internal gdUnit Node (the known cold-start-batch orphan class); 9 orphans -> GdUnitCmdTool exits non-zero; tests/run-godot prints ''Tests passed'' from its own failure-count parse but exits with the raw tool code (run-godot:144 ''exit $EXIT_CODE''); the pre-push hook trusts the exit code -> push aborted. Server-touching branches never hit this because cargo test builds the debug binary as a side effect. Workaround used: cargo build in the worktree before pushing. Proper fixes (pick during implementation): (a) convert the four suites'' _do_skip usage to the guarded early-return pattern test_atlas_window_nature_overlay_draw_smoke already uses (the established no-orphan skip shape); and/or (b) make run-godot''s exit code consistent with its own parse (0 failures+errors -> exit 0, with orphans surfaced in the JSON as a field so genuine leak regressions stay visible rather than silently ignored — careful: orphan detection is a real leak gate for non-skip tests, don''t blanket-disable it).', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-23 09:40:08.489', '2026-07-23 09:40:08.489', NULL, 'bd768ec1e060065fe28bf6c44b4d27b7', 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 ('06FRW2ABEG6HQHVKQ9F7NQ1K8R', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'River skeleton draws over drawn ocean — two-waterline disagreement (raw heightmap sea level vs invented/warped morphology coast)', 'Jeroen, 2026-07-23, first hands-on with PR #195''s merged captures: ''the rivers seem to be continuing under the waterline of the ocean.'' Mechanism confirmed by code inspection: river_cells/mouths are filtered against the RAW 512x256 heightmap sea level (drainage.rs:258-259, elevation[i] >= sea_level), but the drawn ocean is the derived MORPHOLOGY verdict (district_profile.rs:1464-1465 open_water) — which post-T-1162 includes the coast-warp invention (the drawn coastline is deterministically displaced from the heightmap coast) and at Region rung aggregates at 204.8 km cells. Anywhere the two waterlines disagree, a raw-land river dot renders over painted ocean; mouths are worst-case by definition (last land cell on the RAW coast — wherever the drawn coast is displaced inland, mouths render offshore). Long ocean-crossing dot trails = low-lying raw-land chains the derived morphology paints as water. This is the Q-100 dual-artifact class Si''s wave-1 audit pre-flagged. Fix-layer options for Tyre''s ruling: (A) client-side clip against the arrived composite''s per-cell water class (cheap, presentational, keeps two truths but hides disagreement; needs a mouth rule — snap-to-drawn-land vs suppress); (B) server-side reconciliation — classify skeleton cells against the WARPED/derived coast at extraction (one truthful waterline per D-166 honesty; couples drainage to the invention layer, golden + cost implications); (C) fold into T-1170 course invention (courses terminate at the invented coast naturally) and accept the artifact until then.
TYRE RULING (2026-07-23, full text in PR record): option (D) — client-side draw-time clip now, retired into T-1170. Mechanism sizing corrected: WARP_AMPLITUDE_CAP_PX=0.75 (sub-pixel, ~57km max on Lendel) proves the warp is fringe-only — the ocean-crossing trails are Region morphology AGGREGATION painting low raw-land shelves as OpenOcean. Decisive architecture point: the drawn coast is RUNG-INDEXED (warp_fbm''s cutoff admits more octaves at finer rungs — a family of curves, not one), so no single server-side waterline is well-defined; option (B) rejected on well-definedness, not cost. Rules: (1) clip river dots + confluences against the arrived composite''s per-cell morphology water verdict (OpenOcean/Lake) at the rung being painted — strict drop, no snap; (2) Region 205km-cell amputation of final coastal dots accepted (invisible vs the trails it kills); (3) mouths in drawn-water cells SUPPRESSED, never snapped (snapping invents an uncomputed landmark; suppressed mouths return with real geometry in T-1170) — flagged to Jeroen as the one taste call, proceeding on Tyre''s recommendation; (4) basin boundaries NOT clipped (faint watershed overlay, legitimately grazes offshore pour-points — out of scope); (5) dot-pop across rungs is the honest consequence of the rung-indexed coast, not a regression; (6) retirement marker at the clip site, retired in the same change as T-1170''s coast-terminating courses. D-226 two-waterline note text supplied (rides the fix branch).', 'review', 'high', NULL, 'client', 'D-226', '2026-07-23 08:13:21.652', '2026-07-23 09:42:13.593', NULL, 'ab9dde91281b4a0e18a1dac9d2df5be7', 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 ('06FRW2ABEG6HQHVKQ9F7NQ1K8R', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'River skeleton draws over drawn ocean — two-waterline disagreement (raw heightmap sea level vs invented/warped morphology coast)', 'Jeroen, 2026-07-23, first hands-on with PR #195''s merged captures: ''the rivers seem to be continuing under the waterline of the ocean.'' Mechanism confirmed by code inspection: river_cells/mouths are filtered against the RAW 512x256 heightmap sea level (drainage.rs:258-259, elevation[i] >= sea_level), but the drawn ocean is the derived MORPHOLOGY verdict (district_profile.rs:1464-1465 open_water) — which post-T-1162 includes the coast-warp invention (the drawn coastline is deterministically displaced from the heightmap coast) and at Region rung aggregates at 204.8 km cells. Anywhere the two waterlines disagree, a raw-land river dot renders over painted ocean; mouths are worst-case by definition (last land cell on the RAW coast — wherever the drawn coast is displaced inland, mouths render offshore). Long ocean-crossing dot trails = low-lying raw-land chains the derived morphology paints as water. This is the Q-100 dual-artifact class Si''s wave-1 audit pre-flagged. Fix-layer options for Tyre''s ruling: (A) client-side clip against the arrived composite''s per-cell water class (cheap, presentational, keeps two truths but hides disagreement; needs a mouth rule — snap-to-drawn-land vs suppress); (B) server-side reconciliation — classify skeleton cells against the WARPED/derived coast at extraction (one truthful waterline per D-166 honesty; couples drainage to the invention layer, golden + cost implications); (C) fold into T-1170 course invention (courses terminate at the invented coast naturally) and accept the artifact until then.
TYRE RULING (2026-07-23, full text in PR record): option (D) — client-side draw-time clip now, retired into T-1170. Mechanism sizing corrected: WARP_AMPLITUDE_CAP_PX=0.75 (sub-pixel, ~57km max on Lendel) proves the warp is fringe-only — the ocean-crossing trails are Region morphology AGGREGATION painting low raw-land shelves as OpenOcean. Decisive architecture point: the drawn coast is RUNG-INDEXED (warp_fbm''s cutoff admits more octaves at finer rungs — a family of curves, not one), so no single server-side waterline is well-defined; option (B) rejected on well-definedness, not cost. Rules: (1) clip river dots + confluences against the arrived composite''s per-cell morphology water verdict (OpenOcean/Lake) at the rung being painted — strict drop, no snap; (2) Region 205km-cell amputation of final coastal dots accepted (invisible vs the trails it kills); (3) mouths in drawn-water cells SUPPRESSED, never snapped (snapping invents an uncomputed landmark; suppressed mouths return with real geometry in T-1170) — flagged to Jeroen as the one taste call, proceeding on Tyre''s recommendation; (4) basin boundaries NOT clipped (faint watershed overlay, legitimately grazes offshore pour-points — out of scope); (5) dot-pop across rungs is the honest consequence of the rung-indexed coast, not a regression; (6) retirement marker at the clip site, retired in the same change as T-1170''s coast-terminating courses. D-226 two-waterline note text supplied (rides the fix branch).', 'done', 'high', NULL, 'client', 'D-226', '2026-07-23 08:13:21.652', '2026-07-23 09:52:33.554', NULL, 'ef7d600d47006495945258fa28cb359e', 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 ('06FRV2GT1FQVATWX641KY9K66C', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Invent river courses below heightmap resolution — deterministic warped polylines between D8 cells', 'Named experience gap from T-1156 wave 1 (Si audit + lead, 2026-07-23): the D8 river skeleton lives at heightmap working-grid resolution (512x256 — ~76.6 km per river cell on Lendel). Wave 1''s per-rung class filtering makes planetary/Region river courses real, but at District (2 km cells, ~65 km window) and Quarter the geometry is SPARSER THAN THE WINDOW — a window typically contains zero or one river cell, so ''tributaries at District, streams at Quarter'' cannot materialize as visible courses from the stored skeleton alone. The fix is the linear-feature analog of T-1162''s coast crinkle: deterministically INVENT river course geometry between/below D8 cells — domain-warped polyline interpolation between adjacent river cells (seeded per body+cell pair, D-227 discipline, elevation-aware so courses prefer invented-terrain valleys where feasible), refining with rung the same way the coast warp refines the coastline. Also the natural home for reconciling rivers with T-1162''s invented relief (a wave-1 known gap: drawn rivers may cross invented hills — acceptable at 76 km dot spacing, glaring once courses are continuous). Depends on wave 1''s carrier (river_class on RiverNetwork) landing first.
T-1156 review forward-note (Tyre, PR #195 Q1): when this ticket redesigns the binning corner, consider PER-BASIN anchoring as an explicit option — every watershed gets its own trunk regardless of the body''s dominant river (the right eventual model once basins are player-inspectable), at the cost of a second normalization pass and a cross-basin legibility decision at Region scale (several equal-weight ''trunks'' of wildly different magnitude undersell the dominant drainage). The wave-1 global-per-body anchor is correct for ''show the main river system at a glance''. Single revisit points: classify_river_cell (server) and RIVER_CLASS_VISIBLE_BY_RUNG (client).
T-1172 retirement dependency (Tyre''s clip ruling): when this ticket''s course invention terminates courses at the invented coast with continuous geometry, RETIRE the T-1172 draw-time water clip in the same change (marker comment at the clip site in atlas_window_nature_overlay.gd) — suppressed offshore mouths return with real coast-terminus geometry here. Also: the clip''s strict-drop behavior at Region 205km cells (accepted amputation of final coastal dots) is superseded by real course termini.
Mouth semantics follow-up (Jeroen''s capture question, 2026-07-23): two mouth-placement oddities to resolve when courses/termini become real geometry here — (1) inland-reading mouths: the raw-coast mouth cell can sit landward of the drawn coastline (the mirror of T-1172''s offshore case; not clipped since it''s on drawn land) — mouths should move to the course''s actual invented-coast terminus; (2) pole-edge drains: drainage.rs counts rivers draining off the grid''s top/bottom edge as ''mouths'', rendering double-rings in polar ice with no sea adjacent — revisit whether these render as mouths at all (probably not; they are grid artifacts, not river-meets-sea events).', 'backlog', 'medium', NULL, 'server', 'D-227', '2026-07-23 05:54:25.931', '2026-07-23 10:14:21.616', NULL, '558a64a097fc55802e86813ae6a3ea53', 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 ('06FRV2GT1FQVATWX641KY9K66C', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Invent river courses below heightmap resolution — deterministic warped polylines between D8 cells', 'Named experience gap from T-1156 wave 1 (Si audit + lead, 2026-07-23): the D8 river skeleton lives at heightmap working-grid resolution (512x256 — ~76.6 km per river cell on Lendel). Wave 1''s per-rung class filtering makes planetary/Region river courses real, but at District (2 km cells, ~65 km window) and Quarter the geometry is SPARSER THAN THE WINDOW — a window typically contains zero or one river cell, so ''tributaries at District, streams at Quarter'' cannot materialize as visible courses from the stored skeleton alone. The fix is the linear-feature analog of T-1162''s coast crinkle: deterministically INVENT river course geometry between/below D8 cells — domain-warped polyline interpolation between adjacent river cells (seeded per body+cell pair, D-227 discipline, elevation-aware so courses prefer invented-terrain valleys where feasible), refining with rung the same way the coast warp refines the coastline. Also the natural home for reconciling rivers with T-1162''s invented relief (a wave-1 known gap: drawn rivers may cross invented hills — acceptable at 76 km dot spacing, glaring once courses are continuous). Depends on wave 1''s carrier (river_class on RiverNetwork) landing first.
T-1156 review forward-note (Tyre, PR #195 Q1): when this ticket redesigns the binning corner, consider PER-BASIN anchoring as an explicit option — every watershed gets its own trunk regardless of the body''s dominant river (the right eventual model once basins are player-inspectable), at the cost of a second normalization pass and a cross-basin legibility decision at Region scale (several equal-weight ''trunks'' of wildly different magnitude undersell the dominant drainage). The wave-1 global-per-body anchor is correct for ''show the main river system at a glance''. Single revisit points: classify_river_cell (server) and RIVER_CLASS_VISIBLE_BY_RUNG (client).
T-1172 retirement dependency (Tyre''s clip ruling): when this ticket''s course invention terminates courses at the invented coast with continuous geometry, RETIRE the T-1172 draw-time water clip in the same change (marker comment at the clip site in atlas_window_nature_overlay.gd) — suppressed offshore mouths return with real coast-terminus geometry here. Also: the clip''s strict-drop behavior at Region 205km cells (accepted amputation of final coastal dots) is superseded by real course termini.
Mouth semantics follow-up (Jeroen''s capture question, 2026-07-23): two mouth-placement oddities to resolve when courses/termini become real geometry here — (1) inland-reading mouths: the raw-coast mouth cell can sit landward of the drawn coastline (the mirror of T-1172''s offshore case; not clipped since it''s on drawn land) — mouths should move to the course''s actual invented-coast terminus; (2) pole-edge drains: drainage.rs counts rivers draining off the grid''s top/bottom edge as ''mouths'', rendering double-rings in polar ice with no sea adjacent — revisit whether these render as mouths at all (probably not; they are grid artifacts, not river-meets-sea events).', 'in_progress', 'medium', NULL, 'server', 'D-227', '2026-07-23 05:54:25.931', '2026-07-23 10:16:37.340', NULL, '59af2c27ebbc4dc29c857ebe7486fb70', 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 ('06FRV224PD3NMWKR1AAC0J0D58', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Thread drainage skeleton into derive_at_metres as per-cell riparian signal (near_perennial_water)', 'derive_at_metres (district_profile.rs:1461-1469) hardcodes near_perennial_water = false into derive_vegetation, so district/quarter windows cannot render riparian bands (RiparianScrub/RiparianThicket) or the hyper-arid micro-oasis (:421) even where a trunk river crosses the cell. After T-1156 puts rivers on the ladder, this surfaces as a visible mismatch: a drawn river over vegetation painted barren. Fix: thread the Layer-1 river-cell set into the window derive and compute per-cell near_perennial_water as a ''within riparian-band distance of a river cell'' test. Rivers sample the drainage GEOGRAPHY; vegetation''s moisture influence rides T-1162''s perturbed moisture_q — never a re-derivation of base moisture (forward contract from T-1156 wave-1 ruling 3, Tyre 2026-07-23). Re-pin the believability/derivation golden harnesses. Gated: not wave 1.', 'in_progress', 'medium', NULL, 'server', 'D-226', '2026-07-23 05:52:25.779', '2026-07-23 10:16:37.347', NULL, 'c10756f4af995db8039bf94bf5f27423', 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 ('06FRXGSXBZKXTHR7MR5K9H2HRG', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Batch vs window derive paths sample different world positions for the same DistrictPos', 'Pre-existing architectural quirk found by Dudley during T-1168 integration testing (2026-07-23, not introduced by that batch): derive_district_profile''s cell-aggregate-centre sampling and the window path''s district-origin sampling resolve to genuinely different world positions for ''the same DistrictPos'' — full DistrictProfile output legitimately differs between paths (morphology/elev/moisture) at the same nominal district. His integration test was redesigned to compare the riparian signal at a shared exact world position instead (the right test). Investigate whether any existing batch/window consumer pair assumes positional agreement (city placement vs window rendering?), decide the canonical sampling convention, and either unify or document the divergence as intentional with the reasoning.', 'backlog', 'low', NULL, 'server', NULL, '2026-07-23 11:36:27.743', '2026-07-23 11:36:27.743', NULL, 'c7e03d0b07f506447f291c5f0024c83e', 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 ('06FRXKFEJMDEPQ0PZ5Q862DHKG', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas map-reading polish pass — river/nature presentation refinements (single large pass)', 'Jeroen, 2026-07-23, reviewing the T-1170 course captures: ''there are a bunch of graphical cleanups I would want to suggest around line thickness and tapering off to a point at the source to make the map read more fluent, but I feel map polish like that is better off as a single large pass later.'' This ticket is that pass''s collector — park presentation refinements here rather than folding them into feature batches. Seeded items: (1) river line thickness tuning across rungs (beyond the functional per-class defaults Araminta confirmed in T-1170); (2) SOURCE TAPERING — courses should taper to a point at their upstream source rather than starting at full class width (the classic cartographic river grammar; likely per-vertex width via Polygon2D strip or per-segment width ramp, since draw_polyline is single-width); (3) candidates declined-as-scope during T-1170 review: class-tinted color ramp (Araminta: width+opacity sufficient for now, hue is solvable-later). Accumulate further items via pql ticket append as they come up in captures/reviews. Scope discipline: correctness issues (ruled values not rendering, clip bugs) are NEVER parked here — they get fixed in their own batches; this pass is aesthetics only. Schedule: after the nature layer stands (post T-1156 wave 2 at the earliest), as one coherent Araminta-led pass.', 'backlog', 'low', NULL, 'client', 'D-226', '2026-07-23 11:48:08.469', '2026-07-23 11:48:08.469', NULL, '95f3d89de3656c968fa4d679e70c05ca', 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 ('06FRXKFEJMDEPQ0PZ5Q862DHKG', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas map-reading polish pass — river/nature presentation refinements (single large pass)', 'Jeroen, 2026-07-23, reviewing the T-1170 course captures: ''there are a bunch of graphical cleanups I would want to suggest around line thickness and tapering off to a point at the source to make the map read more fluent, but I feel map polish like that is better off as a single large pass later.'' This ticket is that pass''s collector — park presentation refinements here rather than folding them into feature batches. Seeded items: (1) river line thickness tuning across rungs (beyond the functional per-class defaults Araminta confirmed in T-1170); (2) SOURCE TAPERING — courses should taper to a point at their upstream source rather than starting at full class width (the classic cartographic river grammar; likely per-vertex width via Polygon2D strip or per-segment width ramp, since draw_polyline is single-width); (3) candidates declined-as-scope during T-1170 review: class-tinted color ramp (Araminta: width+opacity sufficient for now, hue is solvable-later). Accumulate further items via pql ticket append as they come up in captures/reviews. Scope discipline: correctness issues (ruled values not rendering, clip bugs) are NEVER parked here — they get fixed in their own batches; this pass is aesthetics only. Schedule: after the nature layer stands (post T-1156 wave 2 at the earliest), as one coherent Araminta-led pass.
REFERENCE (Jeroen, 2026-07-23): RimWorld''s world map as the fluency benchmark — ''the rimworld maps do roads, rivers, coasts and biomes quite well'' (hex substrate explicitly NOT us; the techniques transfer, the tiling doesn''t). Extracted techniques for this pass: (1) RIVERS AS NETWORKS — visible tributary-joins-trunk convergence, subtle width variation along the run, taper-to-source (his earlier item 2), thin/consistent/restrained overall; (2) ROADS VS RIVERS disambiguated by COLOR + STRAIGHTNESS (grey chord-like vs blue winding), not width — the wave-2 road-rendering rule; (3) COASTS as a shoreline GRADIENT — beach/transition band + subtle near-shore water shading (our coastal/transition morphology class already exists; presentation only); (4) BIOME/RELIEF TEXTURE — stipple/texture density carrying hills/mountains/forest on top of hue; strong fit for us since T-1162''s vegetation-patchiness and relief fields can DRIVE stipple density (texture as data, not decoration); (5) geographic LABELS do heavy fluency lifting — that half lives in T-1169, coordinate the passes. Screenshot reference in the session record 2026-07-23.', 'backlog', 'low', NULL, 'client', 'D-226', '2026-07-23 11:48:08.469', '2026-07-23 11:51:22.671', NULL, 'ad9dfafb140cd4d7d022b525bdc5be77', 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 ('06FRXKFEJMDEPQ0PZ5Q862DHKG', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas map-reading polish pass — river/nature presentation refinements (single large pass)', 'Jeroen, 2026-07-23, reviewing the T-1170 course captures: ''there are a bunch of graphical cleanups I would want to suggest around line thickness and tapering off to a point at the source to make the map read more fluent, but I feel map polish like that is better off as a single large pass later.'' This ticket is that pass''s collector — park presentation refinements here rather than folding them into feature batches. Seeded items: (1) river line thickness tuning across rungs (beyond the functional per-class defaults Araminta confirmed in T-1170); (2) SOURCE TAPERING — courses should taper to a point at their upstream source rather than starting at full class width (the classic cartographic river grammar; likely per-vertex width via Polygon2D strip or per-segment width ramp, since draw_polyline is single-width); (3) candidates declined-as-scope during T-1170 review: class-tinted color ramp (Araminta: width+opacity sufficient for now, hue is solvable-later). Accumulate further items via pql ticket append as they come up in captures/reviews. Scope discipline: correctness issues (ruled values not rendering, clip bugs) are NEVER parked here — they get fixed in their own batches; this pass is aesthetics only. Schedule: after the nature layer stands (post T-1156 wave 2 at the earliest), as one coherent Araminta-led pass.
REFERENCE (Jeroen, 2026-07-23): RimWorld''s world map as the fluency benchmark — ''the rimworld maps do roads, rivers, coasts and biomes quite well'' (hex substrate explicitly NOT us; the techniques transfer, the tiling doesn''t). Extracted techniques for this pass: (1) RIVERS AS NETWORKS — visible tributary-joins-trunk convergence, subtle width variation along the run, taper-to-source (his earlier item 2), thin/consistent/restrained overall; (2) ROADS VS RIVERS disambiguated by COLOR + STRAIGHTNESS (grey chord-like vs blue winding), not width — the wave-2 road-rendering rule; (3) COASTS as a shoreline GRADIENT — beach/transition band + subtle near-shore water shading (our coastal/transition morphology class already exists; presentation only); (4) BIOME/RELIEF TEXTURE — stipple/texture density carrying hills/mountains/forest on top of hue; strong fit for us since T-1162''s vegetation-patchiness and relief fields can DRIVE stipple density (texture as data, not decoration); (5) geographic LABELS do heavy fluency lifting — that half lives in T-1169, coordinate the passes. Screenshot reference in the session record 2026-07-23.
Reference screenshot saved in-repo: docs/design/references/rimworld-world-map-fluency.jpg (+ README note in that dir) — supersedes the ''session record'' pointer in the previous append.', 'backlog', 'low', NULL, 'client', 'D-226', '2026-07-23 11:48:08.469', '2026-07-23 11:54:02.271', NULL, 'd46950b7f50014259838506bb76c1667', 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 ('06FRV2GT1FQVATWX641KY9K66C', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Invent river courses below heightmap resolution — deterministic warped polylines between D8 cells', 'Named experience gap from T-1156 wave 1 (Si audit + lead, 2026-07-23): the D8 river skeleton lives at heightmap working-grid resolution (512x256 — ~76.6 km per river cell on Lendel). Wave 1''s per-rung class filtering makes planetary/Region river courses real, but at District (2 km cells, ~65 km window) and Quarter the geometry is SPARSER THAN THE WINDOW — a window typically contains zero or one river cell, so ''tributaries at District, streams at Quarter'' cannot materialize as visible courses from the stored skeleton alone. The fix is the linear-feature analog of T-1162''s coast crinkle: deterministically INVENT river course geometry between/below D8 cells — domain-warped polyline interpolation between adjacent river cells (seeded per body+cell pair, D-227 discipline, elevation-aware so courses prefer invented-terrain valleys where feasible), refining with rung the same way the coast warp refines the coastline. Also the natural home for reconciling rivers with T-1162''s invented relief (a wave-1 known gap: drawn rivers may cross invented hills — acceptable at 76 km dot spacing, glaring once courses are continuous). Depends on wave 1''s carrier (river_class on RiverNetwork) landing first.
T-1156 review forward-note (Tyre, PR #195 Q1): when this ticket redesigns the binning corner, consider PER-BASIN anchoring as an explicit option — every watershed gets its own trunk regardless of the body''s dominant river (the right eventual model once basins are player-inspectable), at the cost of a second normalization pass and a cross-basin legibility decision at Region scale (several equal-weight ''trunks'' of wildly different magnitude undersell the dominant drainage). The wave-1 global-per-body anchor is correct for ''show the main river system at a glance''. Single revisit points: classify_river_cell (server) and RIVER_CLASS_VISIBLE_BY_RUNG (client).
T-1172 retirement dependency (Tyre''s clip ruling): when this ticket''s course invention terminates courses at the invented coast with continuous geometry, RETIRE the T-1172 draw-time water clip in the same change (marker comment at the clip site in atlas_window_nature_overlay.gd) — suppressed offshore mouths return with real coast-terminus geometry here. Also: the clip''s strict-drop behavior at Region 205km cells (accepted amputation of final coastal dots) is superseded by real course termini.
Mouth semantics follow-up (Jeroen''s capture question, 2026-07-23): two mouth-placement oddities to resolve when courses/termini become real geometry here — (1) inland-reading mouths: the raw-coast mouth cell can sit landward of the drawn coastline (the mirror of T-1172''s offshore case; not clipped since it''s on drawn land) — mouths should move to the course''s actual invented-coast terminus; (2) pole-edge drains: drainage.rs counts rivers draining off the grid''s top/bottom edge as ''mouths'', rendering double-rings in polar ice with no sea adjacent — revisit whether these render as mouths at all (probably not; they are grid artifacts, not river-meets-sea events).', 'review', 'medium', NULL, 'server', 'D-227', '2026-07-23 05:54:25.931', '2026-07-23 12:17:43.879', NULL, 'a294084eb1fc47caafb59738e5a562f5', 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 ('06FRV224PD3NMWKR1AAC0J0D58', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Thread drainage skeleton into derive_at_metres as per-cell riparian signal (near_perennial_water)', 'derive_at_metres (district_profile.rs:1461-1469) hardcodes near_perennial_water = false into derive_vegetation, so district/quarter windows cannot render riparian bands (RiparianScrub/RiparianThicket) or the hyper-arid micro-oasis (:421) even where a trunk river crosses the cell. After T-1156 puts rivers on the ladder, this surfaces as a visible mismatch: a drawn river over vegetation painted barren. Fix: thread the Layer-1 river-cell set into the window derive and compute per-cell near_perennial_water as a ''within riparian-band distance of a river cell'' test. Rivers sample the drainage GEOGRAPHY; vegetation''s moisture influence rides T-1162''s perturbed moisture_q — never a re-derivation of base moisture (forward contract from T-1156 wave-1 ruling 3, Tyre 2026-07-23). Re-pin the believability/derivation golden harnesses. Gated: not wave 1.', 'review', 'medium', NULL, 'server', 'D-226', '2026-07-23 05:52:25.779', '2026-07-23 12:17:43.882', NULL, '79858546de97bfe8cca4ca7349444a2b', 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 ('06FRY9W24PV55Z6FMW8TEM7XER', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Design discussion: revise the Atlas map render mechanism (bypass the zoom-scaled-canvas error class)', 'Jeroen, 2026-07-23, during the T-1170 live rounds: ''I want to discuss and maybe revise the pixel scan/output mechanism for drawing the map. I feel we can be smarter about this and bypass an entire class of errors we are seeing as well as probably improve performance.'' DISCUSSION FIRST — this is a design conversation with Jeroen (pair/workshop mode), not autonomous work; scheduled after the T-1170/T-1168 cycle closes. The error class in evidence, all descending from immediate-mode draw calls inside a zoom-scaled canvas (_canvas.scale = view_zoom): the per-call-site zoom compensation discipline (_zs/_zs_stroke, silently degrading when one site is missed — PR #195 I1, PR #197 stroke sites), Godot''s line-rasterizer width floor (sub-1.0-canvas-unit strokes collapse to hairline — the T-1170 width bug), the radius-smaller-than-stroke draw_arc regime (the mouth-ring investigation), and arguably the wrap-image resolution family. Candidate directions to bring to the discussion: (a) Tyre''s unscaled screen-space sibling layer (positions transformed, sizes literal — already noted on T-1156 wave 2/T-1158); (b) render-to-texture per window/tile at native resolution, composited as textures (sizes become texel-exact, zoom becomes pure texture transform); (c) shader-side composition of the per-cell data (morphology/courses as data textures, coloring+features in fragment shader — likely the performance win); (d) hybrid. Inputs to gather before the discussion: current draw-call counts/frame costs per rung, the T-1175 polish requirements (per-vertex tapering wants Polygon2D strips or shaders anyway), and the wave-2 marker volume. Relates: T-1158 (viewer decomposition), T-1175 (polish pass), Tyre''s PR #197 Q5 note.', 'backlog', 'high', NULL, 'client', 'D-226', '2026-07-23 13:25:58.949', '2026-07-23 13:25:58.949', NULL, '8765843a0114899fbf21e31688103044', 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 ('06FRV2GT1FQVATWX641KY9K66C', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Invent river courses below heightmap resolution — deterministic warped polylines between D8 cells', 'Named experience gap from T-1156 wave 1 (Si audit + lead, 2026-07-23): the D8 river skeleton lives at heightmap working-grid resolution (512x256 — ~76.6 km per river cell on Lendel). Wave 1''s per-rung class filtering makes planetary/Region river courses real, but at District (2 km cells, ~65 km window) and Quarter the geometry is SPARSER THAN THE WINDOW — a window typically contains zero or one river cell, so ''tributaries at District, streams at Quarter'' cannot materialize as visible courses from the stored skeleton alone. The fix is the linear-feature analog of T-1162''s coast crinkle: deterministically INVENT river course geometry between/below D8 cells — domain-warped polyline interpolation between adjacent river cells (seeded per body+cell pair, D-227 discipline, elevation-aware so courses prefer invented-terrain valleys where feasible), refining with rung the same way the coast warp refines the coastline. Also the natural home for reconciling rivers with T-1162''s invented relief (a wave-1 known gap: drawn rivers may cross invented hills — acceptable at 76 km dot spacing, glaring once courses are continuous). Depends on wave 1''s carrier (river_class on RiverNetwork) landing first.
T-1156 review forward-note (Tyre, PR #195 Q1): when this ticket redesigns the binning corner, consider PER-BASIN anchoring as an explicit option — every watershed gets its own trunk regardless of the body''s dominant river (the right eventual model once basins are player-inspectable), at the cost of a second normalization pass and a cross-basin legibility decision at Region scale (several equal-weight ''trunks'' of wildly different magnitude undersell the dominant drainage). The wave-1 global-per-body anchor is correct for ''show the main river system at a glance''. Single revisit points: classify_river_cell (server) and RIVER_CLASS_VISIBLE_BY_RUNG (client).
T-1172 retirement dependency (Tyre''s clip ruling): when this ticket''s course invention terminates courses at the invented coast with continuous geometry, RETIRE the T-1172 draw-time water clip in the same change (marker comment at the clip site in atlas_window_nature_overlay.gd) — suppressed offshore mouths return with real coast-terminus geometry here. Also: the clip''s strict-drop behavior at Region 205km cells (accepted amputation of final coastal dots) is superseded by real course termini.
Mouth semantics follow-up (Jeroen''s capture question, 2026-07-23): two mouth-placement oddities to resolve when courses/termini become real geometry here — (1) inland-reading mouths: the raw-coast mouth cell can sit landward of the drawn coastline (the mirror of T-1172''s offshore case; not clipped since it''s on drawn land) — mouths should move to the course''s actual invented-coast terminus; (2) pole-edge drains: drainage.rs counts rivers draining off the grid''s top/bottom edge as ''mouths'', rendering double-rings in polar ice with no sea adjacent — revisit whether these render as mouths at all (probably not; they are grid artifacts, not river-meets-sea events).', 'done', 'medium', NULL, 'server', 'D-227', '2026-07-23 05:54:25.931', '2026-07-23 14:10:16.116', NULL, 'a4598a9ddb2ba9308f7e45511a854c8e', 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 ('06FRV224PD3NMWKR1AAC0J0D58', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Thread drainage skeleton into derive_at_metres as per-cell riparian signal (near_perennial_water)', 'derive_at_metres (district_profile.rs:1461-1469) hardcodes near_perennial_water = false into derive_vegetation, so district/quarter windows cannot render riparian bands (RiparianScrub/RiparianThicket) or the hyper-arid micro-oasis (:421) even where a trunk river crosses the cell. After T-1156 puts rivers on the ladder, this surfaces as a visible mismatch: a drawn river over vegetation painted barren. Fix: thread the Layer-1 river-cell set into the window derive and compute per-cell near_perennial_water as a ''within riparian-band distance of a river cell'' test. Rivers sample the drainage GEOGRAPHY; vegetation''s moisture influence rides T-1162''s perturbed moisture_q — never a re-derivation of base moisture (forward contract from T-1156 wave-1 ruling 3, Tyre 2026-07-23). Re-pin the believability/derivation golden harnesses. Gated: not wave 1.', 'done', 'medium', NULL, 'server', 'D-226', '2026-07-23 05:52:25.779', '2026-07-23 14:10:16.118', NULL, '69d78fa243b250862d9193023ccc7bf2', 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 ('06FRY9W24PV55Z6FMW8TEM7XER', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Design discussion: revise the Atlas map render mechanism (bypass the zoom-scaled-canvas error class)', 'Jeroen, 2026-07-23, during the T-1170 live rounds: ''I want to discuss and maybe revise the pixel scan/output mechanism for drawing the map. I feel we can be smarter about this and bypass an entire class of errors we are seeing as well as probably improve performance.'' DISCUSSION FIRST — this is a design conversation with Jeroen (pair/workshop mode), not autonomous work; scheduled after the T-1170/T-1168 cycle closes. The error class in evidence, all descending from immediate-mode draw calls inside a zoom-scaled canvas (_canvas.scale = view_zoom): the per-call-site zoom compensation discipline (_zs/_zs_stroke, silently degrading when one site is missed — PR #195 I1, PR #197 stroke sites), Godot''s line-rasterizer width floor (sub-1.0-canvas-unit strokes collapse to hairline — the T-1170 width bug), the radius-smaller-than-stroke draw_arc regime (the mouth-ring investigation), and arguably the wrap-image resolution family. Candidate directions to bring to the discussion: (a) Tyre''s unscaled screen-space sibling layer (positions transformed, sizes literal — already noted on T-1156 wave 2/T-1158); (b) render-to-texture per window/tile at native resolution, composited as textures (sizes become texel-exact, zoom becomes pure texture transform); (c) shader-side composition of the per-cell data (morphology/courses as data textures, coloring+features in fragment shader — likely the performance win); (d) hybrid. Inputs to gather before the discussion: current draw-call counts/frame costs per rung, the T-1175 polish requirements (per-vertex tapering wants Polygon2D strips or shaders anyway), and the wave-2 marker volume. Relates: T-1158 (viewer decomposition), T-1175 (polish pass), Tyre''s PR #197 Q5 note.', 'in_progress', 'high', NULL, 'client', 'D-226', '2026-07-23 13:25:58.949', '2026-07-23 16:26:46.891', NULL, '1713e3e2338ee916912b020c42dbfc8f', 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 ('06FRY9W24PV55Z6FMW8TEM7XER', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Design discussion: revise the Atlas map render mechanism (bypass the zoom-scaled-canvas error class)', 'Jeroen, 2026-07-23, during the T-1170 live rounds: ''I want to discuss and maybe revise the pixel scan/output mechanism for drawing the map. I feel we can be smarter about this and bypass an entire class of errors we are seeing as well as probably improve performance.'' DISCUSSION FIRST — this is a design conversation with Jeroen (pair/workshop mode), not autonomous work; scheduled after the T-1170/T-1168 cycle closes. The error class in evidence, all descending from immediate-mode draw calls inside a zoom-scaled canvas (_canvas.scale = view_zoom): the per-call-site zoom compensation discipline (_zs/_zs_stroke, silently degrading when one site is missed — PR #195 I1, PR #197 stroke sites), Godot''s line-rasterizer width floor (sub-1.0-canvas-unit strokes collapse to hairline — the T-1170 width bug), the radius-smaller-than-stroke draw_arc regime (the mouth-ring investigation), and arguably the wrap-image resolution family. Candidate directions to bring to the discussion: (a) Tyre''s unscaled screen-space sibling layer (positions transformed, sizes literal — already noted on T-1156 wave 2/T-1158); (b) render-to-texture per window/tile at native resolution, composited as textures (sizes become texel-exact, zoom becomes pure texture transform); (c) shader-side composition of the per-cell data (morphology/courses as data textures, coloring+features in fragment shader — likely the performance win); (d) hybrid. Inputs to gather before the discussion: current draw-call counts/frame costs per rung, the T-1175 polish requirements (per-vertex tapering wants Polygon2D strips or shaders anyway), and the wave-2 marker volume. Relates: T-1158 (viewer decomposition), T-1175 (polish pass), Tyre''s PR #197 Q5 note.
PREP SESSION COMPLETE + BRIEF SIGNED OFF (2026-07-25, Jeroen + lead + Tyre). Jeroen''s design outline captured and clarified over two AskUserQuestion rounds; Tyre delivered governance-delta/implications pass and ratified the brief (zero misrepresentations, zero blocking gaps). Signed-off workshop brief: docs/workshops/body-map-viewer/body-map-viewer-workshop-brief.md (session artifacts: jeroen-outline.md, clarifications.md, tyre-implications.md, prep-grounding.md in the same dir). Settled premises: server-side content determination per zoom step; client map-art function; RTT terrain + screen-space vector annotations baseline (locks T-1176 candidates b+a; c1 styling open); GPU presentation-only; stepped zoom (supersedes continuous — recorded at workshop filing); tile=1m only, ''gridunit (at zoom level)'' coined; data resolution tunable (1px ideal, 5x5 fallback); hydrology = deterministic equilibrium; client-primary cache (store shape open: server-side SQLite cache / godot-sqlite / file cache — SQLite ships in the Rust server, NOT the Godot client). Workshop: 5 seats (Dudley/Araminta/Stig/Tyre + Troblum r2), 2 rounds + lead interview, extendable at Jeroen''s call, round 1 GATED on measurements — see blocker edges. Expected Output 2 mandates an explicit deprecation sweep (Clerk audit) of all conflicting DQR records/tickets at filing.', 'in_progress', 'high', NULL, 'client', 'D-226', '2026-07-23 13:25:58.949', '2026-07-23 16:26:57.202', NULL, 'bd849919a626392f58d705a18e577d63', 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 ('06FRZKAV6ZK4KAZ6YCGFNBQ4T0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ①: equilibrium hydrology solver prototype + bench (512×256 and 4K-class) incl. cliff representation', 'Workshop gate measurement for body-map-viewer (brief: docs/workshops/body-map-viewer/body-map-viewer-workshop-brief.md, appendix ①; analysis: tyre-implications.md §3.1). Prototype the deterministic-equilibrium hydrology solver (priority-flood family: lake fill above sea level, overflow re-routing to sea or endorheic self-cycle, gorge carving when no easier path — settled end-state, pure function of seed + moisture/climate, extends drainage.rs D8). Bench at 512×256 (Layer-1 grid) AND a 4K-class canvas (~330K and up to 8.3M cells). MUST also output the cliff/two-heights-per-gridunit representation proposal (red flag 4) — the data-model answer, not just cost. Owner: Dudley. Effort est 12 days. This is the one measurement that could invalidate per-body-open settled hydrology if it lands in seconds not milliseconds.', 'backlog', 'high', NULL, 'server', 'D-227', '2026-07-23 16:27:07.959', '2026-07-23 16:27:07.959', NULL, 'ddef9bb8bbc7d672800e2330b03b2746', 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 ('06FRZKBTD7VKSXNWADWHV3QDQ8', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ②: per-gridunit derive bench at real canvas sizes (330K and 8.3M cells, production par_iter path)', 'Workshop gate measurement for body-map-viewer (brief appendix ②; tyre-implications.md §3.2). Run zoom_ladder_bench-class measurement of derive_at_metres on the PRODUCTION build_district_window_layer row-chunked par_iter path at ~330K cells (5×5 display-unit fallback, 4K-class) and ~8.3M cells (1×1 ideal). Known baselines: 1.785 µs/cell District cutoff, 1.454 µs/cell orbital, 0.617 ms per 4,096-cell served window — measured at 4,096 cells only; the chunking behavior at 330K+ is the exact extrapolation gap that killed the T-1143 planetary-rung cost story. MEASURE, do not extrapolate. Feeds Dudley Q3/Q4, red-flag-2 memory math, and Troblum''s round-2 cost pass. Owner: Dudley. Effort est 0.5 day.', 'backlog', 'high', NULL, 'server', 'D-226', '2026-07-23 16:27:15.945', '2026-07-23 16:27:15.945', NULL, '8953bb164d41bcb11dc3ecea32ba6607', 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 ('06FRZKCN13EF5TD9EZTXMSJ7EG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ④: wire-size table for step-canvas encodings (raw/bit-packed/RLE/PNG + MessagePack round-trip)', 'Workshop gate measurement for body-map-viewer (brief appendix ④; tyre-implications.md §3.3). For a ~330K-gridunit step canvas: dense classification arrays ≈ 45 bytes/gridunit ≈ 1.31.6 MB/step raw. Produce the comparison table: raw dense vs bit-packed vs run-length vs PNG-encoded raster, with an actual MessagePack round-trip on one real canvas (not arithmetic only). Directly informs the named-feature encoding decision (Araminta Q1) and whether the tagged-envelope migration (D-225 deferral) is forced. Owners: Araminta (encoding) + Dudley (real canvas + numbers). Effort est 0.5 day.', 'backlog', 'high', NULL, 'server', 'D-225', '2026-07-23 16:27:22.760', '2026-07-23 16:27:22.760', NULL, '675d9fc1377d3fdff5429f2349dfa992', 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 ('06FRZKDFB23YFACY2G4M6WRSXC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ⑤: Godot ImageTexture upload cost per step-cross (330K8.3M px)', 'Workshop measurement for body-map-viewer (brief appendix ⑤; tyre-implications.md §3.5). Measure ImageTexture.create_from_image (and update paths) for 330K8.3M px images on step-cross — client-side stutter risk for the RTT terrain layer''s hold-fetch-swap. NOT a round-1 gate (may land during round 1), but a real interactive-latency input for Stig Q2. Owner: Stig. Effort est 0.25 day.', 'backlog', 'medium', NULL, 'client', 'D-226', '2026-07-23 16:27:29.496', '2026-07-23 16:27:29.496', NULL, 'f8bc0a04880735556d5e528e1862cddc', 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 ('06FRCF0CX1Y5DTD3ER2JCDT27G', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Below-quarter measurement pass — block/tile rung costs and wire (gate for the opened (d) floor)', 'Gate ticket from Jeroen''s floor ruling (2026-07-21, D-226 amendment: ''we set a new BHAG so old restrictions are up for debate'' — the Atlas ladder may descend below quarter toward tile, implementation-gated on THIS pass). Measure what the design pass could not (docs/architecture/atlas-zoom-ladder-t1143.md SS2 marks block/tile ''not estimated''): per-cell derive cost at block (128m) and tile-adjacent spacings with the min_wavelength cutoff active; realistic window cell counts and wire bytes at those granularities vs the SS4 budget; memory/caching behavior; whether voxel_relief/voxel_mosaic math is reusable as-is for Atlas sampling or needs an extraction like derive_at_metres. Output: a costs appendix to the design doc + go/no-go recommendation per rung. Constraint preserved by the ruling: chunk/tile output never appears as a whole-body planetary layer — windowed viewport only. Blocked by derive_at_metres (the cutoff must exist to measure with it).
REPURPOSED as body-map-viewer workshop gate measurement ③ (2026-07-25, brief appendix ③: docs/workshops/body-map-viewer/body-map-viewer-workshop-brief.md). Unblocked since T-1162 shipped the octave-cutoff plumbing this was blocked on. Scope unchanged (block/tile rung per-cell costs + wire at those granularities); its numbers now price the bottom of the stepped gridunit ladder (~10px per 1m tile bottom-out). Round 1 of the workshop is gated on this landing alongside T-1177/T-1178/T-1179.', 'backlog', 'medium', NULL, 'server', 'D-226', '2026-07-21 19:51:50.248', '2026-07-23 16:27:35.477', NULL, '63b287f7b8e387a50f1631d9da3201b3', 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 ('06FRCF0CX1Y5DTD3ER2JCDT27G', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Below-quarter measurement pass — block/tile rung costs and wire (gate for the opened (d) floor)', 'Gate ticket from Jeroen''s floor ruling (2026-07-21, D-226 amendment: ''we set a new BHAG so old restrictions are up for debate'' — the Atlas ladder may descend below quarter toward tile, implementation-gated on THIS pass). Measure what the design pass could not (docs/architecture/atlas-zoom-ladder-t1143.md SS2 marks block/tile ''not estimated''): per-cell derive cost at block (128m) and tile-adjacent spacings with the min_wavelength cutoff active; realistic window cell counts and wire bytes at those granularities vs the SS4 budget; memory/caching behavior; whether voxel_relief/voxel_mosaic math is reusable as-is for Atlas sampling or needs an extraction like derive_at_metres. Output: a costs appendix to the design doc + go/no-go recommendation per rung. Constraint preserved by the ruling: chunk/tile output never appears as a whole-body planetary layer — windowed viewport only. Blocked by derive_at_metres (the cutoff must exist to measure with it).
REPURPOSED as body-map-viewer workshop gate measurement ③ (2026-07-25, brief appendix ③: docs/workshops/body-map-viewer/body-map-viewer-workshop-brief.md). Unblocked since T-1162 shipped the octave-cutoff plumbing this was blocked on. Scope unchanged (block/tile rung per-cell costs + wire at those granularities); its numbers now price the bottom of the stepped gridunit ladder (~10px per 1m tile bottom-out). Round 1 of the workshop is gated on this landing alongside T-1177/T-1178/T-1179.', 'ready', 'medium', NULL, 'server', 'D-226', '2026-07-21 19:51:50.248', '2026-07-23 16:27:39.311', NULL, '76dec706ced641ffe1033aee805cae0e', 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 ('06FRZKBTD7VKSXNWADWHV3QDQ8', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ②: per-gridunit derive bench at real canvas sizes (330K and 8.3M cells, production par_iter path)', 'Workshop gate measurement for body-map-viewer (brief appendix ②; tyre-implications.md §3.2). Run zoom_ladder_bench-class measurement of derive_at_metres on the PRODUCTION build_district_window_layer row-chunked par_iter path at ~330K cells (5×5 display-unit fallback, 4K-class) and ~8.3M cells (1×1 ideal). Known baselines: 1.785 µs/cell District cutoff, 1.454 µs/cell orbital, 0.617 ms per 4,096-cell served window — measured at 4,096 cells only; the chunking behavior at 330K+ is the exact extrapolation gap that killed the T-1143 planetary-rung cost story. MEASURE, do not extrapolate. Feeds Dudley Q3/Q4, red-flag-2 memory math, and Troblum''s round-2 cost pass. Owner: Dudley. Effort est 0.5 day.', 'ready', 'high', NULL, 'server', 'D-226', '2026-07-23 16:27:15.945', '2026-07-23 16:27:39.318', NULL, '73433e771b7ac522632d112c6e669a4f', 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 ('06FRZKAV6ZK4KAZ6YCGFNBQ4T0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ①: equilibrium hydrology solver prototype + bench (512×256 and 4K-class) incl. cliff representation', 'Workshop gate measurement for body-map-viewer (brief: docs/workshops/body-map-viewer/body-map-viewer-workshop-brief.md, appendix ①; analysis: tyre-implications.md §3.1). Prototype the deterministic-equilibrium hydrology solver (priority-flood family: lake fill above sea level, overflow re-routing to sea or endorheic self-cycle, gorge carving when no easier path — settled end-state, pure function of seed + moisture/climate, extends drainage.rs D8). Bench at 512×256 (Layer-1 grid) AND a 4K-class canvas (~330K and up to 8.3M cells). MUST also output the cliff/two-heights-per-gridunit representation proposal (red flag 4) — the data-model answer, not just cost. Owner: Dudley. Effort est 12 days. This is the one measurement that could invalidate per-body-open settled hydrology if it lands in seconds not milliseconds.', 'ready', 'high', NULL, 'server', 'D-227', '2026-07-23 16:27:07.959', '2026-07-23 16:27:39.318', NULL, '97b8fabcc206dd5cddc8f397d0915ddc', 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 ('06FRZKDFB23YFACY2G4M6WRSXC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ⑤: Godot ImageTexture upload cost per step-cross (330K8.3M px)', 'Workshop measurement for body-map-viewer (brief appendix ⑤; tyre-implications.md §3.5). Measure ImageTexture.create_from_image (and update paths) for 330K8.3M px images on step-cross — client-side stutter risk for the RTT terrain layer''s hold-fetch-swap. NOT a round-1 gate (may land during round 1), but a real interactive-latency input for Stig Q2. Owner: Stig. Effort est 0.25 day.', 'ready', 'medium', NULL, 'client', 'D-226', '2026-07-23 16:27:29.496', '2026-07-23 16:27:39.319', NULL, '37f990b776c224c05e0d4eaf93625c78', 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 ('06FRZKCN13EF5TD9EZTXMSJ7EG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ④: wire-size table for step-canvas encodings (raw/bit-packed/RLE/PNG + MessagePack round-trip)', 'Workshop gate measurement for body-map-viewer (brief appendix ④; tyre-implications.md §3.3). For a ~330K-gridunit step canvas: dense classification arrays ≈ 45 bytes/gridunit ≈ 1.31.6 MB/step raw. Produce the comparison table: raw dense vs bit-packed vs run-length vs PNG-encoded raster, with an actual MessagePack round-trip on one real canvas (not arithmetic only). Directly informs the named-feature encoding decision (Araminta Q1) and whether the tagged-envelope migration (D-225 deferral) is forced. Owners: Araminta (encoding) + Dudley (real canvas + numbers). Effort est 0.5 day.', 'ready', 'high', NULL, 'server', 'D-225', '2026-07-23 16:27:22.760', '2026-07-23 16:27:39.319', NULL, 'c4ec326c77a801398dce8bfa88eb11d1', 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 ('06FRZKAV6ZK4KAZ6YCGFNBQ4T0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ①: equilibrium hydrology solver prototype + bench (512×256 and 4K-class) incl. cliff representation', 'Workshop gate measurement for body-map-viewer (brief: docs/workshops/body-map-viewer/body-map-viewer-workshop-brief.md, appendix ①; analysis: tyre-implications.md §3.1). Prototype the deterministic-equilibrium hydrology solver (priority-flood family: lake fill above sea level, overflow re-routing to sea or endorheic self-cycle, gorge carving when no easier path — settled end-state, pure function of seed + moisture/climate, extends drainage.rs D8). Bench at 512×256 (Layer-1 grid) AND a 4K-class canvas (~330K and up to 8.3M cells). MUST also output the cliff/two-heights-per-gridunit representation proposal (red flag 4) — the data-model answer, not just cost. Owner: Dudley. Effort est 12 days. This is the one measurement that could invalidate per-body-open settled hydrology if it lands in seconds not milliseconds.', 'in_progress', 'high', NULL, 'server', 'D-227', '2026-07-23 16:27:07.959', '2026-07-23 16:31:37.581', NULL, '61c073617a346243e78fcbabed4cf69d', 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 ('06FRZKBTD7VKSXNWADWHV3QDQ8', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ②: per-gridunit derive bench at real canvas sizes (330K and 8.3M cells, production par_iter path)', 'Workshop gate measurement for body-map-viewer (brief appendix ②; tyre-implications.md §3.2). Run zoom_ladder_bench-class measurement of derive_at_metres on the PRODUCTION build_district_window_layer row-chunked par_iter path at ~330K cells (5×5 display-unit fallback, 4K-class) and ~8.3M cells (1×1 ideal). Known baselines: 1.785 µs/cell District cutoff, 1.454 µs/cell orbital, 0.617 ms per 4,096-cell served window — measured at 4,096 cells only; the chunking behavior at 330K+ is the exact extrapolation gap that killed the T-1143 planetary-rung cost story. MEASURE, do not extrapolate. Feeds Dudley Q3/Q4, red-flag-2 memory math, and Troblum''s round-2 cost pass. Owner: Dudley. Effort est 0.5 day.', 'in_progress', 'high', NULL, 'server', 'D-226', '2026-07-23 16:27:15.945', '2026-07-23 16:31:37.588', NULL, '05532e9b2b981513533cd58362c36344', 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 ('06FRZKCN13EF5TD9EZTXMSJ7EG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ④: wire-size table for step-canvas encodings (raw/bit-packed/RLE/PNG + MessagePack round-trip)', 'Workshop gate measurement for body-map-viewer (brief appendix ④; tyre-implications.md §3.3). For a ~330K-gridunit step canvas: dense classification arrays ≈ 45 bytes/gridunit ≈ 1.31.6 MB/step raw. Produce the comparison table: raw dense vs bit-packed vs run-length vs PNG-encoded raster, with an actual MessagePack round-trip on one real canvas (not arithmetic only). Directly informs the named-feature encoding decision (Araminta Q1) and whether the tagged-envelope migration (D-225 deferral) is forced. Owners: Araminta (encoding) + Dudley (real canvas + numbers). Effort est 0.5 day.', 'in_progress', 'high', NULL, 'server', 'D-225', '2026-07-23 16:27:22.760', '2026-07-23 16:31:37.588', NULL, '712e42ed863b9e3105a2c5997027e165', 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 ('06FRCF0CX1Y5DTD3ER2JCDT27G', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Below-quarter measurement pass — block/tile rung costs and wire (gate for the opened (d) floor)', 'Gate ticket from Jeroen''s floor ruling (2026-07-21, D-226 amendment: ''we set a new BHAG so old restrictions are up for debate'' — the Atlas ladder may descend below quarter toward tile, implementation-gated on THIS pass). Measure what the design pass could not (docs/architecture/atlas-zoom-ladder-t1143.md SS2 marks block/tile ''not estimated''): per-cell derive cost at block (128m) and tile-adjacent spacings with the min_wavelength cutoff active; realistic window cell counts and wire bytes at those granularities vs the SS4 budget; memory/caching behavior; whether voxel_relief/voxel_mosaic math is reusable as-is for Atlas sampling or needs an extraction like derive_at_metres. Output: a costs appendix to the design doc + go/no-go recommendation per rung. Constraint preserved by the ruling: chunk/tile output never appears as a whole-body planetary layer — windowed viewport only. Blocked by derive_at_metres (the cutoff must exist to measure with it).
REPURPOSED as body-map-viewer workshop gate measurement ③ (2026-07-25, brief appendix ③: docs/workshops/body-map-viewer/body-map-viewer-workshop-brief.md). Unblocked since T-1162 shipped the octave-cutoff plumbing this was blocked on. Scope unchanged (block/tile rung per-cell costs + wire at those granularities); its numbers now price the bottom of the stepped gridunit ladder (~10px per 1m tile bottom-out). Round 1 of the workshop is gated on this landing alongside T-1177/T-1178/T-1179.', 'in_progress', 'medium', NULL, 'server', 'D-226', '2026-07-21 19:51:50.248', '2026-07-23 16:31:37.588', NULL, 'cdc96a05f582115e1ce211e78d496fe6', 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 ('06FRZKDFB23YFACY2G4M6WRSXC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ⑤: Godot ImageTexture upload cost per step-cross (330K8.3M px)', 'Workshop measurement for body-map-viewer (brief appendix ⑤; tyre-implications.md §3.5). Measure ImageTexture.create_from_image (and update paths) for 330K8.3M px images on step-cross — client-side stutter risk for the RTT terrain layer''s hold-fetch-swap. NOT a round-1 gate (may land during round 1), but a real interactive-latency input for Stig Q2. Owner: Stig. Effort est 0.25 day.', 'in_progress', 'medium', NULL, 'client', 'D-226', '2026-07-23 16:27:29.496', '2026-07-23 16:31:37.589', NULL, '4ba81e04e267da1df792e1cdb1bb4e0d', 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 ('06FRZKAV6ZK4KAZ6YCGFNBQ4T0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ①: equilibrium hydrology solver prototype + bench (512×256 and 4K-class) incl. cliff representation', 'Workshop gate measurement for body-map-viewer (brief: docs/workshops/body-map-viewer/body-map-viewer-workshop-brief.md, appendix ①; analysis: tyre-implications.md §3.1). Prototype the deterministic-equilibrium hydrology solver (priority-flood family: lake fill above sea level, overflow re-routing to sea or endorheic self-cycle, gorge carving when no easier path — settled end-state, pure function of seed + moisture/climate, extends drainage.rs D8). Bench at 512×256 (Layer-1 grid) AND a 4K-class canvas (~330K and up to 8.3M cells). MUST also output the cliff/two-heights-per-gridunit representation proposal (red flag 4) — the data-model answer, not just cost. Owner: Dudley. Effort est 12 days. This is the one measurement that could invalidate per-body-open settled hydrology if it lands in seconds not milliseconds.', 'review', 'high', NULL, 'server', 'D-227', '2026-07-23 16:27:07.959', '2026-07-23 17:28:10.723', NULL, '25bbbdd52c7a1f8bd08f4dfe8ca007a4', 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 ('06FRZKBTD7VKSXNWADWHV3QDQ8', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ②: per-gridunit derive bench at real canvas sizes (330K and 8.3M cells, production par_iter path)', 'Workshop gate measurement for body-map-viewer (brief appendix ②; tyre-implications.md §3.2). Run zoom_ladder_bench-class measurement of derive_at_metres on the PRODUCTION build_district_window_layer row-chunked par_iter path at ~330K cells (5×5 display-unit fallback, 4K-class) and ~8.3M cells (1×1 ideal). Known baselines: 1.785 µs/cell District cutoff, 1.454 µs/cell orbital, 0.617 ms per 4,096-cell served window — measured at 4,096 cells only; the chunking behavior at 330K+ is the exact extrapolation gap that killed the T-1143 planetary-rung cost story. MEASURE, do not extrapolate. Feeds Dudley Q3/Q4, red-flag-2 memory math, and Troblum''s round-2 cost pass. Owner: Dudley. Effort est 0.5 day.', 'review', 'high', NULL, 'server', 'D-226', '2026-07-23 16:27:15.945', '2026-07-23 17:28:10.725', NULL, '5bb2e3e8039f1150466150ce991b7bea', 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 ('06FRCF0CX1Y5DTD3ER2JCDT27G', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Below-quarter measurement pass — block/tile rung costs and wire (gate for the opened (d) floor)', 'Gate ticket from Jeroen''s floor ruling (2026-07-21, D-226 amendment: ''we set a new BHAG so old restrictions are up for debate'' — the Atlas ladder may descend below quarter toward tile, implementation-gated on THIS pass). Measure what the design pass could not (docs/architecture/atlas-zoom-ladder-t1143.md SS2 marks block/tile ''not estimated''): per-cell derive cost at block (128m) and tile-adjacent spacings with the min_wavelength cutoff active; realistic window cell counts and wire bytes at those granularities vs the SS4 budget; memory/caching behavior; whether voxel_relief/voxel_mosaic math is reusable as-is for Atlas sampling or needs an extraction like derive_at_metres. Output: a costs appendix to the design doc + go/no-go recommendation per rung. Constraint preserved by the ruling: chunk/tile output never appears as a whole-body planetary layer — windowed viewport only. Blocked by derive_at_metres (the cutoff must exist to measure with it).
REPURPOSED as body-map-viewer workshop gate measurement ③ (2026-07-25, brief appendix ③: docs/workshops/body-map-viewer/body-map-viewer-workshop-brief.md). Unblocked since T-1162 shipped the octave-cutoff plumbing this was blocked on. Scope unchanged (block/tile rung per-cell costs + wire at those granularities); its numbers now price the bottom of the stepped gridunit ladder (~10px per 1m tile bottom-out). Round 1 of the workshop is gated on this landing alongside T-1177/T-1178/T-1179.', 'review', 'medium', NULL, 'server', 'D-226', '2026-07-21 19:51:50.248', '2026-07-23 17:28:10.725', NULL, '98adde08162e6d4019b92020e025cceb', 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 ('06FRZKCN13EF5TD9EZTXMSJ7EG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ④: wire-size table for step-canvas encodings (raw/bit-packed/RLE/PNG + MessagePack round-trip)', 'Workshop gate measurement for body-map-viewer (brief appendix ④; tyre-implications.md §3.3). For a ~330K-gridunit step canvas: dense classification arrays ≈ 45 bytes/gridunit ≈ 1.31.6 MB/step raw. Produce the comparison table: raw dense vs bit-packed vs run-length vs PNG-encoded raster, with an actual MessagePack round-trip on one real canvas (not arithmetic only). Directly informs the named-feature encoding decision (Araminta Q1) and whether the tagged-envelope migration (D-225 deferral) is forced. Owners: Araminta (encoding) + Dudley (real canvas + numbers). Effort est 0.5 day.', 'review', 'high', NULL, 'server', 'D-225', '2026-07-23 16:27:22.760', '2026-07-23 17:28:10.726', NULL, '1195ca54ac02ea0f33fe408ecefaa3b9', 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 ('06FRZKDFB23YFACY2G4M6WRSXC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ⑤: Godot ImageTexture upload cost per step-cross (330K8.3M px)', 'Workshop measurement for body-map-viewer (brief appendix ⑤; tyre-implications.md §3.5). Measure ImageTexture.create_from_image (and update paths) for 330K8.3M px images on step-cross — client-side stutter risk for the RTT terrain layer''s hold-fetch-swap. NOT a round-1 gate (may land during round 1), but a real interactive-latency input for Stig Q2. Owner: Stig. Effort est 0.25 day.', 'review', 'medium', NULL, 'client', 'D-226', '2026-07-23 16:27:29.496', '2026-07-23 17:28:10.726', NULL, '4b979135194bb90d460f9f3d056069fa', 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 ('06FRZKAV6ZK4KAZ6YCGFNBQ4T0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ①: equilibrium hydrology solver prototype + bench (512×256 and 4K-class) incl. cliff representation', 'Workshop gate measurement for body-map-viewer (brief: docs/workshops/body-map-viewer/body-map-viewer-workshop-brief.md, appendix ①; analysis: tyre-implications.md §3.1). Prototype the deterministic-equilibrium hydrology solver (priority-flood family: lake fill above sea level, overflow re-routing to sea or endorheic self-cycle, gorge carving when no easier path — settled end-state, pure function of seed + moisture/climate, extends drainage.rs D8). Bench at 512×256 (Layer-1 grid) AND a 4K-class canvas (~330K and up to 8.3M cells). MUST also output the cliff/two-heights-per-gridunit representation proposal (red flag 4) — the data-model answer, not just cost. Owner: Dudley. Effort est 12 days. This is the one measurement that could invalidate per-body-open settled hydrology if it lands in seconds not milliseconds.', 'done', 'high', NULL, 'server', 'D-227', '2026-07-23 16:27:07.959', '2026-07-23 18:06:12.142', NULL, '323b4aebaae21b94da208ba73908ebd4', 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 ('06FRZKBTD7VKSXNWADWHV3QDQ8', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ②: per-gridunit derive bench at real canvas sizes (330K and 8.3M cells, production par_iter path)', 'Workshop gate measurement for body-map-viewer (brief appendix ②; tyre-implications.md §3.2). Run zoom_ladder_bench-class measurement of derive_at_metres on the PRODUCTION build_district_window_layer row-chunked par_iter path at ~330K cells (5×5 display-unit fallback, 4K-class) and ~8.3M cells (1×1 ideal). Known baselines: 1.785 µs/cell District cutoff, 1.454 µs/cell orbital, 0.617 ms per 4,096-cell served window — measured at 4,096 cells only; the chunking behavior at 330K+ is the exact extrapolation gap that killed the T-1143 planetary-rung cost story. MEASURE, do not extrapolate. Feeds Dudley Q3/Q4, red-flag-2 memory math, and Troblum''s round-2 cost pass. Owner: Dudley. Effort est 0.5 day.', 'done', 'high', NULL, 'server', 'D-226', '2026-07-23 16:27:15.945', '2026-07-23 18:06:12.145', NULL, '8c7518f5e39f198eafe61842923a3dc8', 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 ('06FRCF0CX1Y5DTD3ER2JCDT27G', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Below-quarter measurement pass — block/tile rung costs and wire (gate for the opened (d) floor)', 'Gate ticket from Jeroen''s floor ruling (2026-07-21, D-226 amendment: ''we set a new BHAG so old restrictions are up for debate'' — the Atlas ladder may descend below quarter toward tile, implementation-gated on THIS pass). Measure what the design pass could not (docs/architecture/atlas-zoom-ladder-t1143.md SS2 marks block/tile ''not estimated''): per-cell derive cost at block (128m) and tile-adjacent spacings with the min_wavelength cutoff active; realistic window cell counts and wire bytes at those granularities vs the SS4 budget; memory/caching behavior; whether voxel_relief/voxel_mosaic math is reusable as-is for Atlas sampling or needs an extraction like derive_at_metres. Output: a costs appendix to the design doc + go/no-go recommendation per rung. Constraint preserved by the ruling: chunk/tile output never appears as a whole-body planetary layer — windowed viewport only. Blocked by derive_at_metres (the cutoff must exist to measure with it).
REPURPOSED as body-map-viewer workshop gate measurement ③ (2026-07-25, brief appendix ③: docs/workshops/body-map-viewer/body-map-viewer-workshop-brief.md). Unblocked since T-1162 shipped the octave-cutoff plumbing this was blocked on. Scope unchanged (block/tile rung per-cell costs + wire at those granularities); its numbers now price the bottom of the stepped gridunit ladder (~10px per 1m tile bottom-out). Round 1 of the workshop is gated on this landing alongside T-1177/T-1178/T-1179.', 'done', 'medium', NULL, 'server', 'D-226', '2026-07-21 19:51:50.248', '2026-07-23 18:06:12.146', NULL, 'c248f2785286f98c3d60982bcfb9fd85', 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 ('06FRZKDFB23YFACY2G4M6WRSXC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ⑤: Godot ImageTexture upload cost per step-cross (330K8.3M px)', 'Workshop measurement for body-map-viewer (brief appendix ⑤; tyre-implications.md §3.5). Measure ImageTexture.create_from_image (and update paths) for 330K8.3M px images on step-cross — client-side stutter risk for the RTT terrain layer''s hold-fetch-swap. NOT a round-1 gate (may land during round 1), but a real interactive-latency input for Stig Q2. Owner: Stig. Effort est 0.25 day.', 'done', 'medium', NULL, 'client', 'D-226', '2026-07-23 16:27:29.496', '2026-07-23 18:06:12.147', NULL, '149c633463919bfebfbea2bebb8dc566', 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 ('06FRZKCN13EF5TD9EZTXMSJ7EG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Measurement ④: wire-size table for step-canvas encodings (raw/bit-packed/RLE/PNG + MessagePack round-trip)', 'Workshop gate measurement for body-map-viewer (brief appendix ④; tyre-implications.md §3.3). For a ~330K-gridunit step canvas: dense classification arrays ≈ 45 bytes/gridunit ≈ 1.31.6 MB/step raw. Produce the comparison table: raw dense vs bit-packed vs run-length vs PNG-encoded raster, with an actual MessagePack round-trip on one real canvas (not arithmetic only). Directly informs the named-feature encoding decision (Araminta Q1) and whether the tagged-envelope migration (D-225 deferral) is forced. Owners: Araminta (encoding) + Dudley (real canvas + numbers). Effort est 0.5 day.', 'done', 'high', NULL, 'server', 'D-225', '2026-07-23 16:27:22.760', '2026-07-23 18:06:12.147', NULL, '47f5693da9e161d70c3389969ff6d051', 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 ('06FS5XQW73B1P8WKR2BCZBFXZW', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Step-canvas serving: StepCanvasRequest/Response tagged envelope (D-255 wire migration)', 'The D-225-discharging migration per D-255(c): one new Inbound variant (StepCanvasRequest, required marker field step_canvas, extending the proven star_map/city_names/browse ShapeProbe pattern in server/src/bridge/mod.rs) + a dedicated StepCanvasResponse (NOT a field on AtlasLayerResponse — the D-226 §2 ceiling is re-scoped to the legacy carrier). Serves all six rungs of the D-255(a) ladder — rung-0 Global variable-extent is a field value, not a different shape; fixed rungs viewport-sized per D-255(b). Encoding: PNG-per-field dense (8 fields: 6 static + glaciation/flooded sim-state L8 planes) + MessagePack-native sparse (courses, cliffs) per the T-1179 measured table. SERVER CACHE TIERS ARE THIS TICKET''S SCOPE TOO (SI gap 3): the D-203-shaped resident global tier (~8.85MB population-wide, measured) + TTL(detail, time, distance) for finer rungs + SIM_STATE_TTL[field] = 1x the field''s own fastest driving clock-bucket per body class (flooded: tidal bucket moon-bearing / seasonal moonless; glaciation: seasonal — D-227 amendment (1), Dudley+Araminta joint formula). ACCEPTANCE GATE (SI gap 2, mandatory per D-227 amendment (3)): byte-identical output test — cache-hit derive path == cache-miss derive path for every rung; the seed-chaining cache-accelerated-pure-function correctness gate, same shape as T-1170''s window-independence invariant. Implementation note carried from measurement S2: courses cost +38-87% at chunk/block spacing (station count scales inversely with rung spacing — 1,732 vs 867 points/course measured); a station-spacing cap independent of rung spacing is a named candidate optimization, decision at implementation. Design sources: dudley-round2.md §(a), tyre-round2.md §(b), D-255(c)(d)(f). Legacy district_window survives byte-unchanged until its consumer is replaced.', 'backlog', 'high', NULL, 'server', 'D-255', '2026-07-24 07:11:27.800', '2026-07-24 07:11:27.800', NULL, 'd6a9e1781177ade8a4dfadb912ad5e6a', 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 ('06FS5XT949C8D2JZRWDX2CRZN8', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Client map component: RTT terrain + screen-space annotations + stepped transport (D-255)', 'The two-layer client rebuild per D-255(a)(b)(e): a render-to-texture terrain layer (per-step data canvas colorized CPU-side — c1 ruling: Image.set_pixel, measured 77.5 ns/cell flat, NOT a hand-rolled buffer write which measured 2x slower; L8 planes where single-channel per measurement ⑤; texture.update reuse on step-cross) + an unscaled screen-space annotation sibling (names, glyphs, course polylines, markers — literal px sizes, positions world→screen). Stepped transport: discrete rung index 0-5, scroll steps, cursor-anchored, edge-scroll pan, full-zoom-out reset to the rung-0 Global frame; hold-fetch-swap on step-cross (morph/tween optional cosmetic follow-on, never re-derivation); step-dependent display ratio (1x1 deep/mid, ~5x5 shallow — presentation only, never spacing/cache-key). EXECUTES THE FULL _canvas.scale RETIREMENT PATH in the same change (tyre-round2.md ~line 1074): (1) _canvas.scale/_canvas.position/_apply_transform continuous-scale model; (2) the _zs/_zs_stroke/_zs_ring_radius compensation family in atlas_window_geometry_nature.gd — deleted wholesale; (3) select_rung + MAX_COVERAGE_M + compute_tile_grid (T-1152/T-1153 code); (4) the AtlasViewer/orbital-mosaic-vs-window two-code-path split (collapses to ''Global opener is rung 0, one viewer one path''); (5) _view_zoom float + _canonical_fit_zoom. Their test suites (coverage-walk unit tests, continuous-zoom seam tests, compute_tile_grid mosaic tests) are DELETED in the same change — never leave old goldens asserting against retired frames (the T-1157 dead-goldens rule); replacement coverage lands under re-scoped T-1157. SURVIVING SURFACES — do not delete (SI gap 1): atlas_window_tile_set.gd''s LRU SHAPE survives as the skeleton for the client cache-store ticket (adapt it, do not delete it — it is not on the retirement path); _filter_for_granularity_v2 (NEAREST/LINEAR per rung is still a real RTT draw-time question); compositing/legend/overlay-bar chrome (call-site updates only). Determinism boundary per D-255(e): client interpolates only within the closed server-supplied input set. Design sources: stig-round1.md, stig-round2.md, D-255(a)(b)(e).', 'backlog', 'high', NULL, 'client', 'D-255', '2026-07-24 07:11:47.490', '2026-07-24 07:11:47.490', NULL, '8c2daef21d9f83357442fcf9301da982', 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 ('06FS5XW0ZMPVYA2CPBBTP88TQC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Client cache store: FileAccess dir, two-axis eviction, version tag, retention cap (D-255)', 'The client-side cache tiers per D-255(d) and Stig''s three-tier spec (stig-round2.md (c)): in-memory LRU (adapting atlas_window_tile_set.gd''s surviving LRU skeleton from the map-component ticket) + disk-backed FileAccess cache dir with index. Two-axis eviction per D-227 amendment (1) — staleness and storage are DISTINCT: geometry entries never go stale (determinism; byte-valid forever) and are evicted only by (2a) time-since-last-visit (Jeroen''s storage-thrift ruling) and (2b) LRU capacity budget, two independent sweeps kept separate; sim-state entries (frozen/flooded planes) carry the real staleness TTL (SIM_STATE_TTL formula — served/invalidated per the serving ticket''s server-side formula). Rung-0 Global entries: retention floor, never swept. HARDENING REQUIREMENTS from D-255(d), both mandatory: (i) per-body deep-rung retention cap (max resident chunk/block-spacing tile count or disk quota per body, independent of the rung-0 floor — the structural closure of the D-226(d) accumulation gap; the AtlasAgentInterface QA channel inherits the same cap); (ii) every persistent entry carries a schema/version tag (project version or generator stamp) — read-time mismatch = cache miss + re-fetch, NEVER decode (the D-192 co-ship boundary; a disk cache survives game updates). Index-entry schema sketch in stig-round2.md (key/file_path/tier/written_at/last_read_at/size_bytes/sim_ttl/retention_floor); sweep triggers on body-open + coarse background timer, never per-frame. Explicitly rejected by the workshop: SQLite in any shape (server-side relocation, godot-sqlite addon) — plain FileAccess + index won on both agents'' independent analyses. Design sources: stig-round2.md (c), dudley-round2.md (d), D-255(d), D-227 amendments (1)(2).', 'backlog', 'high', NULL, 'client', 'D-255', '2026-07-24 07:12:01.789', '2026-07-24 07:12:01.789', NULL, '671ce378f0b2adbde098e4ef64cc8ea7', 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 ('06FS5XXY5GNCQEXYAYQEQD0CHM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Hydrology into the pipeline: productionize the T-1177 solver + lake morphology sourcing (D-255)', 'Two halves, one ticket (the D-227 lakes amendment''s implementation; converged in araminta-round2.md §(e) + dudley-interview2-response.md CORRECTED section). HALF 1 — productionize: wire server/src/atlas/hydrology_equilibrium.rs (currently an inert workshop prototype, zero non-test callers) into the derive pipeline: solve once per body at rung-0 Global populate (measured ~24ms/body at 512x256; population survey: all 267 bodies ~0.86s), result held with the global tier (D-203-shaped, keep-always). HALF 2 — lake sourcing: MorphologyZone::Lake (discriminant 1 — ALREADY in the frozen D-239 17-zone vocabulary, no widening) becomes sourced from the settled solver: at derive time a gridunit is Lake when bilinear-sampled HydrologyResult.filled_scaled exceeds bilinear-sampled original elevation (the continuous filled-surface comparison — lake edges refine with rung like coastlines, per D-166; NEVER project basin-cell membership as a discrete lookup, which gives blocky non-refining edges), with today''s ocean_fraction_q>=60 heuristic as fallback where no solve exists. This is seed-chaining mechanism B (coarse continuous primitive sampled fresh per rung — D-255(f)). STATIC classification — distinct from the sim-state flooded plane; a lake''s existence must never flicker on a TTL. NO endorheic bit anywhere (ruled: the cue is outflow-course presence, delivered by the separate outlet-wiring ticket; until that lands the map shows lakes without the drains-vs-closed distinction — honest sequencing per D-227 amendment (4)). Byte cost: zero new wire bytes (existing field). Uniform moisture_q=55 scope limit from the survey noted: affects endorheic/overflow split only, never lake extent.', 'backlog', 'medium', NULL, 'server', 'D-227', '2026-07-24 07:12:17.452', '2026-07-24 07:12:17.452', NULL, '3dafd698cf9833cf55198d56384eb275', 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 ('06FS5XZF4A978WS1J66Q2JBK8C', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Basin-outlet → course-network wiring: the endorheic cue deliverable (D-255)', 'Thread HydrologyResult overflow basins'' resolved outlet_path/spill points into RiverNetwork/courses as D8 downstream continuations, so an overflow lake''s exit river appears in the served courses and an endorheic basin''s absence of one reads as ''no course leaves this lake''. THE RULED ENDORHEIC CUE (D-227 amendment (4), converged Dudley+Araminta): outflow-course PRESENCE — no wire bit, no 18th zone (proportionality: 1,030/22,270 = 4.63% endorheic doesn''t justify widening the frozen vocabulary; the inference is definitionally sound with no misfire case — every Overflow basin has a guaranteed non-empty outlet_path (tested: overflowing_basin_has_nonempty_outlet_path), every Endorheic none). Pre-cleared ADDITIVE by T-1170 Ruling 7b: the RIVER_DOWNSTREAM_TERMINAL sentinel was reserved exactly for endorheic interior sinks, and river_course::build_edges is already no-op-safe on TERMINAL (verified) — not a wire migration. Required regardless of the cue: overflow lakes must show their exit rivers to be hydrologically honest on the map (a lake that drains but shows no outlet river is simply wrong). Until this lands, the map shows lakes (via the lake-sourcing ticket) but not the drains-vs-closed distinction — the recorded honest sequencing. Endorheic basins map to TERMINAL; overflow outlets extend the D8 network from the spill cell along outlet_path. Design sources: dudley-interview2-response.md CORRECTED section, araminta-round2.md §(e), t1177-hydrology.md, D-227 amendment (4).', 'backlog', 'medium', NULL, 'server', 'D-227', '2026-07-24 07:12:29.986', '2026-07-24 07:12:29.986', NULL, 'c328c9c06c251f04977ce9350dc13df8', 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 ('06FRXGSXBZKXTHR7MR5K9H2HRG', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Batch vs window derive paths sample different world positions for the same DistrictPos', 'Pre-existing architectural quirk found by Dudley during T-1168 integration testing (2026-07-23, not introduced by that batch): derive_district_profile''s cell-aggregate-centre sampling and the window path''s district-origin sampling resolve to genuinely different world positions for ''the same DistrictPos'' — full DistrictProfile output legitimately differs between paths (morphology/elev/moisture) at the same nominal district. His integration test was redesigned to compare the riparian signal at a shared exact world position instead (the right test). Investigate whether any existing batch/window consumer pair assumes positional agreement (city placement vs window rendering?), decide the canonical sampling convention, and either unify or document the divergence as intentional with the reasoning.
D-255 (2026-07-24): the tagged-envelope step-canvas serving (T-1181) depends on batch and window derive agreeing cell-for-cell — this bug now also blocks the step-canvas serving path, and priority is raised accordingly (Tyre disposition, body-map-viewer workshop). No supersession; intent unchanged.', 'backlog', 'low', NULL, 'server', NULL, '2026-07-23 11:36:27.743', '2026-07-24 07:12:48.892', NULL, '2150cc98e2294ca1d408a8aa18db92b1', 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 ('06FRXKFEJMDEPQ0PZ5Q862DHKG', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas map-reading polish pass — river/nature presentation refinements (single large pass)', 'Jeroen, 2026-07-23, reviewing the T-1170 course captures: ''there are a bunch of graphical cleanups I would want to suggest around line thickness and tapering off to a point at the source to make the map read more fluent, but I feel map polish like that is better off as a single large pass later.'' This ticket is that pass''s collector — park presentation refinements here rather than folding them into feature batches. Seeded items: (1) river line thickness tuning across rungs (beyond the functional per-class defaults Araminta confirmed in T-1170); (2) SOURCE TAPERING — courses should taper to a point at their upstream source rather than starting at full class width (the classic cartographic river grammar; likely per-vertex width via Polygon2D strip or per-segment width ramp, since draw_polyline is single-width); (3) candidates declined-as-scope during T-1170 review: class-tinted color ramp (Araminta: width+opacity sufficient for now, hue is solvable-later). Accumulate further items via pql ticket append as they come up in captures/reviews. Scope discipline: correctness issues (ruled values not rendering, clip bugs) are NEVER parked here — they get fixed in their own batches; this pass is aesthetics only. Schedule: after the nature layer stands (post T-1156 wave 2 at the earliest), as one coherent Araminta-led pass.
REFERENCE (Jeroen, 2026-07-23): RimWorld''s world map as the fluency benchmark — ''the rimworld maps do roads, rivers, coasts and biomes quite well'' (hex substrate explicitly NOT us; the techniques transfer, the tiling doesn''t). Extracted techniques for this pass: (1) RIVERS AS NETWORKS — visible tributary-joins-trunk convergence, subtle width variation along the run, taper-to-source (his earlier item 2), thin/consistent/restrained overall; (2) ROADS VS RIVERS disambiguated by COLOR + STRAIGHTNESS (grey chord-like vs blue winding), not width — the wave-2 road-rendering rule; (3) COASTS as a shoreline GRADIENT — beach/transition band + subtle near-shore water shading (our coastal/transition morphology class already exists; presentation only); (4) BIOME/RELIEF TEXTURE — stipple/texture density carrying hills/mountains/forest on top of hue; strong fit for us since T-1162''s vegetation-patchiness and relief fields can DRIVE stipple density (texture as data, not decoration); (5) geographic LABELS do heavy fluency lifting — that half lives in T-1169, coordinate the passes. Screenshot reference in the session record 2026-07-23.
Reference screenshot saved in-repo: docs/design/references/rimworld-world-map-fluency.jpg (+ README note in that dir) — supersedes the ''session record'' pointer in the previous append.
RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): the per-vertex river tapering/width-grammar work lands on the NEW screen-space annotation layer (T-1182) via Polygon2D strips — draw_polyline cannot taper. The c1 styling-engine blocker is DISCHARGED: measurement ⑥ landed (Image.set_pixel 77.5 ns/cell flat) and the call resolved CPU-first; shaders deferred, not a precondition. This ticket now depends only on the annotation layer landing (blocked-by T-1182, edge added). Not cancelled — re-homed onto the new component, unblocked otherwise.', 'backlog', 'low', NULL, 'client', 'D-226', '2026-07-23 11:48:08.469', '2026-07-24 07:12:51.865', NULL, '150a69959f11e86bd0d84d33977254dc', 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 ('06FRXGSXBZKXTHR7MR5K9H2HRG', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Batch vs window derive paths sample different world positions for the same DistrictPos', 'Pre-existing architectural quirk found by Dudley during T-1168 integration testing (2026-07-23, not introduced by that batch): derive_district_profile''s cell-aggregate-centre sampling and the window path''s district-origin sampling resolve to genuinely different world positions for ''the same DistrictPos'' — full DistrictProfile output legitimately differs between paths (morphology/elev/moisture) at the same nominal district. His integration test was redesigned to compare the riparian signal at a shared exact world position instead (the right test). Investigate whether any existing batch/window consumer pair assumes positional agreement (city placement vs window rendering?), decide the canonical sampling convention, and either unify or document the divergence as intentional with the reasoning.
D-255 (2026-07-24): the tagged-envelope step-canvas serving (T-1181) depends on batch and window derive agreeing cell-for-cell — this bug now also blocks the step-canvas serving path, and priority is raised accordingly (Tyre disposition, body-map-viewer workshop). No supersession; intent unchanged.', 'backlog', 'high', NULL, 'server', NULL, '2026-07-23 11:36:27.743', '2026-07-24 07:13:02.546', NULL, '2750e1728d81d27338e4795b9ab238d6', 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 ('06FRJD6EKPBQE08NA8VHP692W8', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Redesign the atlas visual-capture goldens for the zoom ladder (visual_scenarios/visual_capture)', 'T-1153 broke the T-1120/T-1121 golden capture harness: client/tests/visual_scenarios.gd and visual_capture.gd call app._regional_screen._viewer.get_heightmap_texture()/show_body() — both crash against AtlasWindowViewer (regional_screen now wraps the ladder viewer; AtlasViewer is unreachable from nav). Confirmed NOT wired into tests/run-godot or the pre-push hook (no reference in either), so nothing blocks, but the 12 curated atlas_* goldens and the --atlas-matrix capture path are dead until redesigned for the ladder (capture at defined rungs/zooms instead of the fixed heightmap frame; the new canonical orbital frame is the natural replacement for the old whole-body shot). Also decide the fate of the retired AtlasViewer code these harnesses were the last live consumers of — likely deletable once T-1156 (overlay port) settles what carries forward.
Live round 4 addendum: client/tests/test_atlas_window_overlay_draw_smoke.gd (3 scenarios -> now 2 after removing a flaky harness-sanity check: single-window draw + tile-mosaic draw, both asserting real composited pixels via SubViewport render) is a standalone gdUnit file with its own headless-skip guard, added because this redesign hadn''t landed yet. When this ticket lands, migrate its 2 scenarios into the redesigned tests/visual.json harness (folding the standalone file away) for consistency with the rest of the real-rendering test suite — see docs/DEVOPS.md''s ''Real-rendering test exception'' note for the interim rationale.
RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): this becomes the capture harness for the STEPPED render architecture — goldens key on (body, step) canvases, not the retired continuous-zoom frames. The T-1152/T-1153 test suites (coverage-walk, continuous-zoom seams, compute_tile_grid mosaic) retire with their code in T-1182''s change; this re-scoped harness is their replacement and the eyeball-check substrate for every step canvas. More needed than ever — keep, don''t cancel.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-22 09:42:47.197', '2026-07-24 07:13:05.192', NULL, 'dd52cedc5657190dc60033ffdca63451', 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 ('06FRMPSM9GSWK586N5GBP2N22G', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Decompose atlas_window_viewer.gd — extract the canonical-frame + rung-reselection cluster', 'PR #192 review (Tyre): the viewer sits at exactly gdlint''s 1000-line cap, twice trimmed to fit, and fuses three separable responsibilities: (a) input handling (_gui_input/_process/_apply_pan_delta/edge-scroll), (b) request orchestration + rung reselection (_maybe_reselect_rung/_maybe_refloat_window), (c) the canonical-frame state machine (_zoom_at zoom floor/_is_at_canonical_frame/_maybe_reset_to_canonical_frame/_canonical_fit_zoom). All six live fix rounds of the T-1152/T-1153 batch traced to held-state/transform/lifetime coupling in this file — extract cluster (c) (+ likely (b)) into its own class BEFORE the next change breaches the cap and forces either a deadline extraction or comment-gutting that loses the hard-won live-round rationale comments. The tile-set (atlas_window_tile_set.gd) and geometry (atlas_window_geometry.gd) extractions are the precedent. Behavior-preserving refactor: the zoom-ladder suites (44+122+20 tests) are the safety net.
SUPERSEDED by the body-map-viewer render architecture (D-255, 2026-07-24): the canonical-frame + rung-reselection state machine this ticket would extract is exactly what changes shape under stepped zoom (no continuous zoom floor, no _canonical_fit_zoom, select_rung retired). Extracting code about to be deleted is waste (Stig''s round-1 call, confirmed by the workshop). The new component''s decomposition (input/pan, step-cross orchestration, two draw layers) is decided fresh in T-1182, not by reviving this ticket. Cancelled.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-22 15:04:20.812', '2026-07-24 07:13:14.171', NULL, 'b70e9d80468ec0f8295b561f99c98b2e', 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 ('06FRMPSM9GSWK586N5GBP2N22G', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Decompose atlas_window_viewer.gd — extract the canonical-frame + rung-reselection cluster', 'PR #192 review (Tyre): the viewer sits at exactly gdlint''s 1000-line cap, twice trimmed to fit, and fuses three separable responsibilities: (a) input handling (_gui_input/_process/_apply_pan_delta/edge-scroll), (b) request orchestration + rung reselection (_maybe_reselect_rung/_maybe_refloat_window), (c) the canonical-frame state machine (_zoom_at zoom floor/_is_at_canonical_frame/_maybe_reset_to_canonical_frame/_canonical_fit_zoom). All six live fix rounds of the T-1152/T-1153 batch traced to held-state/transform/lifetime coupling in this file — extract cluster (c) (+ likely (b)) into its own class BEFORE the next change breaches the cap and forces either a deadline extraction or comment-gutting that loses the hard-won live-round rationale comments. The tile-set (atlas_window_tile_set.gd) and geometry (atlas_window_geometry.gd) extractions are the precedent. Behavior-preserving refactor: the zoom-ladder suites (44+122+20 tests) are the safety net.
SUPERSEDED by the body-map-viewer render architecture (D-255, 2026-07-24): the canonical-frame + rung-reselection state machine this ticket would extract is exactly what changes shape under stepped zoom (no continuous zoom floor, no _canonical_fit_zoom, select_rung retired). Extracting code about to be deleted is waste (Stig''s round-1 call, confirmed by the workshop). The new component''s decomposition (input/pan, step-cross orchestration, two draw layers) is decided fresh in T-1182, not by reviving this ticket. Cancelled.', 'cancelled', 'medium', NULL, 'client', NULL, '2026-07-22 15:04:20.812', '2026-07-24 07:13:18.468', NULL, '955c12d603c68fda38f74a92ab6b942b', 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 ('06FRY9W24PV55Z6FMW8TEM7XER', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Design discussion: revise the Atlas map render mechanism (bypass the zoom-scaled-canvas error class)', 'Jeroen, 2026-07-23, during the T-1170 live rounds: ''I want to discuss and maybe revise the pixel scan/output mechanism for drawing the map. I feel we can be smarter about this and bypass an entire class of errors we are seeing as well as probably improve performance.'' DISCUSSION FIRST — this is a design conversation with Jeroen (pair/workshop mode), not autonomous work; scheduled after the T-1170/T-1168 cycle closes. The error class in evidence, all descending from immediate-mode draw calls inside a zoom-scaled canvas (_canvas.scale = view_zoom): the per-call-site zoom compensation discipline (_zs/_zs_stroke, silently degrading when one site is missed — PR #195 I1, PR #197 stroke sites), Godot''s line-rasterizer width floor (sub-1.0-canvas-unit strokes collapse to hairline — the T-1170 width bug), the radius-smaller-than-stroke draw_arc regime (the mouth-ring investigation), and arguably the wrap-image resolution family. Candidate directions to bring to the discussion: (a) Tyre''s unscaled screen-space sibling layer (positions transformed, sizes literal — already noted on T-1156 wave 2/T-1158); (b) render-to-texture per window/tile at native resolution, composited as textures (sizes become texel-exact, zoom becomes pure texture transform); (c) shader-side composition of the per-cell data (morphology/courses as data textures, coloring+features in fragment shader — likely the performance win); (d) hybrid. Inputs to gather before the discussion: current draw-call counts/frame costs per rung, the T-1175 polish requirements (per-vertex tapering wants Polygon2D strips or shaders anyway), and the wave-2 marker volume. Relates: T-1158 (viewer decomposition), T-1175 (polish pass), Tyre''s PR #197 Q5 note.
PREP SESSION COMPLETE + BRIEF SIGNED OFF (2026-07-25, Jeroen + lead + Tyre). Jeroen''s design outline captured and clarified over two AskUserQuestion rounds; Tyre delivered governance-delta/implications pass and ratified the brief (zero misrepresentations, zero blocking gaps). Signed-off workshop brief: docs/workshops/body-map-viewer/body-map-viewer-workshop-brief.md (session artifacts: jeroen-outline.md, clarifications.md, tyre-implications.md, prep-grounding.md in the same dir). Settled premises: server-side content determination per zoom step; client map-art function; RTT terrain + screen-space vector annotations baseline (locks T-1176 candidates b+a; c1 styling open); GPU presentation-only; stepped zoom (supersedes continuous — recorded at workshop filing); tile=1m only, ''gridunit (at zoom level)'' coined; data resolution tunable (1px ideal, 5x5 fallback); hydrology = deterministic equilibrium; client-primary cache (store shape open: server-side SQLite cache / godot-sqlite / file cache — SQLite ships in the Rust server, NOT the Godot client). Workshop: 5 seats (Dudley/Araminta/Stig/Tyre + Troblum r2), 2 rounds + lead interview, extendable at Jeroen''s call, round 1 GATED on measurements — see blocker edges. Expected Output 2 mandates an explicit deprecation sweep (Clerk audit) of all conflicting DQR records/tickets at filing.
DELIVERED (2026-07-24): the body-map-viewer workshop is this ticket''s output. D-255 filed (stepped Atlas render architecture: server-determines-content, client map-art function, six-level ladder Global+Region→Chunk, tagged-envelope wire, three-tier cache, cache-accelerated-pure-function seed-chaining, lakes from settled hydrology) + 12 in-place amendments (D-166/D-192/D-225/D-226/D-227/D-243 + river-courses-t1170 gloss) + Clerk audit clean (2 unswept conflicts found and fixed: t1143 doc superseded banner, CLAUDE.md Phase-4 row) + measurements ①–⑥ and the population survey all MEASURED + implementation chain T-1181T-1185 with reconciliations (T-1158 cancelled, T-1175/T-1157 re-scoped, T-1174 raised). Full record: docs/workshops/body-map-viewer/ (workshop-outcomes.md; architecture-briefing-final.md is the plain-language companion).', 'in_progress', 'high', NULL, 'client', 'D-226', '2026-07-23 13:25:58.949', '2026-07-24 07:13:23.420', NULL, '5e902693de50646d79225005c3c2875d', 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 ('06FRY9W24PV55Z6FMW8TEM7XER', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Design discussion: revise the Atlas map render mechanism (bypass the zoom-scaled-canvas error class)', 'Jeroen, 2026-07-23, during the T-1170 live rounds: ''I want to discuss and maybe revise the pixel scan/output mechanism for drawing the map. I feel we can be smarter about this and bypass an entire class of errors we are seeing as well as probably improve performance.'' DISCUSSION FIRST — this is a design conversation with Jeroen (pair/workshop mode), not autonomous work; scheduled after the T-1170/T-1168 cycle closes. The error class in evidence, all descending from immediate-mode draw calls inside a zoom-scaled canvas (_canvas.scale = view_zoom): the per-call-site zoom compensation discipline (_zs/_zs_stroke, silently degrading when one site is missed — PR #195 I1, PR #197 stroke sites), Godot''s line-rasterizer width floor (sub-1.0-canvas-unit strokes collapse to hairline — the T-1170 width bug), the radius-smaller-than-stroke draw_arc regime (the mouth-ring investigation), and arguably the wrap-image resolution family. Candidate directions to bring to the discussion: (a) Tyre''s unscaled screen-space sibling layer (positions transformed, sizes literal — already noted on T-1156 wave 2/T-1158); (b) render-to-texture per window/tile at native resolution, composited as textures (sizes become texel-exact, zoom becomes pure texture transform); (c) shader-side composition of the per-cell data (morphology/courses as data textures, coloring+features in fragment shader — likely the performance win); (d) hybrid. Inputs to gather before the discussion: current draw-call counts/frame costs per rung, the T-1175 polish requirements (per-vertex tapering wants Polygon2D strips or shaders anyway), and the wave-2 marker volume. Relates: T-1158 (viewer decomposition), T-1175 (polish pass), Tyre''s PR #197 Q5 note.
PREP SESSION COMPLETE + BRIEF SIGNED OFF (2026-07-25, Jeroen + lead + Tyre). Jeroen''s design outline captured and clarified over two AskUserQuestion rounds; Tyre delivered governance-delta/implications pass and ratified the brief (zero misrepresentations, zero blocking gaps). Signed-off workshop brief: docs/workshops/body-map-viewer/body-map-viewer-workshop-brief.md (session artifacts: jeroen-outline.md, clarifications.md, tyre-implications.md, prep-grounding.md in the same dir). Settled premises: server-side content determination per zoom step; client map-art function; RTT terrain + screen-space vector annotations baseline (locks T-1176 candidates b+a; c1 styling open); GPU presentation-only; stepped zoom (supersedes continuous — recorded at workshop filing); tile=1m only, ''gridunit (at zoom level)'' coined; data resolution tunable (1px ideal, 5x5 fallback); hydrology = deterministic equilibrium; client-primary cache (store shape open: server-side SQLite cache / godot-sqlite / file cache — SQLite ships in the Rust server, NOT the Godot client). Workshop: 5 seats (Dudley/Araminta/Stig/Tyre + Troblum r2), 2 rounds + lead interview, extendable at Jeroen''s call, round 1 GATED on measurements — see blocker edges. Expected Output 2 mandates an explicit deprecation sweep (Clerk audit) of all conflicting DQR records/tickets at filing.
DELIVERED (2026-07-24): the body-map-viewer workshop is this ticket''s output. D-255 filed (stepped Atlas render architecture: server-determines-content, client map-art function, six-level ladder Global+Region→Chunk, tagged-envelope wire, three-tier cache, cache-accelerated-pure-function seed-chaining, lakes from settled hydrology) + 12 in-place amendments (D-166/D-192/D-225/D-226/D-227/D-243 + river-courses-t1170 gloss) + Clerk audit clean (2 unswept conflicts found and fixed: t1143 doc superseded banner, CLAUDE.md Phase-4 row) + measurements ①–⑥ and the population survey all MEASURED + implementation chain T-1181T-1185 with reconciliations (T-1158 cancelled, T-1175/T-1157 re-scoped, T-1174 raised). Full record: docs/workshops/body-map-viewer/ (workshop-outcomes.md; architecture-briefing-final.md is the plain-language companion).', 'done', 'high', NULL, 'client', 'D-226', '2026-07-23 13:25:58.949', '2026-07-24 07:13:28.217', NULL, '0d5ca1f2955ad2d785d4dacda92a26f1', 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 ('06FB0TNSRZXCHGS16BFHSSGSV4', 'epic', '06FB0TNSRZE4CSS8YR0FCEXMK8', 'Phase 4: World generation (tile/chunk/block) — deterministic multilayer seed-to-tile cascade + per-layer Atlas maps + asset pipeline', 'Deliverable: Walkable generated world + asset creation catalog with production status. Automated tile/chunk/block generation (NPC layer ignored). Parallel asset pipeline: rural walls, barns, station walls, auto doors, cars, lamp posts, TVs, billboards etc. Depends on Phases 1-4 (world structure + rendering pipeline exist).
SCOPE CLARIFICATION (2026-07-24, Jeroen): the seed-to-tile cascade this phase builds is ALSO the mechanism that determines what exists where the player walks in Phase 5 — not an Atlas-only pipeline. In-world streaming derives the chunk neighborhood around the player (minimum the 3x3 chunks needed to draw everything; prefetch radius a tuning constant) via the same derivation, with coarser-layer context self-provided by function composition (D-255(f) cache-accelerated pure function): Atlas interaction is never a precondition, cache only accelerates, output byte-identical either way — the T-1181 cache-hit==cache-miss determinism gate protects exactly this property. Founding driver: D-012 (''chunks load/unload around the player; the world generates as you explore''), amended 2026-07-24 to point at the cascade records. The insert minimap (Q-093) is noted as a same-derivation consumer; its design defers to Phase 5.', 'in_progress', 'high', NULL, NULL, NULL, '2026-03-25 13:30:00', '2026-07-24 11:54:05.117', NULL, 'd6d93db1b98276ecbbf7beca050dba81', 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 ('06FRXGSXBZKXTHR7MR5K9H2HRG', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Batch vs window derive paths sample different world positions for the same DistrictPos', 'Pre-existing architectural quirk found by Dudley during T-1168 integration testing (2026-07-23, not introduced by that batch): derive_district_profile''s cell-aggregate-centre sampling and the window path''s district-origin sampling resolve to genuinely different world positions for ''the same DistrictPos'' — full DistrictProfile output legitimately differs between paths (morphology/elev/moisture) at the same nominal district. His integration test was redesigned to compare the riparian signal at a shared exact world position instead (the right test). Investigate whether any existing batch/window consumer pair assumes positional agreement (city placement vs window rendering?), decide the canonical sampling convention, and either unify or document the divergence as intentional with the reasoning.
D-255 (2026-07-24): the tagged-envelope step-canvas serving (T-1181) depends on batch and window derive agreeing cell-for-cell — this bug now also blocks the step-canvas serving path, and priority is raised accordingly (Tyre disposition, body-map-viewer workshop). No supersession; intent unchanged.', 'in_progress', 'high', NULL, 'server', NULL, '2026-07-23 11:36:27.743', '2026-07-24 20:56:44.313', NULL, '02d50d91c3613289446d113298460f58', 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 ('06FRXGSXBZKXTHR7MR5K9H2HRG', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Batch vs window derive paths sample different world positions for the same DistrictPos', 'Pre-existing architectural quirk found by Dudley during T-1168 integration testing (2026-07-23, not introduced by that batch): derive_district_profile''s cell-aggregate-centre sampling and the window path''s district-origin sampling resolve to genuinely different world positions for ''the same DistrictPos'' — full DistrictProfile output legitimately differs between paths (morphology/elev/moisture) at the same nominal district. His integration test was redesigned to compare the riparian signal at a shared exact world position instead (the right test). Investigate whether any existing batch/window consumer pair assumes positional agreement (city placement vs window rendering?), decide the canonical sampling convention, and either unify or document the divergence as intentional with the reasoning.
D-255 (2026-07-24): the tagged-envelope step-canvas serving (T-1181) depends on batch and window derive agreeing cell-for-cell — this bug now also blocks the step-canvas serving path, and priority is raised accordingly (Tyre disposition, body-map-viewer workshop). No supersession; intent unchanged.
RULING (2026-07-24): D-256 claimed and written on branch t1174-derive-sampling (rides the PR; syncs to main on merge). Canonical convention = derive(seed, absolute world metres) via the derive_at_metres family; batch 64x32 pseudo-grid re-scoped as a survey raster with a real SurveyCellPos newtype; derive_district_profile becomes a thin wrapper over an extracted riparian-parameterized core at survey-cell-centre positions (basin_direction preserved by post-call override; near_perennial_water_at plumbed, never the empty-slice default); skeleton morphology judgments move to exact-position derive; step canvases fenced to the window family. LayerRegionOutput rebuild deferred to T-1181 (tripwire verified: sole reader is the region_grid overlay); voxel carrier deferred to Phase 5. Consults: Tyre APPROVE-WITH-CHANGES, Dudley FEASIBLE-WITH-CHANGES (both incorporated). Settlement/road positions do NOT move (L1 attractor-sourced; T-1075 bake orthogonal); believability goldens regen, gates unchanged; window goldens must stay byte-green.', 'in_progress', 'high', NULL, 'server', NULL, '2026-07-23 11:36:27.743', '2026-07-24 21:38:41.718', NULL, 'ef598fd52d197f90132b715f298b1f15', 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 ('06FB0TNSRZZ5XKET93HPHKQHKW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Phase-4 test hardening — deferred review gaps (#953/#963 review)', 'Follow-ups from the Hoshe/Tyre pre-bake review (the BLOCKER/MAJOR items were fixed inline; these are the deferred MINOR/coverage items):
TESTS:
- features.rs: full per-type attractor reachability fixtures — craft heightmaps that guarantee LakeShore (enclosed depression), PassEntrance (saddle), PlainCenter (flat), RiverCrossing (confluence). Currently only RiverMouth/CoastalAccess/ValleyFloor are exercised on the slope/sine fixtures.
- heightmap.rs: 8-bit grayscale decode path; sea_level passthrough assertion; downsample identity/upsample early-return.
- drainage.rs: basin area_pct bit-for-bit determinism (only river_cells + basin count checked today).
- layer1.rs: attach_feature_names mountain branch with a non-empty mountain pool + ≥1 Alpine attractor.
- features.rs: thin_by_spacing behavior (spacing collisions, equirectangular column wrap).
- import_economics.py: automated idempotency test — two runs on a scratch DB yield identical atlas_city_names count (not doubled), and 0 rows for system ''GJ 0'' (Sol exemption). Wire alongside check-systems-db / pre-push.
- Wire test_sim_determinism.py into pre-push or a make target.
MINOR CODE NITS:
- drainage.rs ~480-488: comment the intentional isolated-basin fallback divergence from the old merge-to-basin-0 behavior (Tyre N1).
- planet_simulation.py ~787-788: oasis_water binary_dilation iterations are fixed-pixel — scale by GRID_W/512 for biome-display consistency at 1024 (cosmetic, Atlas only, not cascade; Tyre N2).
D-256/T-1174 finding (2026-07-25): the believability harness golden is INSENSITIVE to a total basin_direction regression — an all-North basin collapse (every survey cell defaulting) produced a byte-identical believability.json at current sample density (64/2048 districts; basin only reaches voxel meander phase/channel width, too localized for the coarse aggregate stats). The cross-namespace keying seam is now covered by the restored derive_all_districts_threads_supplied_basin_directions test (production-shaped survey keys), but the harness itself has no basin-sensitive metric. Candidate hardening: a basin-direction distribution stat (distinct directions >= 2 on a drained body) or a voxel-transect metric that moves with meander phase.', 'backlog', 'medium', NULL, 'server', NULL, '2026-05-23 06:09:45', '2026-07-24 22:38:59.482', NULL, '1aff700df70cc2f275222974be4ed75e', 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 ('06FRXGSXBZKXTHR7MR5K9H2HRG', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Batch vs window derive paths sample different world positions for the same DistrictPos', 'Pre-existing architectural quirk found by Dudley during T-1168 integration testing (2026-07-23, not introduced by that batch): derive_district_profile''s cell-aggregate-centre sampling and the window path''s district-origin sampling resolve to genuinely different world positions for ''the same DistrictPos'' — full DistrictProfile output legitimately differs between paths (morphology/elev/moisture) at the same nominal district. His integration test was redesigned to compare the riparian signal at a shared exact world position instead (the right test). Investigate whether any existing batch/window consumer pair assumes positional agreement (city placement vs window rendering?), decide the canonical sampling convention, and either unify or document the divergence as intentional with the reasoning.
D-255 (2026-07-24): the tagged-envelope step-canvas serving (T-1181) depends on batch and window derive agreeing cell-for-cell — this bug now also blocks the step-canvas serving path, and priority is raised accordingly (Tyre disposition, body-map-viewer workshop). No supersession; intent unchanged.
RULING (2026-07-24): D-256 claimed and written on branch t1174-derive-sampling (rides the PR; syncs to main on merge). Canonical convention = derive(seed, absolute world metres) via the derive_at_metres family; batch 64x32 pseudo-grid re-scoped as a survey raster with a real SurveyCellPos newtype; derive_district_profile becomes a thin wrapper over an extracted riparian-parameterized core at survey-cell-centre positions (basin_direction preserved by post-call override; near_perennial_water_at plumbed, never the empty-slice default); skeleton morphology judgments move to exact-position derive; step canvases fenced to the window family. LayerRegionOutput rebuild deferred to T-1181 (tripwire verified: sole reader is the region_grid overlay); voxel carrier deferred to Phase 5. Consults: Tyre APPROVE-WITH-CHANGES, Dudley FEASIBLE-WITH-CHANGES (both incorporated). Settlement/road positions do NOT move (L1 attractor-sourced; T-1075 bake orthogonal); believability goldens regen, gates unchanged; window goldens must stay byte-green.', 'review', 'high', NULL, 'server', NULL, '2026-07-23 11:36:27.743', '2026-07-24 23:18:26.773', NULL, '7442e6e6c69c84c53bc577c96a0a2b79', 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 ('06FSCXX7E3BXT7WG0BYDB6PJA0', 'bug', '06FBPPMZNNEV052DBYYY3A897C', 'Region-baseline latitude is pole-anchored while the derive core keys equator-anchored signed regions — northern hemisphere clamps to +90', 'Found during the PR #199 (T-1174/D-256) review fix round, PRE-EXISTING on the window path since the region climate stack (T-1078/T-1113) was wired into derive_at_metres. region_profile::region_centre_latitude_deg (region_profile.rs ~270-280) maps region row 0 to the north pole (+90) with lat_frac clamped to [0,1] — a pole-anchored, non-negative row convention. But the derive core (derive_at_metres_with_riparian, district_profile.rs ~1788) keys regions by floor-dividing EQUATOR-anchored world metres (negative wy = north), producing signed region rows (Earth-class: -49..+49). Consequence: every northern-hemisphere region has negative row -> region_centre_y_m negative -> lat_frac clamps to 0 -> baseline latitude +90 (polar) regardless of true latitude; southern-hemisphere rows 0..~48 read as compressed NORTHERN latitudes ~89..~0.7 (south pole reads as equator). Affects the region temperature baseline (and glaciation/moisture components derived from it) for the window path at every rung, and post-D-256 the batch/survey path inherits the same keys (still an improvement over the pre-D-256 body-wide region-(0,0) baseline, which read ~+89 everywhere). NOT touched in PR #199 to keep the review round scoped; goldens currently pin the wrong-latitude values. FIX SHAPE: make region_centre_latitude_deg mirror the derive core''s inverse mapping (equator-anchored signed rows: lat = -((ry+0.5)*REGION_M/meridian_m clamped to [-0.5,0.5])*180), audit the OTHER convention''s callers (the D-255 rung-0 canvas row space and the collapsed LayerRegionOutput builder use non-negative pole-anchored rows - decide ONE convention per D-256''s one-inverse-mapping principle, likely at the T-1181 rung-0 rebuild), regen affected goldens, and re-run the believability direction check. Related: D-256, D-243, T-1181, T-1078.', 'backlog', 'high', NULL, 'server', NULL, '2026-07-24 23:30:51.889', '2026-07-24 23:30:51.889', NULL, '6a7b5705a768da90b74449383d5a127b', 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 ('06FS5XQW73B1P8WKR2BCZBFXZW', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Step-canvas serving: StepCanvasRequest/Response tagged envelope (D-255 wire migration)', 'The D-225-discharging migration per D-255(c): one new Inbound variant (StepCanvasRequest, required marker field step_canvas, extending the proven star_map/city_names/browse ShapeProbe pattern in server/src/bridge/mod.rs) + a dedicated StepCanvasResponse (NOT a field on AtlasLayerResponse — the D-226 §2 ceiling is re-scoped to the legacy carrier). Serves all six rungs of the D-255(a) ladder — rung-0 Global variable-extent is a field value, not a different shape; fixed rungs viewport-sized per D-255(b). Encoding: PNG-per-field dense (8 fields: 6 static + glaciation/flooded sim-state L8 planes) + MessagePack-native sparse (courses, cliffs) per the T-1179 measured table. SERVER CACHE TIERS ARE THIS TICKET''S SCOPE TOO (SI gap 3): the D-203-shaped resident global tier (~8.85MB population-wide, measured) + TTL(detail, time, distance) for finer rungs + SIM_STATE_TTL[field] = 1x the field''s own fastest driving clock-bucket per body class (flooded: tidal bucket moon-bearing / seasonal moonless; glaciation: seasonal — D-227 amendment (1), Dudley+Araminta joint formula). ACCEPTANCE GATE (SI gap 2, mandatory per D-227 amendment (3)): byte-identical output test — cache-hit derive path == cache-miss derive path for every rung; the seed-chaining cache-accelerated-pure-function correctness gate, same shape as T-1170''s window-independence invariant. Implementation note carried from measurement S2: courses cost +38-87% at chunk/block spacing (station count scales inversely with rung spacing — 1,732 vs 867 points/course measured); a station-spacing cap independent of rung spacing is a named candidate optimization, decision at implementation. Design sources: dudley-round2.md §(a), tyre-round2.md §(b), D-255(c)(d)(f). Legacy district_window survives byte-unchanged until its consumer is replaced.', 'in_progress', 'high', NULL, 'server', 'D-255', '2026-07-24 07:11:27.800', '2026-07-24 23:52:05.687', NULL, '047e4a20983d55f97bb41ae909addbba', 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 ('06FS5XQW73B1P8WKR2BCZBFXZW', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Step-canvas serving: StepCanvasRequest/Response tagged envelope (D-255 wire migration)', 'The D-225-discharging migration per D-255(c): one new Inbound variant (StepCanvasRequest, required marker field step_canvas, extending the proven star_map/city_names/browse ShapeProbe pattern in server/src/bridge/mod.rs) + a dedicated StepCanvasResponse (NOT a field on AtlasLayerResponse — the D-226 §2 ceiling is re-scoped to the legacy carrier). Serves all six rungs of the D-255(a) ladder — rung-0 Global variable-extent is a field value, not a different shape; fixed rungs viewport-sized per D-255(b). Encoding: PNG-per-field dense (8 fields: 6 static + glaciation/flooded sim-state L8 planes) + MessagePack-native sparse (courses, cliffs) per the T-1179 measured table. SERVER CACHE TIERS ARE THIS TICKET''S SCOPE TOO (SI gap 3): the D-203-shaped resident global tier (~8.85MB population-wide, measured) + TTL(detail, time, distance) for finer rungs + SIM_STATE_TTL[field] = 1x the field''s own fastest driving clock-bucket per body class (flooded: tidal bucket moon-bearing / seasonal moonless; glaciation: seasonal — D-227 amendment (1), Dudley+Araminta joint formula). ACCEPTANCE GATE (SI gap 2, mandatory per D-227 amendment (3)): byte-identical output test — cache-hit derive path == cache-miss derive path for every rung; the seed-chaining cache-accelerated-pure-function correctness gate, same shape as T-1170''s window-independence invariant. Implementation note carried from measurement S2: courses cost +38-87% at chunk/block spacing (station count scales inversely with rung spacing — 1,732 vs 867 points/course measured); a station-spacing cap independent of rung spacing is a named candidate optimization, decision at implementation. Design sources: dudley-round2.md §(a), tyre-round2.md §(b), D-255(c)(d)(f). Legacy district_window survives byte-unchanged until its consumer is replaced.', 'review', 'high', NULL, 'server', 'D-255', '2026-07-24 07:11:27.800', '2026-07-25 00:26:42.634', NULL, '403b79fde0dcd81c48718da87bde22e9', 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 ('06FRXGSXBZKXTHR7MR5K9H2HRG', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Batch vs window derive paths sample different world positions for the same DistrictPos', 'Pre-existing architectural quirk found by Dudley during T-1168 integration testing (2026-07-23, not introduced by that batch): derive_district_profile''s cell-aggregate-centre sampling and the window path''s district-origin sampling resolve to genuinely different world positions for ''the same DistrictPos'' — full DistrictProfile output legitimately differs between paths (morphology/elev/moisture) at the same nominal district. His integration test was redesigned to compare the riparian signal at a shared exact world position instead (the right test). Investigate whether any existing batch/window consumer pair assumes positional agreement (city placement vs window rendering?), decide the canonical sampling convention, and either unify or document the divergence as intentional with the reasoning.
D-255 (2026-07-24): the tagged-envelope step-canvas serving (T-1181) depends on batch and window derive agreeing cell-for-cell — this bug now also blocks the step-canvas serving path, and priority is raised accordingly (Tyre disposition, body-map-viewer workshop). No supersession; intent unchanged.
RULING (2026-07-24): D-256 claimed and written on branch t1174-derive-sampling (rides the PR; syncs to main on merge). Canonical convention = derive(seed, absolute world metres) via the derive_at_metres family; batch 64x32 pseudo-grid re-scoped as a survey raster with a real SurveyCellPos newtype; derive_district_profile becomes a thin wrapper over an extracted riparian-parameterized core at survey-cell-centre positions (basin_direction preserved by post-call override; near_perennial_water_at plumbed, never the empty-slice default); skeleton morphology judgments move to exact-position derive; step canvases fenced to the window family. LayerRegionOutput rebuild deferred to T-1181 (tripwire verified: sole reader is the region_grid overlay); voxel carrier deferred to Phase 5. Consults: Tyre APPROVE-WITH-CHANGES, Dudley FEASIBLE-WITH-CHANGES (both incorporated). Settlement/road positions do NOT move (L1 attractor-sourced; T-1075 bake orthogonal); believability goldens regen, gates unchanged; window goldens must stay byte-green.', 'done', 'high', NULL, 'server', NULL, '2026-07-23 11:36:27.743', '2026-07-24 23:35:06.385', NULL, '8eb2f277a07bd3d5d780a55e4c329792', 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 ('06FS5XQW73B1P8WKR2BCZBFXZW', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Step-canvas serving: StepCanvasRequest/Response tagged envelope (D-255 wire migration)', 'The D-225-discharging migration per D-255(c): one new Inbound variant (StepCanvasRequest, required marker field step_canvas, extending the proven star_map/city_names/browse ShapeProbe pattern in server/src/bridge/mod.rs) + a dedicated StepCanvasResponse (NOT a field on AtlasLayerResponse — the D-226 §2 ceiling is re-scoped to the legacy carrier). Serves all six rungs of the D-255(a) ladder — rung-0 Global variable-extent is a field value, not a different shape; fixed rungs viewport-sized per D-255(b). Encoding: PNG-per-field dense (8 fields: 6 static + glaciation/flooded sim-state L8 planes) + MessagePack-native sparse (courses, cliffs) per the T-1179 measured table. SERVER CACHE TIERS ARE THIS TICKET''S SCOPE TOO (SI gap 3): the D-203-shaped resident global tier (~8.85MB population-wide, measured) + TTL(detail, time, distance) for finer rungs + SIM_STATE_TTL[field] = 1x the field''s own fastest driving clock-bucket per body class (flooded: tidal bucket moon-bearing / seasonal moonless; glaciation: seasonal — D-227 amendment (1), Dudley+Araminta joint formula). ACCEPTANCE GATE (SI gap 2, mandatory per D-227 amendment (3)): byte-identical output test — cache-hit derive path == cache-miss derive path for every rung; the seed-chaining cache-accelerated-pure-function correctness gate, same shape as T-1170''s window-independence invariant. Implementation note carried from measurement S2: courses cost +38-87% at chunk/block spacing (station count scales inversely with rung spacing — 1,732 vs 867 points/course measured); a station-spacing cap independent of rung spacing is a named candidate optimization, decision at implementation. Design sources: dudley-round2.md §(a), tyre-round2.md §(b), D-255(c)(d)(f). Legacy district_window survives byte-unchanged until its consumer is replaced.', 'in_progress', 'high', NULL, 'server', 'D-255', '2026-07-24 07:11:27.800', '2026-07-24 23:49:56.370', NULL, 'ec6cbae56f99407aa4763726af4577c0', 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 ('06FS5XXY5GNCQEXYAYQEQD0CHM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Hydrology into the pipeline: productionize the T-1177 solver + lake morphology sourcing (D-255)', 'Two halves, one ticket (the D-227 lakes amendment''s implementation; converged in araminta-round2.md §(e) + dudley-interview2-response.md CORRECTED section). HALF 1 — productionize: wire server/src/atlas/hydrology_equilibrium.rs (currently an inert workshop prototype, zero non-test callers) into the derive pipeline: solve once per body at rung-0 Global populate (measured ~24ms/body at 512x256; population survey: all 267 bodies ~0.86s), result held with the global tier (D-203-shaped, keep-always). HALF 2 — lake sourcing: MorphologyZone::Lake (discriminant 1 — ALREADY in the frozen D-239 17-zone vocabulary, no widening) becomes sourced from the settled solver: at derive time a gridunit is Lake when bilinear-sampled HydrologyResult.filled_scaled exceeds bilinear-sampled original elevation (the continuous filled-surface comparison — lake edges refine with rung like coastlines, per D-166; NEVER project basin-cell membership as a discrete lookup, which gives blocky non-refining edges), with today''s ocean_fraction_q>=60 heuristic as fallback where no solve exists. This is seed-chaining mechanism B (coarse continuous primitive sampled fresh per rung — D-255(f)). STATIC classification — distinct from the sim-state flooded plane; a lake''s existence must never flicker on a TTL. NO endorheic bit anywhere (ruled: the cue is outflow-course presence, delivered by the separate outlet-wiring ticket; until that lands the map shows lakes without the drains-vs-closed distinction — honest sequencing per D-227 amendment (4)). Byte cost: zero new wire bytes (existing field). Uniform moisture_q=55 scope limit from the survey noted: affects endorheic/overflow split only, never lake extent.', 'in_progress', 'medium', NULL, 'server', 'D-227', '2026-07-24 07:12:17.452', '2026-07-24 23:49:56.374', NULL, '32e1feaeeace1bb37417c88f222dae83', 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 ('06FS5XXY5GNCQEXYAYQEQD0CHM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Hydrology into the pipeline: productionize the T-1177 solver + lake morphology sourcing (D-255)', 'Two halves, one ticket (the D-227 lakes amendment''s implementation; converged in araminta-round2.md §(e) + dudley-interview2-response.md CORRECTED section). HALF 1 — productionize: wire server/src/atlas/hydrology_equilibrium.rs (currently an inert workshop prototype, zero non-test callers) into the derive pipeline: solve once per body at rung-0 Global populate (measured ~24ms/body at 512x256; population survey: all 267 bodies ~0.86s), result held with the global tier (D-203-shaped, keep-always). HALF 2 — lake sourcing: MorphologyZone::Lake (discriminant 1 — ALREADY in the frozen D-239 17-zone vocabulary, no widening) becomes sourced from the settled solver: at derive time a gridunit is Lake when bilinear-sampled HydrologyResult.filled_scaled exceeds bilinear-sampled original elevation (the continuous filled-surface comparison — lake edges refine with rung like coastlines, per D-166; NEVER project basin-cell membership as a discrete lookup, which gives blocky non-refining edges), with today''s ocean_fraction_q>=60 heuristic as fallback where no solve exists. This is seed-chaining mechanism B (coarse continuous primitive sampled fresh per rung — D-255(f)). STATIC classification — distinct from the sim-state flooded plane; a lake''s existence must never flicker on a TTL. NO endorheic bit anywhere (ruled: the cue is outflow-course presence, delivered by the separate outlet-wiring ticket; until that lands the map shows lakes without the drains-vs-closed distinction — honest sequencing per D-227 amendment (4)). Byte cost: zero new wire bytes (existing field). Uniform moisture_q=55 scope limit from the survey noted: affects endorheic/overflow split only, never lake extent.
IMPLEMENTATION NOTE (2026-07-25, lead-accepted deviation): the ticket''s ''result held with the global tier (D-203-shaped, keep-always)'' clause could not be satisfied literally — no keep-always rung-0 resident tier exists in the codebase yet (BodyWorldStateCache is the D-203 50-body LRU and drops TerrainAnalysis per T-1048). Implemented instead: hydrology solves once per TerrainAnalysis build inside run_layer1/run_layer1_with_moisture (~24ms, mirrors drainage::analyze), carried as TerrainAnalysis.hydrology: Option<HydrologySample> (~1MB), resident wherever TA is resident (TerrainAnalysisCache in gen_queue). When T-1181 builds the actual resident global tier, hydrology rides TA into it with no further wiring. Also: TerrainAnalysisCache::get_or_derive gained a body_params param to thread the real moisture ceiling — a known merge seam with the concurrent T-1181 branch (second-to-land rebases). Basin-id/outcome sampling deliberately NOT wired (T-1185 will need its own nearest-sample basin-id lookup if it needs identity beyond outlet-path presence); HydrologyResult.basins spill points computed but currently discarded after filled/elevation extraction — cheap to extend at the same call site.', 'in_progress', 'medium', NULL, 'server', 'D-227', '2026-07-24 07:12:17.452', '2026-07-25 00:16:29.760', NULL, '176b6cd1a00b243926603db5e64acd5b', 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 ('06FS5XXY5GNCQEXYAYQEQD0CHM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Hydrology into the pipeline: productionize the T-1177 solver + lake morphology sourcing (D-255)', 'Two halves, one ticket (the D-227 lakes amendment''s implementation; converged in araminta-round2.md §(e) + dudley-interview2-response.md CORRECTED section). HALF 1 — productionize: wire server/src/atlas/hydrology_equilibrium.rs (currently an inert workshop prototype, zero non-test callers) into the derive pipeline: solve once per body at rung-0 Global populate (measured ~24ms/body at 512x256; population survey: all 267 bodies ~0.86s), result held with the global tier (D-203-shaped, keep-always). HALF 2 — lake sourcing: MorphologyZone::Lake (discriminant 1 — ALREADY in the frozen D-239 17-zone vocabulary, no widening) becomes sourced from the settled solver: at derive time a gridunit is Lake when bilinear-sampled HydrologyResult.filled_scaled exceeds bilinear-sampled original elevation (the continuous filled-surface comparison — lake edges refine with rung like coastlines, per D-166; NEVER project basin-cell membership as a discrete lookup, which gives blocky non-refining edges), with today''s ocean_fraction_q>=60 heuristic as fallback where no solve exists. This is seed-chaining mechanism B (coarse continuous primitive sampled fresh per rung — D-255(f)). STATIC classification — distinct from the sim-state flooded plane; a lake''s existence must never flicker on a TTL. NO endorheic bit anywhere (ruled: the cue is outflow-course presence, delivered by the separate outlet-wiring ticket; until that lands the map shows lakes without the drains-vs-closed distinction — honest sequencing per D-227 amendment (4)). Byte cost: zero new wire bytes (existing field). Uniform moisture_q=55 scope limit from the survey noted: affects endorheic/overflow split only, never lake extent.
IMPLEMENTATION NOTE (2026-07-25, lead-accepted deviation): the ticket''s ''result held with the global tier (D-203-shaped, keep-always)'' clause could not be satisfied literally — no keep-always rung-0 resident tier exists in the codebase yet (BodyWorldStateCache is the D-203 50-body LRU and drops TerrainAnalysis per T-1048). Implemented instead: hydrology solves once per TerrainAnalysis build inside run_layer1/run_layer1_with_moisture (~24ms, mirrors drainage::analyze), carried as TerrainAnalysis.hydrology: Option<HydrologySample> (~1MB), resident wherever TA is resident (TerrainAnalysisCache in gen_queue). When T-1181 builds the actual resident global tier, hydrology rides TA into it with no further wiring. Also: TerrainAnalysisCache::get_or_derive gained a body_params param to thread the real moisture ceiling — a known merge seam with the concurrent T-1181 branch (second-to-land rebases). Basin-id/outcome sampling deliberately NOT wired (T-1185 will need its own nearest-sample basin-id lookup if it needs identity beyond outlet-path presence); HydrologyResult.basins spill points computed but currently discarded after filled/elevation extraction — cheap to extend at the same call site.', 'review', 'medium', NULL, 'server', 'D-227', '2026-07-24 07:12:17.452', '2026-07-25 00:28:44.132', NULL, 'ce686f7567ba8cd58fb6d4af0641f353', 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 ('06FSDE3STZBP0Y7GQ3ZEZPKFP8', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Arc-wrap TerrainAnalysis in TerrainAnalysisCache — clone-on-hit copies ~2.6MB per window request', 'Follow-up from PR #200 review (T-1184, 2026-07-25). TerrainAnalysisCache::get_or_derive''s hit branch clones the full TerrainAnalysis (~2.62MB/entry at the 512x256 working grid post-hydrology: ~1.57MB dense fields + ~1.05MB HydrologySample) on EVERY cache hit — every DeriveWindow/DeriveStepCanvas work item pays a multi-MB memcpy for data that is immutable after construction. The clean fix is Arc<TerrainAnalysis> in the cache (clone becomes a refcount bump); flagged by the implementer during the review round and ruled out of scope there. Sizing truth now documented at gen_queue.rs get_or_derive + features.rs HydrologySample (corrected figures: capacity-8 worst case ~21MB). Low priority: correctness unaffected, cost is per-request memcpy only.', 'backlog', 'low', NULL, 'server', NULL, '2026-07-25 00:41:40.055', '2026-07-25 00:41:40.055', NULL, '28e18f415ed43a36e00d72a4d2502ead', 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 ('06FSCXX7E3BXT7WG0BYDB6PJA0', 'bug', '06FBPPMZNNEV052DBYYY3A897C', 'Region-baseline latitude is pole-anchored while the derive core keys equator-anchored signed regions — northern hemisphere clamps to +90', 'Found during the PR #199 (T-1174/D-256) review fix round, PRE-EXISTING on the window path since the region climate stack (T-1078/T-1113) was wired into derive_at_metres. region_profile::region_centre_latitude_deg (region_profile.rs ~270-280) maps region row 0 to the north pole (+90) with lat_frac clamped to [0,1] — a pole-anchored, non-negative row convention. But the derive core (derive_at_metres_with_riparian, district_profile.rs ~1788) keys regions by floor-dividing EQUATOR-anchored world metres (negative wy = north), producing signed region rows (Earth-class: -49..+49). Consequence: every northern-hemisphere region has negative row -> region_centre_y_m negative -> lat_frac clamps to 0 -> baseline latitude +90 (polar) regardless of true latitude; southern-hemisphere rows 0..~48 read as compressed NORTHERN latitudes ~89..~0.7 (south pole reads as equator). Affects the region temperature baseline (and glaciation/moisture components derived from it) for the window path at every rung, and post-D-256 the batch/survey path inherits the same keys (still an improvement over the pre-D-256 body-wide region-(0,0) baseline, which read ~+89 everywhere). NOT touched in PR #199 to keep the review round scoped; goldens currently pin the wrong-latitude values. FIX SHAPE: make region_centre_latitude_deg mirror the derive core''s inverse mapping (equator-anchored signed rows: lat = -((ry+0.5)*REGION_M/meridian_m clamped to [-0.5,0.5])*180), audit the OTHER convention''s callers (the D-255 rung-0 canvas row space and the collapsed LayerRegionOutput builder use non-negative pole-anchored rows - decide ONE convention per D-256''s one-inverse-mapping principle, likely at the T-1181 rung-0 rebuild), regen affected goldens, and re-run the believability direction check. Related: D-256, D-243, T-1181, T-1078.
In-the-wild datapoint (2026-07-25, T-1184 eyeball session): GJ338Bd (Arbour, R=6711km) survey cell (42,29) — true position latitude -76.2S (wy=8,929,367m, region row 43) — derives with the pole-anchored baseline latitude +13.9N, producing an 18C temperate, glaciation-free lake district near the antarctic circle. Concrete demonstration of the southern-hemisphere-reads-as-northern-tropics half of the bug (the northern half clamps to +90). Conversion math used: scratchpad survey_to_district.py, the D-256 bridge formula.', 'backlog', 'high', NULL, 'server', NULL, '2026-07-24 23:30:51.889', '2026-07-25 01:01:37.348', NULL, 'e21a79e29bd911e28ff64a26cd517c0e', 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 ('06FS5XXY5GNCQEXYAYQEQD0CHM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Hydrology into the pipeline: productionize the T-1177 solver + lake morphology sourcing (D-255)', 'Two halves, one ticket (the D-227 lakes amendment''s implementation; converged in araminta-round2.md §(e) + dudley-interview2-response.md CORRECTED section). HALF 1 — productionize: wire server/src/atlas/hydrology_equilibrium.rs (currently an inert workshop prototype, zero non-test callers) into the derive pipeline: solve once per body at rung-0 Global populate (measured ~24ms/body at 512x256; population survey: all 267 bodies ~0.86s), result held with the global tier (D-203-shaped, keep-always). HALF 2 — lake sourcing: MorphologyZone::Lake (discriminant 1 — ALREADY in the frozen D-239 17-zone vocabulary, no widening) becomes sourced from the settled solver: at derive time a gridunit is Lake when bilinear-sampled HydrologyResult.filled_scaled exceeds bilinear-sampled original elevation (the continuous filled-surface comparison — lake edges refine with rung like coastlines, per D-166; NEVER project basin-cell membership as a discrete lookup, which gives blocky non-refining edges), with today''s ocean_fraction_q>=60 heuristic as fallback where no solve exists. This is seed-chaining mechanism B (coarse continuous primitive sampled fresh per rung — D-255(f)). STATIC classification — distinct from the sim-state flooded plane; a lake''s existence must never flicker on a TTL. NO endorheic bit anywhere (ruled: the cue is outflow-course presence, delivered by the separate outlet-wiring ticket; until that lands the map shows lakes without the drains-vs-closed distinction — honest sequencing per D-227 amendment (4)). Byte cost: zero new wire bytes (existing field). Uniform moisture_q=55 scope limit from the survey noted: affects endorheic/overflow split only, never lake extent.
IMPLEMENTATION NOTE (2026-07-25, lead-accepted deviation): the ticket''s ''result held with the global tier (D-203-shaped, keep-always)'' clause could not be satisfied literally — no keep-always rung-0 resident tier exists in the codebase yet (BodyWorldStateCache is the D-203 50-body LRU and drops TerrainAnalysis per T-1048). Implemented instead: hydrology solves once per TerrainAnalysis build inside run_layer1/run_layer1_with_moisture (~24ms, mirrors drainage::analyze), carried as TerrainAnalysis.hydrology: Option<HydrologySample> (~1MB), resident wherever TA is resident (TerrainAnalysisCache in gen_queue). When T-1181 builds the actual resident global tier, hydrology rides TA into it with no further wiring. Also: TerrainAnalysisCache::get_or_derive gained a body_params param to thread the real moisture ceiling — a known merge seam with the concurrent T-1181 branch (second-to-land rebases). Basin-id/outcome sampling deliberately NOT wired (T-1185 will need its own nearest-sample basin-id lookup if it needs identity beyond outlet-path presence); HydrologyResult.basins spill points computed but currently discarded after filled/elevation extraction — cheap to extend at the same call site.', 'done', 'medium', NULL, 'server', 'D-227', '2026-07-24 07:12:17.452', '2026-07-25 01:11:56.813', NULL, 'b3af895faf92eaa19ef7ab581ee45c2f', 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 ('06FS5XQW73B1P8WKR2BCZBFXZW', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Step-canvas serving: StepCanvasRequest/Response tagged envelope (D-255 wire migration)', 'The D-225-discharging migration per D-255(c): one new Inbound variant (StepCanvasRequest, required marker field step_canvas, extending the proven star_map/city_names/browse ShapeProbe pattern in server/src/bridge/mod.rs) + a dedicated StepCanvasResponse (NOT a field on AtlasLayerResponse — the D-226 §2 ceiling is re-scoped to the legacy carrier). Serves all six rungs of the D-255(a) ladder — rung-0 Global variable-extent is a field value, not a different shape; fixed rungs viewport-sized per D-255(b). Encoding: PNG-per-field dense (8 fields: 6 static + glaciation/flooded sim-state L8 planes) + MessagePack-native sparse (courses, cliffs) per the T-1179 measured table. SERVER CACHE TIERS ARE THIS TICKET''S SCOPE TOO (SI gap 3): the D-203-shaped resident global tier (~8.85MB population-wide, measured) + TTL(detail, time, distance) for finer rungs + SIM_STATE_TTL[field] = 1x the field''s own fastest driving clock-bucket per body class (flooded: tidal bucket moon-bearing / seasonal moonless; glaciation: seasonal — D-227 amendment (1), Dudley+Araminta joint formula). ACCEPTANCE GATE (SI gap 2, mandatory per D-227 amendment (3)): byte-identical output test — cache-hit derive path == cache-miss derive path for every rung; the seed-chaining cache-accelerated-pure-function correctness gate, same shape as T-1170''s window-independence invariant. Implementation note carried from measurement S2: courses cost +38-87% at chunk/block spacing (station count scales inversely with rung spacing — 1,732 vs 867 points/course measured); a station-spacing cap independent of rung spacing is a named candidate optimization, decision at implementation. Design sources: dudley-round2.md §(a), tyre-round2.md §(b), D-255(c)(d)(f). Legacy district_window survives byte-unchanged until its consumer is replaced.
IMPLEMENTATION REPORT — wire shapes for T-1182 (2026-07-25, PR #201): StepCanvasRung = Global (rung 0, variable, one gridunit per region, whole-body, always-kept) | Region 204.8km | District 2048m | Quarter 512m | Block 128m | Chunk 64m (deepest). Global/Region derive via derive_orbital_at_metres; District..Chunk via derive_at_metres. StepCanvasRequest { step_canvas: bool (marker), body_id, rung, center: (i64,i64), extent: (u32,u32), min_wl_m: u32 } — sixth Inbound variant. StepCanvasResponse { body_id, rung, center, min_wl_m, status: StepCanvasStatus, canvas: Option<EncodedStepCanvas> } — dedicated outbound, NOT on AtlasLayerResponse. EncodedStepCanvas: width, height + 6 static fields (morphology, elev_q, moisture_q, vegetation as PNG-per-field; temp_dc raw Vec<i16> msgpack, settlement_id raw Vec<u32> msgpack — too wide for 8-bit PNG) + 2 sim-state (glaciation PNG, flooded_q PNG always-0 stub) + 2 sparse (courses: Vec<RiverCourse>, cliffs: Vec<CliffSegment> — always empty today, zero carved cells in the population). settlement_id: 0=none else city_id, a 1500m proximity radius around the settlement anchor point (honest approximation — no footprint polygons exist; revisit when quarter-skeleton geometry lands); Global rung reports zero coverage. Rung-0 row space: SIGNED equator-anchored rows (wy=(row-rows/2)*REGION_M, negative=north) per the D-256 canonical core — T-1186 interaction documented at build_step_canvas. Caches: GlobalTierCache (BTreeMap by body_id, no eviction API — structurally keep-always, lazy D-206 populate) + StepCanvasCache keyed (body_id, rung, center, extent, min_wl_m), dual-axis eviction on get() (storage TTL per rung, Region ~1h..Chunk ~2min; SIM_STATE_TTL seasonal ~1h / tidal ~15min moon-bearing — note BodyDrivingClockClass::classify always returns Moonless today, no moon signal on BodyParams). Station-spacing cap adopted: course_station_spacing_m(rung) = rung.spacing_m().max(DISTRICT_M) (COURSE_STATION_SPACING_FLOOR_M). Known stubs: flooded_q always 0, glaciation static (D-253 not built); both wire-shape-ready.', 'in_progress', 'high', NULL, 'server', 'D-255', '2026-07-24 07:11:27.800', '2026-07-25 01:21:11.774', NULL, 'aadff35baf50abdbc76b9d6ff95bc6b8', 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 ('06FS5XQW73B1P8WKR2BCZBFXZW', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Step-canvas serving: StepCanvasRequest/Response tagged envelope (D-255 wire migration)', 'The D-225-discharging migration per D-255(c): one new Inbound variant (StepCanvasRequest, required marker field step_canvas, extending the proven star_map/city_names/browse ShapeProbe pattern in server/src/bridge/mod.rs) + a dedicated StepCanvasResponse (NOT a field on AtlasLayerResponse — the D-226 §2 ceiling is re-scoped to the legacy carrier). Serves all six rungs of the D-255(a) ladder — rung-0 Global variable-extent is a field value, not a different shape; fixed rungs viewport-sized per D-255(b). Encoding: PNG-per-field dense (8 fields: 6 static + glaciation/flooded sim-state L8 planes) + MessagePack-native sparse (courses, cliffs) per the T-1179 measured table. SERVER CACHE TIERS ARE THIS TICKET''S SCOPE TOO (SI gap 3): the D-203-shaped resident global tier (~8.85MB population-wide, measured) + TTL(detail, time, distance) for finer rungs + SIM_STATE_TTL[field] = 1x the field''s own fastest driving clock-bucket per body class (flooded: tidal bucket moon-bearing / seasonal moonless; glaciation: seasonal — D-227 amendment (1), Dudley+Araminta joint formula). ACCEPTANCE GATE (SI gap 2, mandatory per D-227 amendment (3)): byte-identical output test — cache-hit derive path == cache-miss derive path for every rung; the seed-chaining cache-accelerated-pure-function correctness gate, same shape as T-1170''s window-independence invariant. Implementation note carried from measurement S2: courses cost +38-87% at chunk/block spacing (station count scales inversely with rung spacing — 1,732 vs 867 points/course measured); a station-spacing cap independent of rung spacing is a named candidate optimization, decision at implementation. Design sources: dudley-round2.md §(a), tyre-round2.md §(b), D-255(c)(d)(f). Legacy district_window survives byte-unchanged until its consumer is replaced.
IMPLEMENTATION REPORT — wire shapes for T-1182 (2026-07-25, PR #201): StepCanvasRung = Global (rung 0, variable, one gridunit per region, whole-body, always-kept) | Region 204.8km | District 2048m | Quarter 512m | Block 128m | Chunk 64m (deepest). Global/Region derive via derive_orbital_at_metres; District..Chunk via derive_at_metres. StepCanvasRequest { step_canvas: bool (marker), body_id, rung, center: (i64,i64), extent: (u32,u32), min_wl_m: u32 } — sixth Inbound variant. StepCanvasResponse { body_id, rung, center, min_wl_m, status: StepCanvasStatus, canvas: Option<EncodedStepCanvas> } — dedicated outbound, NOT on AtlasLayerResponse. EncodedStepCanvas: width, height + 6 static fields (morphology, elev_q, moisture_q, vegetation as PNG-per-field; temp_dc raw Vec<i16> msgpack, settlement_id raw Vec<u32> msgpack — too wide for 8-bit PNG) + 2 sim-state (glaciation PNG, flooded_q PNG always-0 stub) + 2 sparse (courses: Vec<RiverCourse>, cliffs: Vec<CliffSegment> — always empty today, zero carved cells in the population). settlement_id: 0=none else city_id, a 1500m proximity radius around the settlement anchor point (honest approximation — no footprint polygons exist; revisit when quarter-skeleton geometry lands); Global rung reports zero coverage. Rung-0 row space: SIGNED equator-anchored rows (wy=(row-rows/2)*REGION_M, negative=north) per the D-256 canonical core — T-1186 interaction documented at build_step_canvas. Caches: GlobalTierCache (BTreeMap by body_id, no eviction API — structurally keep-always, lazy D-206 populate) + StepCanvasCache keyed (body_id, rung, center, extent, min_wl_m), dual-axis eviction on get() (storage TTL per rung, Region ~1h..Chunk ~2min; SIM_STATE_TTL seasonal ~1h / tidal ~15min moon-bearing — note BodyDrivingClockClass::classify always returns Moonless today, no moon signal on BodyParams). Station-spacing cap adopted: course_station_spacing_m(rung) = rung.spacing_m().max(DISTRICT_M) (COURSE_STATION_SPACING_FLOOR_M). Known stubs: flooded_q always 0, glaciation static (D-253 not built); both wire-shape-ready.', 'review', 'high', NULL, 'server', 'D-255', '2026-07-24 07:11:27.800', '2026-07-25 01:21:19.766', NULL, '3e5eca9aa98c0cfa1ef832cf09cc5f70', 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 ('06FS5XQW73B1P8WKR2BCZBFXZW', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Step-canvas serving: StepCanvasRequest/Response tagged envelope (D-255 wire migration)', 'The D-225-discharging migration per D-255(c): one new Inbound variant (StepCanvasRequest, required marker field step_canvas, extending the proven star_map/city_names/browse ShapeProbe pattern in server/src/bridge/mod.rs) + a dedicated StepCanvasResponse (NOT a field on AtlasLayerResponse — the D-226 §2 ceiling is re-scoped to the legacy carrier). Serves all six rungs of the D-255(a) ladder — rung-0 Global variable-extent is a field value, not a different shape; fixed rungs viewport-sized per D-255(b). Encoding: PNG-per-field dense (8 fields: 6 static + glaciation/flooded sim-state L8 planes) + MessagePack-native sparse (courses, cliffs) per the T-1179 measured table. SERVER CACHE TIERS ARE THIS TICKET''S SCOPE TOO (SI gap 3): the D-203-shaped resident global tier (~8.85MB population-wide, measured) + TTL(detail, time, distance) for finer rungs + SIM_STATE_TTL[field] = 1x the field''s own fastest driving clock-bucket per body class (flooded: tidal bucket moon-bearing / seasonal moonless; glaciation: seasonal — D-227 amendment (1), Dudley+Araminta joint formula). ACCEPTANCE GATE (SI gap 2, mandatory per D-227 amendment (3)): byte-identical output test — cache-hit derive path == cache-miss derive path for every rung; the seed-chaining cache-accelerated-pure-function correctness gate, same shape as T-1170''s window-independence invariant. Implementation note carried from measurement S2: courses cost +38-87% at chunk/block spacing (station count scales inversely with rung spacing — 1,732 vs 867 points/course measured); a station-spacing cap independent of rung spacing is a named candidate optimization, decision at implementation. Design sources: dudley-round2.md §(a), tyre-round2.md §(b), D-255(c)(d)(f). Legacy district_window survives byte-unchanged until its consumer is replaced.
IMPLEMENTATION REPORT — wire shapes for T-1182 (2026-07-25, PR #201): StepCanvasRung = Global (rung 0, variable, one gridunit per region, whole-body, always-kept) | Region 204.8km | District 2048m | Quarter 512m | Block 128m | Chunk 64m (deepest). Global/Region derive via derive_orbital_at_metres; District..Chunk via derive_at_metres. StepCanvasRequest { step_canvas: bool (marker), body_id, rung, center: (i64,i64), extent: (u32,u32), min_wl_m: u32 } — sixth Inbound variant. StepCanvasResponse { body_id, rung, center, min_wl_m, status: StepCanvasStatus, canvas: Option<EncodedStepCanvas> } — dedicated outbound, NOT on AtlasLayerResponse. EncodedStepCanvas: width, height + 6 static fields (morphology, elev_q, moisture_q, vegetation as PNG-per-field; temp_dc raw Vec<i16> msgpack, settlement_id raw Vec<u32> msgpack — too wide for 8-bit PNG) + 2 sim-state (glaciation PNG, flooded_q PNG always-0 stub) + 2 sparse (courses: Vec<RiverCourse>, cliffs: Vec<CliffSegment> — always empty today, zero carved cells in the population). settlement_id: 0=none else city_id, a 1500m proximity radius around the settlement anchor point (honest approximation — no footprint polygons exist; revisit when quarter-skeleton geometry lands); Global rung reports zero coverage. Rung-0 row space: SIGNED equator-anchored rows (wy=(row-rows/2)*REGION_M, negative=north) per the D-256 canonical core — T-1186 interaction documented at build_step_canvas. Caches: GlobalTierCache (BTreeMap by body_id, no eviction API — structurally keep-always, lazy D-206 populate) + StepCanvasCache keyed (body_id, rung, center, extent, min_wl_m), dual-axis eviction on get() (storage TTL per rung, Region ~1h..Chunk ~2min; SIM_STATE_TTL seasonal ~1h / tidal ~15min moon-bearing — note BodyDrivingClockClass::classify always returns Moonless today, no moon signal on BodyParams). Station-spacing cap adopted: course_station_spacing_m(rung) = rung.spacing_m().max(DISTRICT_M) (COURSE_STATION_SPACING_FLOOR_M). Known stubs: flooded_q always 0, glaciation static (D-253 not built); both wire-shape-ready.
WIRE SHAPE ADDENDUM (PR #201 review round): StepCanvasResponse gained an extent: (u32,u32) ECHO field — the server-clamped canvas extent, so the client can detect a clamp (mirrors DistrictWindowLayer.n precedent). Wire extent is clamped server-side per D-255(b): STEP_CANVAS_MAX_EXTENT_AXIS=3840 per axis, STEP_CANVAS_MAX_EXTENT_CELLS=8294400 total (aspect-preserving scale-down); Global rung ignores wire extent entirely. T-1182 client must read the echoed extent, never assume its requested one.', 'review', 'high', NULL, 'server', 'D-255', '2026-07-24 07:11:27.800', '2026-07-25 01:33:48.224', NULL, 'd92ef3d03c1e05c6f17604ee0423f8dd', 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 ('06FS5XQW73B1P8WKR2BCZBFXZW', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Step-canvas serving: StepCanvasRequest/Response tagged envelope (D-255 wire migration)', 'The D-225-discharging migration per D-255(c): one new Inbound variant (StepCanvasRequest, required marker field step_canvas, extending the proven star_map/city_names/browse ShapeProbe pattern in server/src/bridge/mod.rs) + a dedicated StepCanvasResponse (NOT a field on AtlasLayerResponse — the D-226 §2 ceiling is re-scoped to the legacy carrier). Serves all six rungs of the D-255(a) ladder — rung-0 Global variable-extent is a field value, not a different shape; fixed rungs viewport-sized per D-255(b). Encoding: PNG-per-field dense (8 fields: 6 static + glaciation/flooded sim-state L8 planes) + MessagePack-native sparse (courses, cliffs) per the T-1179 measured table. SERVER CACHE TIERS ARE THIS TICKET''S SCOPE TOO (SI gap 3): the D-203-shaped resident global tier (~8.85MB population-wide, measured) + TTL(detail, time, distance) for finer rungs + SIM_STATE_TTL[field] = 1x the field''s own fastest driving clock-bucket per body class (flooded: tidal bucket moon-bearing / seasonal moonless; glaciation: seasonal — D-227 amendment (1), Dudley+Araminta joint formula). ACCEPTANCE GATE (SI gap 2, mandatory per D-227 amendment (3)): byte-identical output test — cache-hit derive path == cache-miss derive path for every rung; the seed-chaining cache-accelerated-pure-function correctness gate, same shape as T-1170''s window-independence invariant. Implementation note carried from measurement S2: courses cost +38-87% at chunk/block spacing (station count scales inversely with rung spacing — 1,732 vs 867 points/course measured); a station-spacing cap independent of rung spacing is a named candidate optimization, decision at implementation. Design sources: dudley-round2.md §(a), tyre-round2.md §(b), D-255(c)(d)(f). Legacy district_window survives byte-unchanged until its consumer is replaced.
IMPLEMENTATION REPORT — wire shapes for T-1182 (2026-07-25, PR #201): StepCanvasRung = Global (rung 0, variable, one gridunit per region, whole-body, always-kept) | Region 204.8km | District 2048m | Quarter 512m | Block 128m | Chunk 64m (deepest). Global/Region derive via derive_orbital_at_metres; District..Chunk via derive_at_metres. StepCanvasRequest { step_canvas: bool (marker), body_id, rung, center: (i64,i64), extent: (u32,u32), min_wl_m: u32 } — sixth Inbound variant. StepCanvasResponse { body_id, rung, center, min_wl_m, status: StepCanvasStatus, canvas: Option<EncodedStepCanvas> } — dedicated outbound, NOT on AtlasLayerResponse. EncodedStepCanvas: width, height + 6 static fields (morphology, elev_q, moisture_q, vegetation as PNG-per-field; temp_dc raw Vec<i16> msgpack, settlement_id raw Vec<u32> msgpack — too wide for 8-bit PNG) + 2 sim-state (glaciation PNG, flooded_q PNG always-0 stub) + 2 sparse (courses: Vec<RiverCourse>, cliffs: Vec<CliffSegment> — always empty today, zero carved cells in the population). settlement_id: 0=none else city_id, a 1500m proximity radius around the settlement anchor point (honest approximation — no footprint polygons exist; revisit when quarter-skeleton geometry lands); Global rung reports zero coverage. Rung-0 row space: SIGNED equator-anchored rows (wy=(row-rows/2)*REGION_M, negative=north) per the D-256 canonical core — T-1186 interaction documented at build_step_canvas. Caches: GlobalTierCache (BTreeMap by body_id, no eviction API — structurally keep-always, lazy D-206 populate) + StepCanvasCache keyed (body_id, rung, center, extent, min_wl_m), dual-axis eviction on get() (storage TTL per rung, Region ~1h..Chunk ~2min; SIM_STATE_TTL seasonal ~1h / tidal ~15min moon-bearing — note BodyDrivingClockClass::classify always returns Moonless today, no moon signal on BodyParams). Station-spacing cap adopted: course_station_spacing_m(rung) = rung.spacing_m().max(DISTRICT_M) (COURSE_STATION_SPACING_FLOOR_M). Known stubs: flooded_q always 0, glaciation static (D-253 not built); both wire-shape-ready.
WIRE SHAPE ADDENDUM (PR #201 review round): StepCanvasResponse gained an extent: (u32,u32) ECHO field — the server-clamped canvas extent, so the client can detect a clamp (mirrors DistrictWindowLayer.n precedent). Wire extent is clamped server-side per D-255(b): STEP_CANVAS_MAX_EXTENT_AXIS=3840 per axis, STEP_CANVAS_MAX_EXTENT_CELLS=8294400 total (aspect-preserving scale-down); Global rung ignores wire extent entirely. T-1182 client must read the echoed extent, never assume its requested one.', 'done', 'high', NULL, 'server', 'D-255', '2026-07-24 07:11:27.800', '2026-07-25 01:37:38.818', NULL, 'ae4790ffc622a6167477ad66f3d683c0', 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 ('06FS5XT949C8D2JZRWDX2CRZN8', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Client map component: RTT terrain + screen-space annotations + stepped transport (D-255)', 'The two-layer client rebuild per D-255(a)(b)(e): a render-to-texture terrain layer (per-step data canvas colorized CPU-side — c1 ruling: Image.set_pixel, measured 77.5 ns/cell flat, NOT a hand-rolled buffer write which measured 2x slower; L8 planes where single-channel per measurement ⑤; texture.update reuse on step-cross) + an unscaled screen-space annotation sibling (names, glyphs, course polylines, markers — literal px sizes, positions world→screen). Stepped transport: discrete rung index 0-5, scroll steps, cursor-anchored, edge-scroll pan, full-zoom-out reset to the rung-0 Global frame; hold-fetch-swap on step-cross (morph/tween optional cosmetic follow-on, never re-derivation); step-dependent display ratio (1x1 deep/mid, ~5x5 shallow — presentation only, never spacing/cache-key). EXECUTES THE FULL _canvas.scale RETIREMENT PATH in the same change (tyre-round2.md ~line 1074): (1) _canvas.scale/_canvas.position/_apply_transform continuous-scale model; (2) the _zs/_zs_stroke/_zs_ring_radius compensation family in atlas_window_geometry_nature.gd — deleted wholesale; (3) select_rung + MAX_COVERAGE_M + compute_tile_grid (T-1152/T-1153 code); (4) the AtlasViewer/orbital-mosaic-vs-window two-code-path split (collapses to ''Global opener is rung 0, one viewer one path''); (5) _view_zoom float + _canonical_fit_zoom. Their test suites (coverage-walk unit tests, continuous-zoom seam tests, compute_tile_grid mosaic tests) are DELETED in the same change — never leave old goldens asserting against retired frames (the T-1157 dead-goldens rule); replacement coverage lands under re-scoped T-1157. SURVIVING SURFACES — do not delete (SI gap 1): atlas_window_tile_set.gd''s LRU SHAPE survives as the skeleton for the client cache-store ticket (adapt it, do not delete it — it is not on the retirement path); _filter_for_granularity_v2 (NEAREST/LINEAR per rung is still a real RTT draw-time question); compositing/legend/overlay-bar chrome (call-site updates only). Determinism boundary per D-255(e): client interpolates only within the closed server-supplied input set. Design sources: stig-round1.md, stig-round2.md, D-255(a)(b)(e).', 'in_progress', 'high', NULL, 'client', 'D-255', '2026-07-24 07:11:47.490', '2026-07-25 06:10:59.087', NULL, 'ab46571f7628768389f3a7a4bbbb8dce', 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 ('06FS5XZF4A978WS1J66Q2JBK8C', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Basin-outlet → course-network wiring: the endorheic cue deliverable (D-255)', 'Thread HydrologyResult overflow basins'' resolved outlet_path/spill points into RiverNetwork/courses as D8 downstream continuations, so an overflow lake''s exit river appears in the served courses and an endorheic basin''s absence of one reads as ''no course leaves this lake''. THE RULED ENDORHEIC CUE (D-227 amendment (4), converged Dudley+Araminta): outflow-course PRESENCE — no wire bit, no 18th zone (proportionality: 1,030/22,270 = 4.63% endorheic doesn''t justify widening the frozen vocabulary; the inference is definitionally sound with no misfire case — every Overflow basin has a guaranteed non-empty outlet_path (tested: overflowing_basin_has_nonempty_outlet_path), every Endorheic none). Pre-cleared ADDITIVE by T-1170 Ruling 7b: the RIVER_DOWNSTREAM_TERMINAL sentinel was reserved exactly for endorheic interior sinks, and river_course::build_edges is already no-op-safe on TERMINAL (verified) — not a wire migration. Required regardless of the cue: overflow lakes must show their exit rivers to be hydrologically honest on the map (a lake that drains but shows no outlet river is simply wrong). Until this lands, the map shows lakes (via the lake-sourcing ticket) but not the drains-vs-closed distinction — the recorded honest sequencing. Endorheic basins map to TERMINAL; overflow outlets extend the D8 network from the spill cell along outlet_path. Design sources: dudley-interview2-response.md CORRECTED section, araminta-round2.md §(e), t1177-hydrology.md, D-227 amendment (4).', 'in_progress', 'medium', NULL, 'server', 'D-227', '2026-07-24 07:12:29.986', '2026-07-25 06:10:59.091', NULL, 'd39af2eca3bcdf560b55508126c1375a', 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 ('06FS5XZF4A978WS1J66Q2JBK8C', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Basin-outlet → course-network wiring: the endorheic cue deliverable (D-255)', 'Thread HydrologyResult overflow basins'' resolved outlet_path/spill points into RiverNetwork/courses as D8 downstream continuations, so an overflow lake''s exit river appears in the served courses and an endorheic basin''s absence of one reads as ''no course leaves this lake''. THE RULED ENDORHEIC CUE (D-227 amendment (4), converged Dudley+Araminta): outflow-course PRESENCE — no wire bit, no 18th zone (proportionality: 1,030/22,270 = 4.63% endorheic doesn''t justify widening the frozen vocabulary; the inference is definitionally sound with no misfire case — every Overflow basin has a guaranteed non-empty outlet_path (tested: overflowing_basin_has_nonempty_outlet_path), every Endorheic none). Pre-cleared ADDITIVE by T-1170 Ruling 7b: the RIVER_DOWNSTREAM_TERMINAL sentinel was reserved exactly for endorheic interior sinks, and river_course::build_edges is already no-op-safe on TERMINAL (verified) — not a wire migration. Required regardless of the cue: overflow lakes must show their exit rivers to be hydrologically honest on the map (a lake that drains but shows no outlet river is simply wrong). Until this lands, the map shows lakes (via the lake-sourcing ticket) but not the drains-vs-closed distinction — the recorded honest sequencing. Endorheic basins map to TERMINAL; overflow outlets extend the D8 network from the spill cell along outlet_path. Design sources: dudley-interview2-response.md CORRECTED section, araminta-round2.md §(e), t1177-hydrology.md, D-227 amendment (4).', 'review', 'medium', NULL, 'server', 'D-227', '2026-07-24 07:12:29.986', '2026-07-25 06:46:29.802', NULL, '455277b0091916caf6f7b723f9718c48', 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 ('06FS5XT949C8D2JZRWDX2CRZN8', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Client map component: RTT terrain + screen-space annotations + stepped transport (D-255)', 'The two-layer client rebuild per D-255(a)(b)(e): a render-to-texture terrain layer (per-step data canvas colorized CPU-side — c1 ruling: Image.set_pixel, measured 77.5 ns/cell flat, NOT a hand-rolled buffer write which measured 2x slower; L8 planes where single-channel per measurement ⑤; texture.update reuse on step-cross) + an unscaled screen-space annotation sibling (names, glyphs, course polylines, markers — literal px sizes, positions world→screen). Stepped transport: discrete rung index 0-5, scroll steps, cursor-anchored, edge-scroll pan, full-zoom-out reset to the rung-0 Global frame; hold-fetch-swap on step-cross (morph/tween optional cosmetic follow-on, never re-derivation); step-dependent display ratio (1x1 deep/mid, ~5x5 shallow — presentation only, never spacing/cache-key). EXECUTES THE FULL _canvas.scale RETIREMENT PATH in the same change (tyre-round2.md ~line 1074): (1) _canvas.scale/_canvas.position/_apply_transform continuous-scale model; (2) the _zs/_zs_stroke/_zs_ring_radius compensation family in atlas_window_geometry_nature.gd — deleted wholesale; (3) select_rung + MAX_COVERAGE_M + compute_tile_grid (T-1152/T-1153 code); (4) the AtlasViewer/orbital-mosaic-vs-window two-code-path split (collapses to ''Global opener is rung 0, one viewer one path''); (5) _view_zoom float + _canonical_fit_zoom. Their test suites (coverage-walk unit tests, continuous-zoom seam tests, compute_tile_grid mosaic tests) are DELETED in the same change — never leave old goldens asserting against retired frames (the T-1157 dead-goldens rule); replacement coverage lands under re-scoped T-1157. SURVIVING SURFACES — do not delete (SI gap 1): atlas_window_tile_set.gd''s LRU SHAPE survives as the skeleton for the client cache-store ticket (adapt it, do not delete it — it is not on the retirement path); _filter_for_granularity_v2 (NEAREST/LINEAR per rung is still a real RTT draw-time question); compositing/legend/overlay-bar chrome (call-site updates only). Determinism boundary per D-255(e): client interpolates only within the closed server-supplied input set. Design sources: stig-round1.md, stig-round2.md, D-255(a)(b)(e).', 'review', 'high', NULL, 'client', 'D-255', '2026-07-24 07:11:47.490', '2026-07-25 06:57:31.456', NULL, '5d5139d002c39df9c37f1e463a94125e', 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 ('06FSG8P6C6WXEG3X81PVCQ833M', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Lake shorelines render as hard step-edges — no transition band unlike ocean coastlines', 'Found in the T-1182 eyeball (2026-07-25): at District and Quarter rung on GJ338Bd''s big lake (DistrictPos 13652,4360), the lake shoreline is a hard rectilinear step-edge while ocean coastlines in the SAME frame show a proper multi-tone refined transition band. Two hypotheses, likely the second: (a) the T-1184 continuous filled>elevation comparison isn''t refining lake edges positionally as designed (would violate the ticket''s own ''edges refine with rung like coastlines'' requirement — verify with a shoreline-crossing sample sweep at multiple rungs); (b) PRESENTATION: ocean transition tones come from ocean_fraction_q banding, but lake cells have ocean_fraction_q=0 and Lake is a binary morphology class — so even a perfectly-refined lake edge has no gradient vocabulary to draw with. If (b), the fix is a lake-margin tone source (e.g. a filled-surface-depth band sampled at derive time, or client-side shading from the served elevation field near Lake cells — must stay inside D-255(e)''s closed input set). Determine which, then fix accordingly. Not a T-1182 defect (viewer draws what the server sends). Captures: .cache/screenshots/t1182-eyeball/rung-ladder/. Related: T-1184, D-227 amendment (4), D-255(e).', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-25 07:17:23.681', '2026-07-25 07:17:23.681', NULL, '2ac92dcf5e322b6e4e744e999c712cb0', 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 ('06FSG8PRRFJQ3Y2V3R348DDRRM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Region-rung viewport can exceed small-body circumference — continent visibly repeats (longitude wrap UX)', 'Found in the T-1182 eyeball (2026-07-25): on Arbour (R=6711km, circumference ~42,166km), the Region rung''s viewport-fit canvas (~200x210 cells x 204.8km) slightly exceeds the body circumference, so derive_orbital_at_metres'' deliberate longitude wrap (rem_euclid) makes the continent visibly repeat side-by-side. Not a math bug — the world legitimately wraps in view. UX question: clamp the Region-rung viewport extent to the body circumference (extent is already server-clamped per D-255(b) — a per-body circumference cap on the requested extent would be a client-side transport refinement), or draw a wrap seam indicator. Smaller bodies make it worse. Captures: .cache/screenshots/t1182-eyeball/rung-ladder/02-region.png. Related: D-243 elastic seam, D-255(a).', 'backlog', 'low', NULL, 'client', NULL, '2026-07-25 07:17:28.388', '2026-07-25 07:17:28.388', NULL, 'b04c525acb596ca14df23c445d6bccfa', 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 ('06FRJD6EKPBQE08NA8VHP692W8', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Redesign the atlas visual-capture goldens for the zoom ladder (visual_scenarios/visual_capture)', 'T-1153 broke the T-1120/T-1121 golden capture harness: client/tests/visual_scenarios.gd and visual_capture.gd call app._regional_screen._viewer.get_heightmap_texture()/show_body() — both crash against AtlasWindowViewer (regional_screen now wraps the ladder viewer; AtlasViewer is unreachable from nav). Confirmed NOT wired into tests/run-godot or the pre-push hook (no reference in either), so nothing blocks, but the 12 curated atlas_* goldens and the --atlas-matrix capture path are dead until redesigned for the ladder (capture at defined rungs/zooms instead of the fixed heightmap frame; the new canonical orbital frame is the natural replacement for the old whole-body shot). Also decide the fate of the retired AtlasViewer code these harnesses were the last live consumers of — likely deletable once T-1156 (overlay port) settles what carries forward.
Live round 4 addendum: client/tests/test_atlas_window_overlay_draw_smoke.gd (3 scenarios -> now 2 after removing a flaky harness-sanity check: single-window draw + tile-mosaic draw, both asserting real composited pixels via SubViewport render) is a standalone gdUnit file with its own headless-skip guard, added because this redesign hadn''t landed yet. When this ticket lands, migrate its 2 scenarios into the redesigned tests/visual.json harness (folding the standalone file away) for consistency with the rest of the real-rendering test suite — see docs/DEVOPS.md''s ''Real-rendering test exception'' note for the interim rationale.
RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): this becomes the capture harness for the STEPPED render architecture — goldens key on (body, step) canvases, not the retired continuous-zoom frames. The T-1152/T-1153 test suites (coverage-walk, continuous-zoom seams, compute_tile_grid mosaic) retire with their code in T-1182''s change; this re-scoped harness is their replacement and the eyeball-check substrate for every step canvas. More needed than ever — keep, don''t cancel.
PR #203 fix-round inventory (2026-07-25): the retirement is now TOTAL — AtlasViewer and its whole cluster (atlas_viewer, atlas_marker_overlay, atlas_descend_geometry, atlas_legend_panel, atlas_generation_proxy, atlas_generation_state) are deleted. visual_scenarios.gd (_setup_atlas_golden_shot, 6 of 12 atlas_GJ* z2_0/z4_0 non-fit scenarios) and visual_capture.gd (_run_atlas_matrix) now call StepCanvasViewer methods that do not exist: get_heightmap_texture(), set_view(zoom, offset), get_view_offset() — the fit-zoom shots early-return before hitting them. Ruled in PR #203: NO shim (would resurrect the retired continuous-zoom API D-255 kills); the redesign here keys captures on (body, rung) via StepCanvasViewer''s real surface (enter/_scroll_rung/rung state + the echoed extent). get_overlay_defs()/set_overlay_visible() duck-type fine and survive. Until this lands, those 6 goldens + atlas_matrix are knowingly dead (manual make targets only, gate-invisible — confirmed again).', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-22 09:42:47.197', '2026-07-25 07:32:19.969', NULL, '258f3e5dbabf9935a55bd33ff400608b', 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 ('06FS5XT949C8D2JZRWDX2CRZN8', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Client map component: RTT terrain + screen-space annotations + stepped transport (D-255)', 'The two-layer client rebuild per D-255(a)(b)(e): a render-to-texture terrain layer (per-step data canvas colorized CPU-side — c1 ruling: Image.set_pixel, measured 77.5 ns/cell flat, NOT a hand-rolled buffer write which measured 2x slower; L8 planes where single-channel per measurement ⑤; texture.update reuse on step-cross) + an unscaled screen-space annotation sibling (names, glyphs, course polylines, markers — literal px sizes, positions world→screen). Stepped transport: discrete rung index 0-5, scroll steps, cursor-anchored, edge-scroll pan, full-zoom-out reset to the rung-0 Global frame; hold-fetch-swap on step-cross (morph/tween optional cosmetic follow-on, never re-derivation); step-dependent display ratio (1x1 deep/mid, ~5x5 shallow — presentation only, never spacing/cache-key). EXECUTES THE FULL _canvas.scale RETIREMENT PATH in the same change (tyre-round2.md ~line 1074): (1) _canvas.scale/_canvas.position/_apply_transform continuous-scale model; (2) the _zs/_zs_stroke/_zs_ring_radius compensation family in atlas_window_geometry_nature.gd — deleted wholesale; (3) select_rung + MAX_COVERAGE_M + compute_tile_grid (T-1152/T-1153 code); (4) the AtlasViewer/orbital-mosaic-vs-window two-code-path split (collapses to ''Global opener is rung 0, one viewer one path''); (5) _view_zoom float + _canonical_fit_zoom. Their test suites (coverage-walk unit tests, continuous-zoom seam tests, compute_tile_grid mosaic tests) are DELETED in the same change — never leave old goldens asserting against retired frames (the T-1157 dead-goldens rule); replacement coverage lands under re-scoped T-1157. SURVIVING SURFACES — do not delete (SI gap 1): atlas_window_tile_set.gd''s LRU SHAPE survives as the skeleton for the client cache-store ticket (adapt it, do not delete it — it is not on the retirement path); _filter_for_granularity_v2 (NEAREST/LINEAR per rung is still a real RTT draw-time question); compositing/legend/overlay-bar chrome (call-site updates only). Determinism boundary per D-255(e): client interpolates only within the closed server-supplied input set. Design sources: stig-round1.md, stig-round2.md, D-255(a)(b)(e).', 'done', 'high', NULL, 'client', 'D-255', '2026-07-24 07:11:47.490', '2026-07-25 08:39:14.793', NULL, '14d720df58c8c8a05755bfcc43a0c7f1', 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 ('06FS5XZF4A978WS1J66Q2JBK8C', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Basin-outlet → course-network wiring: the endorheic cue deliverable (D-255)', 'Thread HydrologyResult overflow basins'' resolved outlet_path/spill points into RiverNetwork/courses as D8 downstream continuations, so an overflow lake''s exit river appears in the served courses and an endorheic basin''s absence of one reads as ''no course leaves this lake''. THE RULED ENDORHEIC CUE (D-227 amendment (4), converged Dudley+Araminta): outflow-course PRESENCE — no wire bit, no 18th zone (proportionality: 1,030/22,270 = 4.63% endorheic doesn''t justify widening the frozen vocabulary; the inference is definitionally sound with no misfire case — every Overflow basin has a guaranteed non-empty outlet_path (tested: overflowing_basin_has_nonempty_outlet_path), every Endorheic none). Pre-cleared ADDITIVE by T-1170 Ruling 7b: the RIVER_DOWNSTREAM_TERMINAL sentinel was reserved exactly for endorheic interior sinks, and river_course::build_edges is already no-op-safe on TERMINAL (verified) — not a wire migration. Required regardless of the cue: overflow lakes must show their exit rivers to be hydrologically honest on the map (a lake that drains but shows no outlet river is simply wrong). Until this lands, the map shows lakes (via the lake-sourcing ticket) but not the drains-vs-closed distinction — the recorded honest sequencing. Endorheic basins map to TERMINAL; overflow outlets extend the D8 network from the spill cell along outlet_path. Design sources: dudley-interview2-response.md CORRECTED section, araminta-round2.md §(e), t1177-hydrology.md, D-227 amendment (4).', 'done', 'medium', NULL, 'server', 'D-227', '2026-07-24 07:12:29.986', '2026-07-25 08:39:14.797', NULL, 'c958b35fd5cbdb472ba132591cd1f632', 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 ('06FS5XW0ZMPVYA2CPBBTP88TQC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Client cache store: FileAccess dir, two-axis eviction, version tag, retention cap (D-255)', 'The client-side cache tiers per D-255(d) and Stig''s three-tier spec (stig-round2.md (c)): in-memory LRU (adapting atlas_window_tile_set.gd''s surviving LRU skeleton from the map-component ticket) + disk-backed FileAccess cache dir with index. Two-axis eviction per D-227 amendment (1) — staleness and storage are DISTINCT: geometry entries never go stale (determinism; byte-valid forever) and are evicted only by (2a) time-since-last-visit (Jeroen''s storage-thrift ruling) and (2b) LRU capacity budget, two independent sweeps kept separate; sim-state entries (frozen/flooded planes) carry the real staleness TTL (SIM_STATE_TTL formula — served/invalidated per the serving ticket''s server-side formula). Rung-0 Global entries: retention floor, never swept. HARDENING REQUIREMENTS from D-255(d), both mandatory: (i) per-body deep-rung retention cap (max resident chunk/block-spacing tile count or disk quota per body, independent of the rung-0 floor — the structural closure of the D-226(d) accumulation gap; the AtlasAgentInterface QA channel inherits the same cap); (ii) every persistent entry carries a schema/version tag (project version or generator stamp) — read-time mismatch = cache miss + re-fetch, NEVER decode (the D-192 co-ship boundary; a disk cache survives game updates). Index-entry schema sketch in stig-round2.md (key/file_path/tier/written_at/last_read_at/size_bytes/sim_ttl/retention_floor); sweep triggers on body-open + coarse background timer, never per-frame. Explicitly rejected by the workshop: SQLite in any shape (server-side relocation, godot-sqlite addon) — plain FileAccess + index won on both agents'' independent analyses. Design sources: stig-round2.md (c), dudley-round2.md (d), D-255(d), D-227 amendments (1)(2).', 'in_progress', 'high', NULL, 'client', 'D-255', '2026-07-24 07:12:01.789', '2026-07-25 08:40:03.632', NULL, 'a08751277ca8c113ad7b9427c8b9a579', 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 ('06FS5XW0ZMPVYA2CPBBTP88TQC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Client cache store: FileAccess dir, two-axis eviction, version tag, retention cap (D-255)', 'The client-side cache tiers per D-255(d) and Stig''s three-tier spec (stig-round2.md (c)): in-memory LRU (adapting atlas_window_tile_set.gd''s surviving LRU skeleton from the map-component ticket) + disk-backed FileAccess cache dir with index. Two-axis eviction per D-227 amendment (1) — staleness and storage are DISTINCT: geometry entries never go stale (determinism; byte-valid forever) and are evicted only by (2a) time-since-last-visit (Jeroen''s storage-thrift ruling) and (2b) LRU capacity budget, two independent sweeps kept separate; sim-state entries (frozen/flooded planes) carry the real staleness TTL (SIM_STATE_TTL formula — served/invalidated per the serving ticket''s server-side formula). Rung-0 Global entries: retention floor, never swept. HARDENING REQUIREMENTS from D-255(d), both mandatory: (i) per-body deep-rung retention cap (max resident chunk/block-spacing tile count or disk quota per body, independent of the rung-0 floor — the structural closure of the D-226(d) accumulation gap; the AtlasAgentInterface QA channel inherits the same cap); (ii) every persistent entry carries a schema/version tag (project version or generator stamp) — read-time mismatch = cache miss + re-fetch, NEVER decode (the D-192 co-ship boundary; a disk cache survives game updates). Index-entry schema sketch in stig-round2.md (key/file_path/tier/written_at/last_read_at/size_bytes/sim_ttl/retention_floor); sweep triggers on body-open + coarse background timer, never per-frame. Explicitly rejected by the workshop: SQLite in any shape (server-side relocation, godot-sqlite addon) — plain FileAccess + index won on both agents'' independent analyses. Design sources: stig-round2.md (c), dudley-round2.md (d), D-255(d), D-227 amendments (1)(2).', 'review', 'high', NULL, 'client', 'D-255', '2026-07-24 07:12:01.789', '2026-07-25 09:11:50.463', NULL, '04e69662c15ff8d68e447fd1fd2f4403', 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 ('06FSH4VE4TTJGFGVVQWHACDBTM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Step-canvas wire staleness signal + client sim_ttl threading (D-253 clock seam)', 'Client disk cache (T-1183) and server StepCanvasCache (T-1181) both carry wired-ahead sim-state TTL machinery for glaciation/flooded_q, but no staleness signal crosses the wire: the server SIM_STATE_TTL formula is tick-based and server-side only, and step_canvas_request.gd calls disk_cache.put() without sim_ttl_sec, so every canvas is (correctly, today) Tier-2 geometry — flooded_q is hardwired 0 and glaciation is the static classification, byte-identical to a fresh derive (server/src/atlas/step_canvas.rs module doc, D-253 wired-ahead). The moment D-253''s sim-state driving clock lands and glaciation/flooded values actually vary with sim time, StepCanvasResponse must carry a TTL (or equivalent staleness signal) and on_response must thread it into the existing put(sim_ttl_sec) parameter so client disk-cached canvases re-request instead of serving stale sim-state indefinitely. Raised by tyre in PR #204 review; adjudicated as a future seam, not a live gap. Blocked conceptually on the D-253 driving clock existing.', 'backlog', 'low', NULL, 'client', 'D-227', '2026-07-25 09:20:26.662', '2026-07-25 09:20:26.662', NULL, '33e980c6a5a4d49fb8b6f2c56a69c274', 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 ('06FSHEPE2Q7GRKYJARH8Z8RPMR', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas edge-scroll pans on mouse motion while window unfocused', 'Found during the PR #204 restart-persistence eyeball: StepCanvasViewer._process() edge-scroll (_is_cursor_edge_scrolling / EDGE_SCROLL_MARGIN_PX=24) acts on _last_mouse_pos updated from real InputEventMouseMotion, which X11/XWayland delivers to the window whenever the pointer is over it — focus not required. Quantitatively proven in the eyeball forensics (drift = PAN_SPEED_PX_S * frame_time * held-frames, 102.69px observed vs 102.67px predicted): a windowed-mode player mousing over the unfocused game window drifts the atlas map. Fix direction: gate edge-scroll (and likely other cursor-anchored viewer reactions) on window focus (DisplayServer window focus / Window.has_focus()), matching standard windowed-game behavior. Low priority — cosmetic in practice, no data effect.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-25 10:03:27.125', '2026-07-25 10:03:27.125', NULL, '6f4a4281ad8eb68ba4343e2254955180', 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 ('06FRJD6EKPBQE08NA8VHP692W8', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Redesign the atlas visual-capture goldens for the zoom ladder (visual_scenarios/visual_capture)', 'T-1153 broke the T-1120/T-1121 golden capture harness: client/tests/visual_scenarios.gd and visual_capture.gd call app._regional_screen._viewer.get_heightmap_texture()/show_body() — both crash against AtlasWindowViewer (regional_screen now wraps the ladder viewer; AtlasViewer is unreachable from nav). Confirmed NOT wired into tests/run-godot or the pre-push hook (no reference in either), so nothing blocks, but the 12 curated atlas_* goldens and the --atlas-matrix capture path are dead until redesigned for the ladder (capture at defined rungs/zooms instead of the fixed heightmap frame; the new canonical orbital frame is the natural replacement for the old whole-body shot). Also decide the fate of the retired AtlasViewer code these harnesses were the last live consumers of — likely deletable once T-1156 (overlay port) settles what carries forward.
Live round 4 addendum: client/tests/test_atlas_window_overlay_draw_smoke.gd (3 scenarios -> now 2 after removing a flaky harness-sanity check: single-window draw + tile-mosaic draw, both asserting real composited pixels via SubViewport render) is a standalone gdUnit file with its own headless-skip guard, added because this redesign hadn''t landed yet. When this ticket lands, migrate its 2 scenarios into the redesigned tests/visual.json harness (folding the standalone file away) for consistency with the rest of the real-rendering test suite — see docs/DEVOPS.md''s ''Real-rendering test exception'' note for the interim rationale.
RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): this becomes the capture harness for the STEPPED render architecture — goldens key on (body, step) canvases, not the retired continuous-zoom frames. The T-1152/T-1153 test suites (coverage-walk, continuous-zoom seams, compute_tile_grid mosaic) retire with their code in T-1182''s change; this re-scoped harness is their replacement and the eyeball-check substrate for every step canvas. More needed than ever — keep, don''t cancel.
PR #203 fix-round inventory (2026-07-25): the retirement is now TOTAL — AtlasViewer and its whole cluster (atlas_viewer, atlas_marker_overlay, atlas_descend_geometry, atlas_legend_panel, atlas_generation_proxy, atlas_generation_state) are deleted. visual_scenarios.gd (_setup_atlas_golden_shot, 6 of 12 atlas_GJ* z2_0/z4_0 non-fit scenarios) and visual_capture.gd (_run_atlas_matrix) now call StepCanvasViewer methods that do not exist: get_heightmap_texture(), set_view(zoom, offset), get_view_offset() — the fit-zoom shots early-return before hitting them. Ruled in PR #203: NO shim (would resurrect the retired continuous-zoom API D-255 kills); the redesign here keys captures on (body, rung) via StepCanvasViewer''s real surface (enter/_scroll_rung/rung state + the echoed extent). get_overlay_defs()/set_overlay_visible() duck-type fine and survive. Until this lands, those 6 goldens + atlas_matrix are knowingly dead (manual make targets only, gate-invisible — confirmed again).
Harness techniques inventory addition (from the PR #204 / T-1183 restart-persistence eyeball, 2026-07-25) — proven live-rendering eyeball patterns for shared-desktop sessions:
1. InputSwallower pattern: root-level Node (added first, low process_priority), _input()+_unhandled_input() both call get_viewport().set_input_as_handled() unconditionally — neutralizes real desktop input reaching a `-s` SceneTree driver''s real window (X11/XWayland delivers pointer events to unfocused windows under the cursor). Verify via a per-capture-window intercept counter: N>0 intercepted AND zero resulting view drift is proof; 0 intercepted proves nothing.
2. In-process navigation via viewer methods, not OS input: call the same methods the app''s own gdUnit suite exercises (StepCanvasViewer.enter()/_scroll_rung(); RegionalScreen.enter() atlas-matrix precedent in visual_capture.gd) — identical production code path, no OS injection needed (Wayland/KWin blocks synthetic X11 injection for XWayland clients entirely).
3. View-transform logging at every capture: world_center, view_offset, the drawing Node2D''s canvas position, footprint_px alongside every screenshot — turns "pixels don''t match" into a root-causable mechanism (PR #204: drift matched PAN_SPEED_PX_S*frame_time*held-frames to 0.02px, identifying unfocused edge-scroll bleed, now T-1191).
4. Full-run-restart-on-anomaly, never partial retry: any mid-sequence assertion failure discards the whole run (cold-wipe for a cold-role attempt; fresh-process-only for a revisit-role attempt) — partial retries silently corrupt the state the comparison depends on.
5. Fixed-center revisit mode: record a prior run''s actual derived centers and re-request them literally on the comparison run, bypassing cursor-anchored re-derivation — required when run 2 must land on run 1''s exact cache keys, not merely repeat the gesture.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-22 09:42:47.197', '2026-07-25 10:11:48.141', NULL, 'af61dc6cbe10368f5a6ee32397c3a71e', 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 ('06FSG8PRRFJQ3Y2V3R348DDRRM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Region-rung viewport can exceed small-body circumference — continent visibly repeats (longitude wrap UX)', 'Found in the T-1182 eyeball (2026-07-25): on Arbour (R=6711km, circumference ~42,166km), the Region rung''s viewport-fit canvas (~200x210 cells x 204.8km) slightly exceeds the body circumference, so derive_orbital_at_metres'' deliberate longitude wrap (rem_euclid) makes the continent visibly repeat side-by-side. Not a math bug — the world legitimately wraps in view. UX question: clamp the Region-rung viewport extent to the body circumference (extent is already server-clamped per D-255(b) — a per-body circumference cap on the requested extent would be a client-side transport refinement), or draw a wrap seam indicator. Smaller bodies make it worse. Captures: .cache/screenshots/t1182-eyeball/rung-ladder/02-region.png. Related: D-243 elastic seam, D-255(a).
ESCALATION from the T-1183 eyeball captures (2026-07-25, GJ1c, .cache/screenshots/t1183-eyeball-run2/02-region.png): this is NOT a small-body edge case and it is TWO axes, not one. At a 1920x1080 viewport the Region rung requested extent 384x216 cells x 204.8 km = 78,643 x 44,237 km — exceeding even an Earth-class circumference (~40,000 km) roughly 2x horizontally, so the continents repeat side-by-side on EVERY body at Region rung, and the vertical span overruns both poles: rows past the pole clamp to the last row and render as smeared vertical stripes filling the bottom ~40% of the frame (much uglier than the longitude repeat — looks like corrupted data, not a wrapped map). Jeroen flagged the frame as looking broken. Fix shape: cap the Region-rung request extent to the body''s region grid (circumference in the x axis, pole-to-pole in the y axis — both already known client-side from the Global canvas extent echo), letterbox the remainder, and consider the same guard for any rung whose footprint can exceed the body. Priority raised low->high: this makes the Region rung look broken on every body at standard viewports.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-25 07:17:28.388', '2026-07-25 10:17:29.416', NULL, '9da309bf615eaf4aa6422a02418f6f83', 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 ('06FSHHXXTS56ZBB78N2W43DD2G', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Global rung presentation: fit/center the canvas in the viewport (currently top-left anchored at raw scale)', 'From the T-1183 eyeball captures (2026-07-25, .cache/screenshots/t1183-eyeball-run2/01-global.png): the Global rung canvas (GJ1c: 177x88 texels) draws top-left-anchored at its raw integer scale, occupying only the upper-left ~885x440 of a 1920x1080 viewport — the remaining ~60% of the screen is empty background, and the Atlas legend panel overlaps the canvas itself. The body-surface opener is the first thing a player sees in the Atlas; it should fit-scale (integer multiple preserving texel-exactness per D-255, or letterboxed non-integer if ruled acceptable) and center in the available viewport, with the legend laid out beside rather than over it. Pure client map-art presentation — no wire/server change; D-255(e) unaffected (same input set, different placement/scale).', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-25 10:17:34.934', '2026-07-25 10:17:34.934', NULL, 'bab4e2b53aa50f7f825551f68f7e086d', 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 ('06FSG8PRRFJQ3Y2V3R348DDRRM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Region-rung viewport can exceed small-body circumference — continent visibly repeats (longitude wrap UX)', 'Found in the T-1182 eyeball (2026-07-25): on Arbour (R=6711km, circumference ~42,166km), the Region rung''s viewport-fit canvas (~200x210 cells x 204.8km) slightly exceeds the body circumference, so derive_orbital_at_metres'' deliberate longitude wrap (rem_euclid) makes the continent visibly repeat side-by-side. Not a math bug — the world legitimately wraps in view. UX question: clamp the Region-rung viewport extent to the body circumference (extent is already server-clamped per D-255(b) — a per-body circumference cap on the requested extent would be a client-side transport refinement), or draw a wrap seam indicator. Smaller bodies make it worse. Captures: .cache/screenshots/t1182-eyeball/rung-ladder/02-region.png. Related: D-243 elastic seam, D-255(a).
ESCALATION from the T-1183 eyeball captures (2026-07-25, GJ1c, .cache/screenshots/t1183-eyeball-run2/02-region.png): this is NOT a small-body edge case and it is TWO axes, not one. At a 1920x1080 viewport the Region rung requested extent 384x216 cells x 204.8 km = 78,643 x 44,237 km — exceeding even an Earth-class circumference (~40,000 km) roughly 2x horizontally, so the continents repeat side-by-side on EVERY body at Region rung, and the vertical span overruns both poles: rows past the pole clamp to the last row and render as smeared vertical stripes filling the bottom ~40% of the frame (much uglier than the longitude repeat — looks like corrupted data, not a wrapped map). Jeroen flagged the frame as looking broken. Fix shape: cap the Region-rung request extent to the body''s region grid (circumference in the x axis, pole-to-pole in the y axis — both already known client-side from the Global canvas extent echo), letterbox the remainder, and consider the same guard for any rung whose footprint can exceed the body. Priority raised low->high: this makes the Region rung look broken on every body at standard viewports.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-25 07:17:28.388', '2026-07-25 10:17:39.835', NULL, 'a033d5af39befbb1b60d945dcf0ff078', 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 ('06FS5XW0ZMPVYA2CPBBTP88TQC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Client cache store: FileAccess dir, two-axis eviction, version tag, retention cap (D-255)', 'The client-side cache tiers per D-255(d) and Stig''s three-tier spec (stig-round2.md (c)): in-memory LRU (adapting atlas_window_tile_set.gd''s surviving LRU skeleton from the map-component ticket) + disk-backed FileAccess cache dir with index. Two-axis eviction per D-227 amendment (1) — staleness and storage are DISTINCT: geometry entries never go stale (determinism; byte-valid forever) and are evicted only by (2a) time-since-last-visit (Jeroen''s storage-thrift ruling) and (2b) LRU capacity budget, two independent sweeps kept separate; sim-state entries (frozen/flooded planes) carry the real staleness TTL (SIM_STATE_TTL formula — served/invalidated per the serving ticket''s server-side formula). Rung-0 Global entries: retention floor, never swept. HARDENING REQUIREMENTS from D-255(d), both mandatory: (i) per-body deep-rung retention cap (max resident chunk/block-spacing tile count or disk quota per body, independent of the rung-0 floor — the structural closure of the D-226(d) accumulation gap; the AtlasAgentInterface QA channel inherits the same cap); (ii) every persistent entry carries a schema/version tag (project version or generator stamp) — read-time mismatch = cache miss + re-fetch, NEVER decode (the D-192 co-ship boundary; a disk cache survives game updates). Index-entry schema sketch in stig-round2.md (key/file_path/tier/written_at/last_read_at/size_bytes/sim_ttl/retention_floor); sweep triggers on body-open + coarse background timer, never per-frame. Explicitly rejected by the workshop: SQLite in any shape (server-side relocation, godot-sqlite addon) — plain FileAccess + index won on both agents'' independent analyses. Design sources: stig-round2.md (c), dudley-round2.md (d), D-255(d), D-227 amendments (1)(2).', 'done', 'high', NULL, 'client', 'D-255', '2026-07-24 07:12:01.789', '2026-07-25 10:17:42.696', NULL, 'fba61e686d33cc2a162aa081fd1a925d', 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 ('06FSCXX7E3BXT7WG0BYDB6PJA0', 'bug', '06FBPPMZNNEV052DBYYY3A897C', 'Region-baseline latitude is pole-anchored while the derive core keys equator-anchored signed regions — northern hemisphere clamps to +90', 'Found during the PR #199 (T-1174/D-256) review fix round, PRE-EXISTING on the window path since the region climate stack (T-1078/T-1113) was wired into derive_at_metres. region_profile::region_centre_latitude_deg (region_profile.rs ~270-280) maps region row 0 to the north pole (+90) with lat_frac clamped to [0,1] — a pole-anchored, non-negative row convention. But the derive core (derive_at_metres_with_riparian, district_profile.rs ~1788) keys regions by floor-dividing EQUATOR-anchored world metres (negative wy = north), producing signed region rows (Earth-class: -49..+49). Consequence: every northern-hemisphere region has negative row -> region_centre_y_m negative -> lat_frac clamps to 0 -> baseline latitude +90 (polar) regardless of true latitude; southern-hemisphere rows 0..~48 read as compressed NORTHERN latitudes ~89..~0.7 (south pole reads as equator). Affects the region temperature baseline (and glaciation/moisture components derived from it) for the window path at every rung, and post-D-256 the batch/survey path inherits the same keys (still an improvement over the pre-D-256 body-wide region-(0,0) baseline, which read ~+89 everywhere). NOT touched in PR #199 to keep the review round scoped; goldens currently pin the wrong-latitude values. FIX SHAPE: make region_centre_latitude_deg mirror the derive core''s inverse mapping (equator-anchored signed rows: lat = -((ry+0.5)*REGION_M/meridian_m clamped to [-0.5,0.5])*180), audit the OTHER convention''s callers (the D-255 rung-0 canvas row space and the collapsed LayerRegionOutput builder use non-negative pole-anchored rows - decide ONE convention per D-256''s one-inverse-mapping principle, likely at the T-1181 rung-0 rebuild), regen affected goldens, and re-run the believability direction check. Related: D-256, D-243, T-1181, T-1078.
In-the-wild datapoint (2026-07-25, T-1184 eyeball session): GJ338Bd (Arbour, R=6711km) survey cell (42,29) — true position latitude -76.2S (wy=8,929,367m, region row 43) — derives with the pole-anchored baseline latitude +13.9N, producing an 18C temperate, glaciation-free lake district near the antarctic circle. Concrete demonstration of the southern-hemisphere-reads-as-northern-tropics half of the bug (the northern half clamps to +90). Conversion math used: scratchpad survey_to_district.py, the D-256 bridge formula.', 'in_progress', 'high', NULL, 'server', NULL, '2026-07-24 23:30:51.889', '2026-07-25 10:20:01.865', NULL, '9385f947d1c1c86418d076fab06a331f', 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 ('06FSG8P6C6WXEG3X81PVCQ833M', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Lake shorelines render as hard step-edges — no transition band unlike ocean coastlines', 'Found in the T-1182 eyeball (2026-07-25): at District and Quarter rung on GJ338Bd''s big lake (DistrictPos 13652,4360), the lake shoreline is a hard rectilinear step-edge while ocean coastlines in the SAME frame show a proper multi-tone refined transition band. Two hypotheses, likely the second: (a) the T-1184 continuous filled>elevation comparison isn''t refining lake edges positionally as designed (would violate the ticket''s own ''edges refine with rung like coastlines'' requirement — verify with a shoreline-crossing sample sweep at multiple rungs); (b) PRESENTATION: ocean transition tones come from ocean_fraction_q banding, but lake cells have ocean_fraction_q=0 and Lake is a binary morphology class — so even a perfectly-refined lake edge has no gradient vocabulary to draw with. If (b), the fix is a lake-margin tone source (e.g. a filled-surface-depth band sampled at derive time, or client-side shading from the served elevation field near Lake cells — must stay inside D-255(e)''s closed input set). Determine which, then fix accordingly. Not a T-1182 defect (viewer draws what the server sends). Captures: .cache/screenshots/t1182-eyeball/rung-ladder/. Related: T-1184, D-227 amendment (4), D-255(e).', 'in_progress', 'medium', NULL, 'server', NULL, '2026-07-25 07:17:23.681', '2026-07-25 10:20:01.872', NULL, '344d93d8cf10d024527cfaa9b1c73816', 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 ('06FSHHXXTS56ZBB78N2W43DD2G', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Global rung presentation: fit/center the canvas in the viewport (currently top-left anchored at raw scale)', 'From the T-1183 eyeball captures (2026-07-25, .cache/screenshots/t1183-eyeball-run2/01-global.png): the Global rung canvas (GJ1c: 177x88 texels) draws top-left-anchored at its raw integer scale, occupying only the upper-left ~885x440 of a 1920x1080 viewport — the remaining ~60% of the screen is empty background, and the Atlas legend panel overlaps the canvas itself. The body-surface opener is the first thing a player sees in the Atlas; it should fit-scale (integer multiple preserving texel-exactness per D-255, or letterboxed non-integer if ruled acceptable) and center in the available viewport, with the legend laid out beside rather than over it. Pure client map-art presentation — no wire/server change; D-255(e) unaffected (same input set, different placement/scale).', 'in_progress', 'medium', NULL, 'client', NULL, '2026-07-25 10:17:34.934', '2026-07-25 10:20:01.872', NULL, '3aa877b0d2e01d1e7a318d4cb086cb88', 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 ('06FSG8PRRFJQ3Y2V3R348DDRRM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Region-rung viewport can exceed small-body circumference — continent visibly repeats (longitude wrap UX)', 'Found in the T-1182 eyeball (2026-07-25): on Arbour (R=6711km, circumference ~42,166km), the Region rung''s viewport-fit canvas (~200x210 cells x 204.8km) slightly exceeds the body circumference, so derive_orbital_at_metres'' deliberate longitude wrap (rem_euclid) makes the continent visibly repeat side-by-side. Not a math bug — the world legitimately wraps in view. UX question: clamp the Region-rung viewport extent to the body circumference (extent is already server-clamped per D-255(b) — a per-body circumference cap on the requested extent would be a client-side transport refinement), or draw a wrap seam indicator. Smaller bodies make it worse. Captures: .cache/screenshots/t1182-eyeball/rung-ladder/02-region.png. Related: D-243 elastic seam, D-255(a).
ESCALATION from the T-1183 eyeball captures (2026-07-25, GJ1c, .cache/screenshots/t1183-eyeball-run2/02-region.png): this is NOT a small-body edge case and it is TWO axes, not one. At a 1920x1080 viewport the Region rung requested extent 384x216 cells x 204.8 km = 78,643 x 44,237 km — exceeding even an Earth-class circumference (~40,000 km) roughly 2x horizontally, so the continents repeat side-by-side on EVERY body at Region rung, and the vertical span overruns both poles: rows past the pole clamp to the last row and render as smeared vertical stripes filling the bottom ~40% of the frame (much uglier than the longitude repeat — looks like corrupted data, not a wrapped map). Jeroen flagged the frame as looking broken. Fix shape: cap the Region-rung request extent to the body''s region grid (circumference in the x axis, pole-to-pole in the y axis — both already known client-side from the Global canvas extent echo), letterbox the remainder, and consider the same guard for any rung whose footprint can exceed the body. Priority raised low->high: this makes the Region rung look broken on every body at standard viewports.', 'in_progress', 'high', NULL, 'client', NULL, '2026-07-25 07:17:28.388', '2026-07-25 10:20:01.872', NULL, 'c7584e29e0d2228da18fc90926380c3d', 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 ('06FSCXX7E3BXT7WG0BYDB6PJA0', 'bug', '06FBPPMZNNEV052DBYYY3A897C', 'Region-baseline latitude is pole-anchored while the derive core keys equator-anchored signed regions — northern hemisphere clamps to +90', 'Found during the PR #199 (T-1174/D-256) review fix round, PRE-EXISTING on the window path since the region climate stack (T-1078/T-1113) was wired into derive_at_metres. region_profile::region_centre_latitude_deg (region_profile.rs ~270-280) maps region row 0 to the north pole (+90) with lat_frac clamped to [0,1] — a pole-anchored, non-negative row convention. But the derive core (derive_at_metres_with_riparian, district_profile.rs ~1788) keys regions by floor-dividing EQUATOR-anchored world metres (negative wy = north), producing signed region rows (Earth-class: -49..+49). Consequence: every northern-hemisphere region has negative row -> region_centre_y_m negative -> lat_frac clamps to 0 -> baseline latitude +90 (polar) regardless of true latitude; southern-hemisphere rows 0..~48 read as compressed NORTHERN latitudes ~89..~0.7 (south pole reads as equator). Affects the region temperature baseline (and glaciation/moisture components derived from it) for the window path at every rung, and post-D-256 the batch/survey path inherits the same keys (still an improvement over the pre-D-256 body-wide region-(0,0) baseline, which read ~+89 everywhere). NOT touched in PR #199 to keep the review round scoped; goldens currently pin the wrong-latitude values. FIX SHAPE: make region_centre_latitude_deg mirror the derive core''s inverse mapping (equator-anchored signed rows: lat = -((ry+0.5)*REGION_M/meridian_m clamped to [-0.5,0.5])*180), audit the OTHER convention''s callers (the D-255 rung-0 canvas row space and the collapsed LayerRegionOutput builder use non-negative pole-anchored rows - decide ONE convention per D-256''s one-inverse-mapping principle, likely at the T-1181 rung-0 rebuild), regen affected goldens, and re-run the believability direction check. Related: D-256, D-243, T-1181, T-1078.
In-the-wild datapoint (2026-07-25, T-1184 eyeball session): GJ338Bd (Arbour, R=6711km) survey cell (42,29) — true position latitude -76.2S (wy=8,929,367m, region row 43) — derives with the pole-anchored baseline latitude +13.9N, producing an 18C temperate, glaciation-free lake district near the antarctic circle. Concrete demonstration of the southern-hemisphere-reads-as-northern-tropics half of the bug (the northern half clamps to +90). Conversion math used: scratchpad survey_to_district.py, the D-256 bridge formula.', 'in_progress', 'high', 'dudley', 'server', NULL, '2026-07-24 23:30:51.889', '2026-07-25 10:21:12.179', NULL, '06433fd1cd5a060faf5fe503e7869183', 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 ('06FSG8P6C6WXEG3X81PVCQ833M', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Lake shorelines render as hard step-edges — no transition band unlike ocean coastlines', 'Found in the T-1182 eyeball (2026-07-25): at District and Quarter rung on GJ338Bd''s big lake (DistrictPos 13652,4360), the lake shoreline is a hard rectilinear step-edge while ocean coastlines in the SAME frame show a proper multi-tone refined transition band. Two hypotheses, likely the second: (a) the T-1184 continuous filled>elevation comparison isn''t refining lake edges positionally as designed (would violate the ticket''s own ''edges refine with rung like coastlines'' requirement — verify with a shoreline-crossing sample sweep at multiple rungs); (b) PRESENTATION: ocean transition tones come from ocean_fraction_q banding, but lake cells have ocean_fraction_q=0 and Lake is a binary morphology class — so even a perfectly-refined lake edge has no gradient vocabulary to draw with. If (b), the fix is a lake-margin tone source (e.g. a filled-surface-depth band sampled at derive time, or client-side shading from the served elevation field near Lake cells — must stay inside D-255(e)''s closed input set). Determine which, then fix accordingly. Not a T-1182 defect (viewer draws what the server sends). Captures: .cache/screenshots/t1182-eyeball/rung-ladder/. Related: T-1184, D-227 amendment (4), D-255(e).', 'in_progress', 'medium', 'dudley', 'server', NULL, '2026-07-25 07:17:23.681', '2026-07-25 10:21:12.182', NULL, '2a01d94f51c957ba8b8ce19312765d43', 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 ('06FSG8PRRFJQ3Y2V3R348DDRRM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Region-rung viewport can exceed small-body circumference — continent visibly repeats (longitude wrap UX)', 'Found in the T-1182 eyeball (2026-07-25): on Arbour (R=6711km, circumference ~42,166km), the Region rung''s viewport-fit canvas (~200x210 cells x 204.8km) slightly exceeds the body circumference, so derive_orbital_at_metres'' deliberate longitude wrap (rem_euclid) makes the continent visibly repeat side-by-side. Not a math bug — the world legitimately wraps in view. UX question: clamp the Region-rung viewport extent to the body circumference (extent is already server-clamped per D-255(b) — a per-body circumference cap on the requested extent would be a client-side transport refinement), or draw a wrap seam indicator. Smaller bodies make it worse. Captures: .cache/screenshots/t1182-eyeball/rung-ladder/02-region.png. Related: D-243 elastic seam, D-255(a).
ESCALATION from the T-1183 eyeball captures (2026-07-25, GJ1c, .cache/screenshots/t1183-eyeball-run2/02-region.png): this is NOT a small-body edge case and it is TWO axes, not one. At a 1920x1080 viewport the Region rung requested extent 384x216 cells x 204.8 km = 78,643 x 44,237 km — exceeding even an Earth-class circumference (~40,000 km) roughly 2x horizontally, so the continents repeat side-by-side on EVERY body at Region rung, and the vertical span overruns both poles: rows past the pole clamp to the last row and render as smeared vertical stripes filling the bottom ~40% of the frame (much uglier than the longitude repeat — looks like corrupted data, not a wrapped map). Jeroen flagged the frame as looking broken. Fix shape: cap the Region-rung request extent to the body''s region grid (circumference in the x axis, pole-to-pole in the y axis — both already known client-side from the Global canvas extent echo), letterbox the remainder, and consider the same guard for any rung whose footprint can exceed the body. Priority raised low->high: this makes the Region rung look broken on every body at standard viewports.', 'in_progress', 'high', 'stig', 'client', NULL, '2026-07-25 07:17:28.388', '2026-07-25 10:21:12.361', NULL, '046d604c827f4f892d92da4db10140a7', 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 ('06FSHHXXTS56ZBB78N2W43DD2G', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Global rung presentation: fit/center the canvas in the viewport (currently top-left anchored at raw scale)', 'From the T-1183 eyeball captures (2026-07-25, .cache/screenshots/t1183-eyeball-run2/01-global.png): the Global rung canvas (GJ1c: 177x88 texels) draws top-left-anchored at its raw integer scale, occupying only the upper-left ~885x440 of a 1920x1080 viewport — the remaining ~60% of the screen is empty background, and the Atlas legend panel overlaps the canvas itself. The body-surface opener is the first thing a player sees in the Atlas; it should fit-scale (integer multiple preserving texel-exactness per D-255, or letterboxed non-integer if ruled acceptable) and center in the available viewport, with the legend laid out beside rather than over it. Pure client map-art presentation — no wire/server change; D-255(e) unaffected (same input set, different placement/scale).', 'in_progress', 'medium', 'stig', 'client', NULL, '2026-07-25 10:17:34.934', '2026-07-25 10:21:12.368', NULL, '4e4308dfbb0087160df2c9559f224161', 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 ('06FSHSYM96N2M9GJTQMF58E4WC', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'StepCanvasViewer test suite lacks an injected disk-cache-root seam — tests share the real user://atlas_cache', 'Flagged by stig during the T-1189/T-1192 round (2026-07-25): test_step_canvas_viewer.gd has no injected disk-cache-root seam (unlike test_step_canvas_request.gd, which injects a disposable root), so viewer tests that exercise the disk-cache path against a real-looking body_id (e.g. GJ1c) read/write the REAL user://atlas_cache/ shared across runs — a stale real entry from earlier manual sessions can leak into test behavior. Current mitigation (used by the new T-1189/T-1192 tests and the existing sweep smoke test): distinctive synthetic body_ids. Proper fix: give the viewer suite the same injectable cache-root seam the request suite has (constructor/setter injection through StepCanvasRequest), and migrate the synthetic-body_id tests onto it. Small, test-only.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-25 10:52:37.833', '2026-07-25 10:52:37.833', NULL, '9d36975961c2ee7d6532ccf56cf962fb', 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 ('06FSG8PRRFJQ3Y2V3R348DDRRM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Region-rung viewport can exceed small-body circumference — continent visibly repeats (longitude wrap UX)', 'Found in the T-1182 eyeball (2026-07-25): on Arbour (R=6711km, circumference ~42,166km), the Region rung''s viewport-fit canvas (~200x210 cells x 204.8km) slightly exceeds the body circumference, so derive_orbital_at_metres'' deliberate longitude wrap (rem_euclid) makes the continent visibly repeat side-by-side. Not a math bug — the world legitimately wraps in view. UX question: clamp the Region-rung viewport extent to the body circumference (extent is already server-clamped per D-255(b) — a per-body circumference cap on the requested extent would be a client-side transport refinement), or draw a wrap seam indicator. Smaller bodies make it worse. Captures: .cache/screenshots/t1182-eyeball/rung-ladder/02-region.png. Related: D-243 elastic seam, D-255(a).
ESCALATION from the T-1183 eyeball captures (2026-07-25, GJ1c, .cache/screenshots/t1183-eyeball-run2/02-region.png): this is NOT a small-body edge case and it is TWO axes, not one. At a 1920x1080 viewport the Region rung requested extent 384x216 cells x 204.8 km = 78,643 x 44,237 km — exceeding even an Earth-class circumference (~40,000 km) roughly 2x horizontally, so the continents repeat side-by-side on EVERY body at Region rung, and the vertical span overruns both poles: rows past the pole clamp to the last row and render as smeared vertical stripes filling the bottom ~40% of the frame (much uglier than the longitude repeat — looks like corrupted data, not a wrapped map). Jeroen flagged the frame as looking broken. Fix shape: cap the Region-rung request extent to the body''s region grid (circumference in the x axis, pole-to-pole in the y axis — both already known client-side from the Global canvas extent echo), letterbox the remainder, and consider the same guard for any rung whose footprint can exceed the body. Priority raised low->high: this makes the Region rung look broken on every body at standard viewports.', 'review', 'high', 'stig', 'client', NULL, '2026-07-25 07:17:28.388', '2026-07-25 10:58:29.489', NULL, 'fe602c8ea1224cc7940690357e0097df', 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 ('06FSHHXXTS56ZBB78N2W43DD2G', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Global rung presentation: fit/center the canvas in the viewport (currently top-left anchored at raw scale)', 'From the T-1183 eyeball captures (2026-07-25, .cache/screenshots/t1183-eyeball-run2/01-global.png): the Global rung canvas (GJ1c: 177x88 texels) draws top-left-anchored at its raw integer scale, occupying only the upper-left ~885x440 of a 1920x1080 viewport — the remaining ~60% of the screen is empty background, and the Atlas legend panel overlaps the canvas itself. The body-surface opener is the first thing a player sees in the Atlas; it should fit-scale (integer multiple preserving texel-exactness per D-255, or letterboxed non-integer if ruled acceptable) and center in the available viewport, with the legend laid out beside rather than over it. Pure client map-art presentation — no wire/server change; D-255(e) unaffected (same input set, different placement/scale).', 'review', 'medium', 'stig', 'client', NULL, '2026-07-25 10:17:34.934', '2026-07-25 10:58:29.494', NULL, '82220681d07fcbf2db496e9901b0af8f', 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 ('06FSG8PRRFJQ3Y2V3R348DDRRM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Region-rung viewport can exceed small-body circumference — continent visibly repeats (longitude wrap UX)', 'Found in the T-1182 eyeball (2026-07-25): on Arbour (R=6711km, circumference ~42,166km), the Region rung''s viewport-fit canvas (~200x210 cells x 204.8km) slightly exceeds the body circumference, so derive_orbital_at_metres'' deliberate longitude wrap (rem_euclid) makes the continent visibly repeat side-by-side. Not a math bug — the world legitimately wraps in view. UX question: clamp the Region-rung viewport extent to the body circumference (extent is already server-clamped per D-255(b) — a per-body circumference cap on the requested extent would be a client-side transport refinement), or draw a wrap seam indicator. Smaller bodies make it worse. Captures: .cache/screenshots/t1182-eyeball/rung-ladder/02-region.png. Related: D-243 elastic seam, D-255(a).
ESCALATION from the T-1183 eyeball captures (2026-07-25, GJ1c, .cache/screenshots/t1183-eyeball-run2/02-region.png): this is NOT a small-body edge case and it is TWO axes, not one. At a 1920x1080 viewport the Region rung requested extent 384x216 cells x 204.8 km = 78,643 x 44,237 km — exceeding even an Earth-class circumference (~40,000 km) roughly 2x horizontally, so the continents repeat side-by-side on EVERY body at Region rung, and the vertical span overruns both poles: rows past the pole clamp to the last row and render as smeared vertical stripes filling the bottom ~40% of the frame (much uglier than the longitude repeat — looks like corrupted data, not a wrapped map). Jeroen flagged the frame as looking broken. Fix shape: cap the Region-rung request extent to the body''s region grid (circumference in the x axis, pole-to-pole in the y axis — both already known client-side from the Global canvas extent echo), letterbox the remainder, and consider the same guard for any rung whose footprint can exceed the body. Priority raised low->high: this makes the Region rung look broken on every body at standard viewports.', 'done', 'high', 'stig', 'client', NULL, '2026-07-25 07:17:28.388', '2026-07-25 11:29:11.342', NULL, 'c7f85f322a503efc1cee80ea14bda6b8', 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 ('06FSHHXXTS56ZBB78N2W43DD2G', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Global rung presentation: fit/center the canvas in the viewport (currently top-left anchored at raw scale)', 'From the T-1183 eyeball captures (2026-07-25, .cache/screenshots/t1183-eyeball-run2/01-global.png): the Global rung canvas (GJ1c: 177x88 texels) draws top-left-anchored at its raw integer scale, occupying only the upper-left ~885x440 of a 1920x1080 viewport — the remaining ~60% of the screen is empty background, and the Atlas legend panel overlaps the canvas itself. The body-surface opener is the first thing a player sees in the Atlas; it should fit-scale (integer multiple preserving texel-exactness per D-255, or letterboxed non-integer if ruled acceptable) and center in the available viewport, with the legend laid out beside rather than over it. Pure client map-art presentation — no wire/server change; D-255(e) unaffected (same input set, different placement/scale).', 'done', 'medium', 'stig', 'client', NULL, '2026-07-25 10:17:34.934', '2026-07-25 11:29:11.346', NULL, '12bdd15c8649e6edb519a0a2d0edc64a', 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 ('06FSCXX7E3BXT7WG0BYDB6PJA0', 'bug', '06FBPPMZNNEV052DBYYY3A897C', 'Region-baseline latitude is pole-anchored while the derive core keys equator-anchored signed regions — northern hemisphere clamps to +90', 'Found during the PR #199 (T-1174/D-256) review fix round, PRE-EXISTING on the window path since the region climate stack (T-1078/T-1113) was wired into derive_at_metres. region_profile::region_centre_latitude_deg (region_profile.rs ~270-280) maps region row 0 to the north pole (+90) with lat_frac clamped to [0,1] — a pole-anchored, non-negative row convention. But the derive core (derive_at_metres_with_riparian, district_profile.rs ~1788) keys regions by floor-dividing EQUATOR-anchored world metres (negative wy = north), producing signed region rows (Earth-class: -49..+49). Consequence: every northern-hemisphere region has negative row -> region_centre_y_m negative -> lat_frac clamps to 0 -> baseline latitude +90 (polar) regardless of true latitude; southern-hemisphere rows 0..~48 read as compressed NORTHERN latitudes ~89..~0.7 (south pole reads as equator). Affects the region temperature baseline (and glaciation/moisture components derived from it) for the window path at every rung, and post-D-256 the batch/survey path inherits the same keys (still an improvement over the pre-D-256 body-wide region-(0,0) baseline, which read ~+89 everywhere). NOT touched in PR #199 to keep the review round scoped; goldens currently pin the wrong-latitude values. FIX SHAPE: make region_centre_latitude_deg mirror the derive core''s inverse mapping (equator-anchored signed rows: lat = -((ry+0.5)*REGION_M/meridian_m clamped to [-0.5,0.5])*180), audit the OTHER convention''s callers (the D-255 rung-0 canvas row space and the collapsed LayerRegionOutput builder use non-negative pole-anchored rows - decide ONE convention per D-256''s one-inverse-mapping principle, likely at the T-1181 rung-0 rebuild), regen affected goldens, and re-run the believability direction check. Related: D-256, D-243, T-1181, T-1078.
In-the-wild datapoint (2026-07-25, T-1184 eyeball session): GJ338Bd (Arbour, R=6711km) survey cell (42,29) — true position latitude -76.2S (wy=8,929,367m, region row 43) — derives with the pole-anchored baseline latitude +13.9N, producing an 18C temperate, glaciation-free lake district near the antarctic circle. Concrete demonstration of the southern-hemisphere-reads-as-northern-tropics half of the bug (the northern half clamps to +90). Conversion math used: scratchpad survey_to_district.py, the D-256 bridge formula.', 'review', 'high', 'dudley', 'server', NULL, '2026-07-24 23:30:51.889', '2026-07-25 11:38:28.087', NULL, 'c6944bb23736a91f8bd786e793f21b1e', 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 ('06FSG8P6C6WXEG3X81PVCQ833M', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Lake shorelines render as hard step-edges — no transition band unlike ocean coastlines', 'Found in the T-1182 eyeball (2026-07-25): at District and Quarter rung on GJ338Bd''s big lake (DistrictPos 13652,4360), the lake shoreline is a hard rectilinear step-edge while ocean coastlines in the SAME frame show a proper multi-tone refined transition band. Two hypotheses, likely the second: (a) the T-1184 continuous filled>elevation comparison isn''t refining lake edges positionally as designed (would violate the ticket''s own ''edges refine with rung like coastlines'' requirement — verify with a shoreline-crossing sample sweep at multiple rungs); (b) PRESENTATION: ocean transition tones come from ocean_fraction_q banding, but lake cells have ocean_fraction_q=0 and Lake is a binary morphology class — so even a perfectly-refined lake edge has no gradient vocabulary to draw with. If (b), the fix is a lake-margin tone source (e.g. a filled-surface-depth band sampled at derive time, or client-side shading from the served elevation field near Lake cells — must stay inside D-255(e)''s closed input set). Determine which, then fix accordingly. Not a T-1182 defect (viewer draws what the server sends). Captures: .cache/screenshots/t1182-eyeball/rung-ladder/. Related: T-1184, D-227 amendment (4), D-255(e).', 'review', 'medium', 'dudley', 'server', NULL, '2026-07-25 07:17:23.681', '2026-07-25 11:38:28.092', NULL, 'bcadc3c04a78b4922439c82bc6a2715f', 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 ('06FSCXX7E3BXT7WG0BYDB6PJA0', 'bug', '06FBPPMZNNEV052DBYYY3A897C', 'Region-baseline latitude is pole-anchored while the derive core keys equator-anchored signed regions — northern hemisphere clamps to +90', 'Found during the PR #199 (T-1174/D-256) review fix round, PRE-EXISTING on the window path since the region climate stack (T-1078/T-1113) was wired into derive_at_metres. region_profile::region_centre_latitude_deg (region_profile.rs ~270-280) maps region row 0 to the north pole (+90) with lat_frac clamped to [0,1] — a pole-anchored, non-negative row convention. But the derive core (derive_at_metres_with_riparian, district_profile.rs ~1788) keys regions by floor-dividing EQUATOR-anchored world metres (negative wy = north), producing signed region rows (Earth-class: -49..+49). Consequence: every northern-hemisphere region has negative row -> region_centre_y_m negative -> lat_frac clamps to 0 -> baseline latitude +90 (polar) regardless of true latitude; southern-hemisphere rows 0..~48 read as compressed NORTHERN latitudes ~89..~0.7 (south pole reads as equator). Affects the region temperature baseline (and glaciation/moisture components derived from it) for the window path at every rung, and post-D-256 the batch/survey path inherits the same keys (still an improvement over the pre-D-256 body-wide region-(0,0) baseline, which read ~+89 everywhere). NOT touched in PR #199 to keep the review round scoped; goldens currently pin the wrong-latitude values. FIX SHAPE: make region_centre_latitude_deg mirror the derive core''s inverse mapping (equator-anchored signed rows: lat = -((ry+0.5)*REGION_M/meridian_m clamped to [-0.5,0.5])*180), audit the OTHER convention''s callers (the D-255 rung-0 canvas row space and the collapsed LayerRegionOutput builder use non-negative pole-anchored rows - decide ONE convention per D-256''s one-inverse-mapping principle, likely at the T-1181 rung-0 rebuild), regen affected goldens, and re-run the believability direction check. Related: D-256, D-243, T-1181, T-1078.
In-the-wild datapoint (2026-07-25, T-1184 eyeball session): GJ338Bd (Arbour, R=6711km) survey cell (42,29) — true position latitude -76.2S (wy=8,929,367m, region row 43) — derives with the pole-anchored baseline latitude +13.9N, producing an 18C temperate, glaciation-free lake district near the antarctic circle. Concrete demonstration of the southern-hemisphere-reads-as-northern-tropics half of the bug (the northern half clamps to +90). Conversion math used: scratchpad survey_to_district.py, the D-256 bridge formula.', 'done', 'high', 'dudley', 'server', NULL, '2026-07-24 23:30:51.889', '2026-07-25 12:45:32.482', NULL, '3e639a87ce35024288534a352e9230cf', 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 ('06FSG8P6C6WXEG3X81PVCQ833M', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Lake shorelines render as hard step-edges — no transition band unlike ocean coastlines', 'Found in the T-1182 eyeball (2026-07-25): at District and Quarter rung on GJ338Bd''s big lake (DistrictPos 13652,4360), the lake shoreline is a hard rectilinear step-edge while ocean coastlines in the SAME frame show a proper multi-tone refined transition band. Two hypotheses, likely the second: (a) the T-1184 continuous filled>elevation comparison isn''t refining lake edges positionally as designed (would violate the ticket''s own ''edges refine with rung like coastlines'' requirement — verify with a shoreline-crossing sample sweep at multiple rungs); (b) PRESENTATION: ocean transition tones come from ocean_fraction_q banding, but lake cells have ocean_fraction_q=0 and Lake is a binary morphology class — so even a perfectly-refined lake edge has no gradient vocabulary to draw with. If (b), the fix is a lake-margin tone source (e.g. a filled-surface-depth band sampled at derive time, or client-side shading from the served elevation field near Lake cells — must stay inside D-255(e)''s closed input set). Determine which, then fix accordingly. Not a T-1182 defect (viewer draws what the server sends). Captures: .cache/screenshots/t1182-eyeball/rung-ladder/. Related: T-1184, D-227 amendment (4), D-255(e).', 'done', 'medium', 'dudley', 'server', NULL, '2026-07-25 07:17:23.681', '2026-07-25 12:45:32.487', NULL, 'b3f66fd935ce03512d448f63529b1b72', 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 ('06FRPEGNSG7VVR602MTT4VDCEW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Tiled orbital terminal-recovery — exhausted DERIVING state has no retry path', 'PR #193 review (Tyre, finding 2): after MAX_RETRIES(30)/~110s of genuine unbroken Pending in the tiled orbital rest state, the player is permanently stuck at DERIVING TERRAIN: pan is a no-op while tiled (_maybe_refloat_window early-returns), zoom-out is floored at the canonical frame (edge-triggered reset never re-fires), and regional_screen.enter() no-ops same-body re-entry — recovery only via zooming IN across the Region boundary or visiting a different body. Pathological trigger (110s of continuous Pending) and the release-server make-atlas change makes it near-impossible on the sanctioned path, but it is a real dead-end. Fix options per the review: a manual retry affordance on the DERIVING state, or same-body re-entry re-issues tile requests when has_pending_tiles() and every tile request is exhausted-non-pending. Single-window is already fully recoverable (any pan/zoom resets the retry state) — this is tile-mode only.
Refinement review (2026-07-25, post-D-255 batch): GAPS — do not implement as written. The root-cause description targets retired tiled-orbital machinery (atlas_window_tile_set.gd, has_pending_tiles()) that no longer exists; Region/Global under D-255(a) is a single flat per-rung canvas. HOWEVER the dead-end symptom plausibly survives in the new architecture: step_canvas_request.gd:47 MAX_RETRIES=30, step_canvas_viewer.gd:544 "DERIVING TERRAIN..." label, _maybe_refloat() (viewer:498) early-returns for Global, and regional_screen.enter() (screens/regional_screen.gd:38-43) no-ops on same-body re-entry guarded on body_id alone — the same-body-reentry trap the ticket cites. Re-scope: reproduce the exhausted-retry dead-end against StepCanvasRequest/StepCanvasViewer''s single-canvas retry model first, then fix what actually reproduces. Investigate-then-fix, not fix-as-described.', 'backlog', 'medium', NULL, 'client', 'D-226', '2026-07-22 19:07:47.532', '2026-07-25 12:50:50.726', NULL, '034c5d32daa7cd49e8a25b9f08b946d2', 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 ('06FRVMZYG7Z5YCKHCBS7XWJPM4', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Pole-adjacent District-rung entry can resolve to a Region window (canonicalization/reselect interplay)', 'Observed during T-1156 wave-1 acceptance drives (2026-07-23, lead): calling AtlasWindowViewer._enter_at_rung(body, system, district_center, 32, ''District'') at a pole-edge center (Lendel district (10466,-4784), the meridian edge) produced a REGION window in one run (header ''region window - 204.800 km/cell'', center canonicalized to (10461,-4773)) while an earlier run at the same nominal center rendered the requested district window (2.048 km/cell, center uncanonicalized (10466,-4784) exactly). Suspected interplay: pole-wall clamp / canonicalize_district_center vs the entry auto-fit and _maybe_reselect_rung''s coverage walk (a pole-clamped degenerate window''s computed extent may trip is_fully_zoomed_out or the coverage ceiling). Also on file: one drive process computed a sign-flipped latitude mapping (row 0 -> +wy) unreproducible afterward — suspected stale compiled-script cache serving a revert-verification''s temporarily-flipped state (godot-cold-parse wipes exactly this class of cache). Reproduce both at the pole edge, pin with tests. Extreme-edge case: normal navigation rarely enters District directly at a pole.
Refinement review (2026-07-25, post-D-255 batch): GAPS — entirely stale as written. Every cited function (AtlasWindowViewer._enter_at_rung, canonicalize_district_center, _maybe_reselect_rung, is_fully_zoomed_out) is retired with the T-1182 rework; regional_screen.gd''s own doc confirms the orbital-mosaic-vs-window split this bug lived in is gone (one StepCanvasViewer, one path, D-255(a)). Whether a pole-edge canonicalization defect exists in the unified single-path model is an open question with no current reproduction. Do not route to an implementer as-is: needs a fresh reproduction attempt against StepCanvasViewer/regional_screen.gd near a pole-adjacent District entry; close as moot if it does not reproduce, refile with new specifics if it does.', 'backlog', 'low', NULL, 'client', 'D-226', '2026-07-23 07:15:08.545', '2026-07-25 12:50:53.776', NULL, 'b3ce996c8fed9fdf188097b7b3adf525', 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 ('06FRV2EV5QK4BCZ1P9H85JH2YM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Natural-feature name labels on the ladder (wire attach_feature_names, populate atlas_feature_names, feature-names proxy)', 'T-1156 wave 1''s ''labels if cheap'' clause, split out after Si''s audit (2026-07-23): cheap but not free — three dormant pieces exist and nothing connects them. (1) layer1::attach_feature_names (layer1.rs:213-262) is unit-tested with zero production callers — wire it into run_layer1''s call path with name pools; (2) atlas_feature_names (systems-schema.sql:537-544) is created-but-never-populated — add the INSERT step at regen time from the D-223 names-only markers.json pools, mirroring populate_atlas_city_names (generator change: import_economics/atlas.py — watchlist/regen-db implications); (3) add a feature-names read proxy mirroring atlas_data_proxy.rs handle_city_names_request (D-236 pattern — its own message type, outside the AtlasLayerResponse ceilings) + client label draw on the ladder (the retired _draw_named_features at atlas_marker_overlay.gd:239-264 reads the pre-D-223 dict shape and would silently no-op — rewrite, don''t reactivate).', 'in_progress', 'low', NULL, 'server', 'D-226', '2026-07-23 05:54:09.837', '2026-07-25 12:51:01.110', NULL, 'ddc09c5ea7383174621d70a3ceed0a48', 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 ('06FRMPTY517G7ZBMPAPMMCCMK8', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Retire the legacy window_granularity u32 wire field', 'PR #192 review (Tyre): window_granularity_v2 (the WindowGranularity enum) fully shadows the legacy u32 field — the server always echoes both, and the only conceivable legacy readers are pre-T-1152 clients, which do not exist (single-repo client/server pair, no external clients). Retire the u32 field + the WINDOW_GRANULARITY_REGION_KEY sentinel machinery from AtlasLayerRequest/DistrictWindowLayer once confirmed nothing reads it: server resolve_window_granularity legacy path, client encode path, the sentinel echo, and the five-touch-point key components that still carry the legacy value alongside the enum. Scheduled in the D-226 amendment refinement-semantics note (PR #192). Do AFTER T-1153''s follow-ups settle — no urgency, the dual-echo is cheap; the win is contract simplicity.', 'in_progress', 'low', NULL, 'server', NULL, '2026-07-22 15:04:31.528', '2026-07-25 12:51:01.116', NULL, '273ae36f28363e1efa04d94809758cee', 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 ('06FRMPWFRCD3M8282XJ01YWGX0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Orbital coastline-position audit — derive_orbital_at_metres skips coast_warp_px', 'PR #192 review (Araminta, follow-up she rated real-not-hypothetical): derive_orbital_at_metres (district_profile.rs:1617-1692) samples the raw heightmap ocean mask via bilinear/bilinear_bool with NO coast_warp_px, while District/Quarter both apply coast_warp_px on the same mask (district_profile.rs:1139). The orbital rung therefore draws a STRUCTURALLY DIFFERENT coastline position than every finer rung — not a coarser sample of the same line — echoing the mandate''s own Lendel evidence clause (the D-166 amendment''s headline failure mode) at the orbital-to-district seam. Design doc §9 R1/R3 flagged the class as unresolved; §6 never gated T-1152/T-1153 on it, hence follow-up not blocker. Audit: quantify the divergence at real coasts (the wrap-warped line vs raw-bilinear line at region spacing), then either apply coast_warp_px at orbital sampling positions (cost: one warp eval per coastal cell — measure) or record an accepted-divergence rationale with Araminta''s sign-off. Related: Araminta also flagged the capture set never demonstrates Quarter-vs-District visual differentiation (flat plains read flat, sanctioned) — the audit''s verification captures should use a coastal/mountainous Quarter zoom to validate the finest rung at the same time.', 'in_progress', 'high', NULL, 'server', 'D-226', '2026-07-22 15:04:44.227', '2026-07-25 12:51:01.116', NULL, 'ed92510bbf567e13ebc7bbb0890e02e3', 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 ('06FRXKFEJMDEPQ0PZ5Q862DHKG', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas map-reading polish pass — river/nature presentation refinements (single large pass)', 'Jeroen, 2026-07-23, reviewing the T-1170 course captures: ''there are a bunch of graphical cleanups I would want to suggest around line thickness and tapering off to a point at the source to make the map read more fluent, but I feel map polish like that is better off as a single large pass later.'' This ticket is that pass''s collector — park presentation refinements here rather than folding them into feature batches. Seeded items: (1) river line thickness tuning across rungs (beyond the functional per-class defaults Araminta confirmed in T-1170); (2) SOURCE TAPERING — courses should taper to a point at their upstream source rather than starting at full class width (the classic cartographic river grammar; likely per-vertex width via Polygon2D strip or per-segment width ramp, since draw_polyline is single-width); (3) candidates declined-as-scope during T-1170 review: class-tinted color ramp (Araminta: width+opacity sufficient for now, hue is solvable-later). Accumulate further items via pql ticket append as they come up in captures/reviews. Scope discipline: correctness issues (ruled values not rendering, clip bugs) are NEVER parked here — they get fixed in their own batches; this pass is aesthetics only. Schedule: after the nature layer stands (post T-1156 wave 2 at the earliest), as one coherent Araminta-led pass.
REFERENCE (Jeroen, 2026-07-23): RimWorld''s world map as the fluency benchmark — ''the rimworld maps do roads, rivers, coasts and biomes quite well'' (hex substrate explicitly NOT us; the techniques transfer, the tiling doesn''t). Extracted techniques for this pass: (1) RIVERS AS NETWORKS — visible tributary-joins-trunk convergence, subtle width variation along the run, taper-to-source (his earlier item 2), thin/consistent/restrained overall; (2) ROADS VS RIVERS disambiguated by COLOR + STRAIGHTNESS (grey chord-like vs blue winding), not width — the wave-2 road-rendering rule; (3) COASTS as a shoreline GRADIENT — beach/transition band + subtle near-shore water shading (our coastal/transition morphology class already exists; presentation only); (4) BIOME/RELIEF TEXTURE — stipple/texture density carrying hills/mountains/forest on top of hue; strong fit for us since T-1162''s vegetation-patchiness and relief fields can DRIVE stipple density (texture as data, not decoration); (5) geographic LABELS do heavy fluency lifting — that half lives in T-1169, coordinate the passes. Screenshot reference in the session record 2026-07-23.
Reference screenshot saved in-repo: docs/design/references/rimworld-world-map-fluency.jpg (+ README note in that dir) — supersedes the ''session record'' pointer in the previous append.
RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): the per-vertex river tapering/width-grammar work lands on the NEW screen-space annotation layer (T-1182) via Polygon2D strips — draw_polyline cannot taper. The c1 styling-engine blocker is DISCHARGED: measurement ⑥ landed (Image.set_pixel 77.5 ns/cell flat) and the call resolved CPU-first; shaders deferred, not a precondition. This ticket now depends only on the annotation layer landing (blocked-by T-1182, edge added). Not cancelled — re-homed onto the new component, unblocked otherwise.', 'in_progress', 'low', NULL, 'client', 'D-226', '2026-07-23 11:48:08.469', '2026-07-25 12:51:01.117', NULL, '1aa8018dbbffdc989b1038fbffcb713b', 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 ('06FRV2EV5QK4BCZ1P9H85JH2YM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Natural-feature name labels on the ladder (wire attach_feature_names, populate atlas_feature_names, feature-names proxy)', 'T-1156 wave 1''s ''labels if cheap'' clause, split out after Si''s audit (2026-07-23): cheap but not free — three dormant pieces exist and nothing connects them. (1) layer1::attach_feature_names (layer1.rs:213-262) is unit-tested with zero production callers — wire it into run_layer1''s call path with name pools; (2) atlas_feature_names (systems-schema.sql:537-544) is created-but-never-populated — add the INSERT step at regen time from the D-223 names-only markers.json pools, mirroring populate_atlas_city_names (generator change: import_economics/atlas.py — watchlist/regen-db implications); (3) add a feature-names read proxy mirroring atlas_data_proxy.rs handle_city_names_request (D-236 pattern — its own message type, outside the AtlasLayerResponse ceilings) + client label draw on the ladder (the retired _draw_named_features at atlas_marker_overlay.gd:239-264 reads the pre-D-223 dict shape and would silently no-op — rewrite, don''t reactivate).', 'in_progress', 'low', 'dudley', 'server', 'D-226', '2026-07-23 05:54:09.837', '2026-07-25 12:51:02.096', NULL, 'caa5367c249a052b21eead897db4651d', 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 ('06FRMPWFRCD3M8282XJ01YWGX0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Orbital coastline-position audit — derive_orbital_at_metres skips coast_warp_px', 'PR #192 review (Araminta, follow-up she rated real-not-hypothetical): derive_orbital_at_metres (district_profile.rs:1617-1692) samples the raw heightmap ocean mask via bilinear/bilinear_bool with NO coast_warp_px, while District/Quarter both apply coast_warp_px on the same mask (district_profile.rs:1139). The orbital rung therefore draws a STRUCTURALLY DIFFERENT coastline position than every finer rung — not a coarser sample of the same line — echoing the mandate''s own Lendel evidence clause (the D-166 amendment''s headline failure mode) at the orbital-to-district seam. Design doc §9 R1/R3 flagged the class as unresolved; §6 never gated T-1152/T-1153 on it, hence follow-up not blocker. Audit: quantify the divergence at real coasts (the wrap-warped line vs raw-bilinear line at region spacing), then either apply coast_warp_px at orbital sampling positions (cost: one warp eval per coastal cell — measure) or record an accepted-divergence rationale with Araminta''s sign-off. Related: Araminta also flagged the capture set never demonstrates Quarter-vs-District visual differentiation (flat plains read flat, sanctioned) — the audit''s verification captures should use a coastal/mountainous Quarter zoom to validate the finest rung at the same time.', 'in_progress', 'high', 'dudley', 'server', 'D-226', '2026-07-22 15:04:44.227', '2026-07-25 12:51:02.107', NULL, '5c10397bf02286dabd819a3b32407816', 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 ('06FRMPTY517G7ZBMPAPMMCCMK8', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Retire the legacy window_granularity u32 wire field', 'PR #192 review (Tyre): window_granularity_v2 (the WindowGranularity enum) fully shadows the legacy u32 field — the server always echoes both, and the only conceivable legacy readers are pre-T-1152 clients, which do not exist (single-repo client/server pair, no external clients). Retire the u32 field + the WINDOW_GRANULARITY_REGION_KEY sentinel machinery from AtlasLayerRequest/DistrictWindowLayer once confirmed nothing reads it: server resolve_window_granularity legacy path, client encode path, the sentinel echo, and the five-touch-point key components that still carry the legacy value alongside the enum. Scheduled in the D-226 amendment refinement-semantics note (PR #192). Do AFTER T-1153''s follow-ups settle — no urgency, the dual-echo is cheap; the win is contract simplicity.', 'in_progress', 'low', 'dudley', 'server', NULL, '2026-07-22 15:04:31.528', '2026-07-25 12:51:02.109', NULL, '95bf72674f15dc0effa667f22c2f3413', 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 ('06FRXKFEJMDEPQ0PZ5Q862DHKG', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas map-reading polish pass — river/nature presentation refinements (single large pass)', 'Jeroen, 2026-07-23, reviewing the T-1170 course captures: ''there are a bunch of graphical cleanups I would want to suggest around line thickness and tapering off to a point at the source to make the map read more fluent, but I feel map polish like that is better off as a single large pass later.'' This ticket is that pass''s collector — park presentation refinements here rather than folding them into feature batches. Seeded items: (1) river line thickness tuning across rungs (beyond the functional per-class defaults Araminta confirmed in T-1170); (2) SOURCE TAPERING — courses should taper to a point at their upstream source rather than starting at full class width (the classic cartographic river grammar; likely per-vertex width via Polygon2D strip or per-segment width ramp, since draw_polyline is single-width); (3) candidates declined-as-scope during T-1170 review: class-tinted color ramp (Araminta: width+opacity sufficient for now, hue is solvable-later). Accumulate further items via pql ticket append as they come up in captures/reviews. Scope discipline: correctness issues (ruled values not rendering, clip bugs) are NEVER parked here — they get fixed in their own batches; this pass is aesthetics only. Schedule: after the nature layer stands (post T-1156 wave 2 at the earliest), as one coherent Araminta-led pass.
REFERENCE (Jeroen, 2026-07-23): RimWorld''s world map as the fluency benchmark — ''the rimworld maps do roads, rivers, coasts and biomes quite well'' (hex substrate explicitly NOT us; the techniques transfer, the tiling doesn''t). Extracted techniques for this pass: (1) RIVERS AS NETWORKS — visible tributary-joins-trunk convergence, subtle width variation along the run, taper-to-source (his earlier item 2), thin/consistent/restrained overall; (2) ROADS VS RIVERS disambiguated by COLOR + STRAIGHTNESS (grey chord-like vs blue winding), not width — the wave-2 road-rendering rule; (3) COASTS as a shoreline GRADIENT — beach/transition band + subtle near-shore water shading (our coastal/transition morphology class already exists; presentation only); (4) BIOME/RELIEF TEXTURE — stipple/texture density carrying hills/mountains/forest on top of hue; strong fit for us since T-1162''s vegetation-patchiness and relief fields can DRIVE stipple density (texture as data, not decoration); (5) geographic LABELS do heavy fluency lifting — that half lives in T-1169, coordinate the passes. Screenshot reference in the session record 2026-07-23.
Reference screenshot saved in-repo: docs/design/references/rimworld-world-map-fluency.jpg (+ README note in that dir) — supersedes the ''session record'' pointer in the previous append.
RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): the per-vertex river tapering/width-grammar work lands on the NEW screen-space annotation layer (T-1182) via Polygon2D strips — draw_polyline cannot taper. The c1 styling-engine blocker is DISCHARGED: measurement ⑥ landed (Image.set_pixel 77.5 ns/cell flat) and the call resolved CPU-first; shaders deferred, not a precondition. This ticket now depends only on the annotation layer landing (blocked-by T-1182, edge added). Not cancelled — re-homed onto the new component, unblocked otherwise.', 'in_progress', 'low', 'stig', 'client', 'D-226', '2026-07-23 11:48:08.469', '2026-07-25 12:51:02.515', NULL, 'ea0e9be1f27619ca19bfc4e4c46ae317', 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 ('06FSJWSX11WV3C1XXZEV88Q3P0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Biome/relief stipple-density layer on the terrain build (RimWorld technique 4)', 'Follow-up from T-1175''s assess-only item (2026-07-25, stig''s write-up): a stipple/texture-density layer carrying hills/mountains/forest on top of hue, driven by the already-wire-carried vegetation and elev_q L8 planes — presentation-only, D-255(e)-legal (texture-space dithering of already-derived per-cell values, deterministically seeded per cell coordinate + value so it is stable across cache hit/miss; never invents samples between server cells). Would live as a post-process in step_canvas_terrain_layer.gd::rebuild_from_canvas()''s Image.set_pixel build. Design questions to settle at pickup: (a) stipple dots inline in the existing per-cell loop (cheap, same O(wxh) pass) vs a second overlay pass (simpler code, doubles pixel-touch cost); (b) density from vegetation class directly vs a combination with elev_q — relief hachures and forest texture are two different visual grammars in the RimWorld reference, not one slider; (c) own legend toggle (TMP/MST/VEG overlay-bar pattern) vs always-on like the elevation lightness modifier. Reference: docs/design/references/rimworld-world-map-fluency.jpg. Related: T-1175, T-1162 (vegetation patchiness fields), D-255(e).', 'backlog', 'low', NULL, 'client', NULL, '2026-07-25 13:24:54.152', '2026-07-25 13:24:54.152', NULL, '97e85dfc6cf1fbf063b981f1876ac860', 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 ('06FSJXFJEBK5EW5WDGN6FTS4KG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Mountain-peak name labels — peak positions onto the step-canvas wire + client draw', 'Split from T-1169 (2026-07-25, dudley''s scope finding): river labels shipped end-to-end in T-1169 (names via the FeatureNamesRequest proxy, anchored at the course mouth ring positions already on the wire), and attach_feature_names is wired into the cascade with assignments stored server-side — but ALPINE PEAK positions are not on the canvas wire anywhere, so mountain labels cannot draw. This ticket: add peak positions to the StepCanvasResponse contract (likely shape: a sparse feature list mirroring courses/cliffs per D-255(c) — positions + name-pool keys, NOT a dense plane; decide which rungs carry it — probably Global/Region/District only, labels are noise deeper), thread server encode → client protocol decode → an additive label-draw anchor in step_canvas_annotation_layer.gd reusing T-1169''s label-draw function. Wire-shape change → project.yaml version bump for the disk-cache tag (D-192/T-1183 mechanism). The server-side data already exists after T-1169 (attach_feature_names assignments on the cascade output); this is transport + presentation only. Related: T-1169, D-255(c), D-236.', 'backlog', 'low', NULL, 'server', NULL, '2026-07-25 13:27:51.667', '2026-07-25 13:27:51.667', NULL, 'd49cb1026c2f99ecea326656dadad8c3', 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 ('06FRXKFEJMDEPQ0PZ5Q862DHKG', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas map-reading polish pass — river/nature presentation refinements (single large pass)', 'Jeroen, 2026-07-23, reviewing the T-1170 course captures: ''there are a bunch of graphical cleanups I would want to suggest around line thickness and tapering off to a point at the source to make the map read more fluent, but I feel map polish like that is better off as a single large pass later.'' This ticket is that pass''s collector — park presentation refinements here rather than folding them into feature batches. Seeded items: (1) river line thickness tuning across rungs (beyond the functional per-class defaults Araminta confirmed in T-1170); (2) SOURCE TAPERING — courses should taper to a point at their upstream source rather than starting at full class width (the classic cartographic river grammar; likely per-vertex width via Polygon2D strip or per-segment width ramp, since draw_polyline is single-width); (3) candidates declined-as-scope during T-1170 review: class-tinted color ramp (Araminta: width+opacity sufficient for now, hue is solvable-later). Accumulate further items via pql ticket append as they come up in captures/reviews. Scope discipline: correctness issues (ruled values not rendering, clip bugs) are NEVER parked here — they get fixed in their own batches; this pass is aesthetics only. Schedule: after the nature layer stands (post T-1156 wave 2 at the earliest), as one coherent Araminta-led pass.
REFERENCE (Jeroen, 2026-07-23): RimWorld''s world map as the fluency benchmark — ''the rimworld maps do roads, rivers, coasts and biomes quite well'' (hex substrate explicitly NOT us; the techniques transfer, the tiling doesn''t). Extracted techniques for this pass: (1) RIVERS AS NETWORKS — visible tributary-joins-trunk convergence, subtle width variation along the run, taper-to-source (his earlier item 2), thin/consistent/restrained overall; (2) ROADS VS RIVERS disambiguated by COLOR + STRAIGHTNESS (grey chord-like vs blue winding), not width — the wave-2 road-rendering rule; (3) COASTS as a shoreline GRADIENT — beach/transition band + subtle near-shore water shading (our coastal/transition morphology class already exists; presentation only); (4) BIOME/RELIEF TEXTURE — stipple/texture density carrying hills/mountains/forest on top of hue; strong fit for us since T-1162''s vegetation-patchiness and relief fields can DRIVE stipple density (texture as data, not decoration); (5) geographic LABELS do heavy fluency lifting — that half lives in T-1169, coordinate the passes. Screenshot reference in the session record 2026-07-23.
Reference screenshot saved in-repo: docs/design/references/rimworld-world-map-fluency.jpg (+ README note in that dir) — supersedes the ''session record'' pointer in the previous append.
RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): the per-vertex river tapering/width-grammar work lands on the NEW screen-space annotation layer (T-1182) via Polygon2D strips — draw_polyline cannot taper. The c1 styling-engine blocker is DISCHARGED: measurement ⑥ landed (Image.set_pixel 77.5 ns/cell flat) and the call resolved CPU-first; shaders deferred, not a precondition. This ticket now depends only on the annotation layer landing (blocked-by T-1182, edge added). Not cancelled — re-homed onto the new component, unblocked otherwise.', 'review', 'low', 'stig', 'client', 'D-226', '2026-07-23 11:48:08.469', '2026-07-25 13:28:36.332', NULL, '1abbef519d9d56c1ae18798ac7d349ca', 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 ('06FRV2EV5QK4BCZ1P9H85JH2YM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Natural-feature name labels on the ladder (wire attach_feature_names, populate atlas_feature_names, feature-names proxy)', 'T-1156 wave 1''s ''labels if cheap'' clause, split out after Si''s audit (2026-07-23): cheap but not free — three dormant pieces exist and nothing connects them. (1) layer1::attach_feature_names (layer1.rs:213-262) is unit-tested with zero production callers — wire it into run_layer1''s call path with name pools; (2) atlas_feature_names (systems-schema.sql:537-544) is created-but-never-populated — add the INSERT step at regen time from the D-223 names-only markers.json pools, mirroring populate_atlas_city_names (generator change: import_economics/atlas.py — watchlist/regen-db implications); (3) add a feature-names read proxy mirroring atlas_data_proxy.rs handle_city_names_request (D-236 pattern — its own message type, outside the AtlasLayerResponse ceilings) + client label draw on the ladder (the retired _draw_named_features at atlas_marker_overlay.gd:239-264 reads the pre-D-223 dict shape and would silently no-op — rewrite, don''t reactivate).
Scope adjudication (2026-07-25, PR round, lead ruling): the "client label draw" clause is RE-SCOPED OUT of this ticket into T-1195, for rivers as well as mountains. Implementation found the ticket''s cheap-anchor premise false: the FeatureNamesRequest pool proxy is position-free (correctly mirroring CityNamesRequest per this ticket''s own instruction), and the client''s course polylines (river_course::InventedCourse) are not correlated with layer1::GeographicAttractor positions by construction — so anchoring a pool name at a mouth ring has no matching basis without a wire-carried position, the identical dependency mountains have. Rather than two ad-hoc wire changes, the one design decision (name rides the course array vs a separate sparse feature-position list mirroring courses/cliffs) lands once in T-1195 for both feature types. This ticket''s delivered scope: atlas_feature_names populated at regen (17,891 rows), the FeatureNamesRequest/Response proxy end-to-end, attach_feature_names wired into the cascade with assignments stored on Layer1Output/BodyWorldState — the three dormant pieces connected.', 'in_progress', 'low', 'dudley', 'server', 'D-226', '2026-07-23 05:54:09.837', '2026-07-25 13:40:57.886', NULL, '74173e1f6032a438e5bc5b0129e60ff0', 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 ('06FSJXFJEBK5EW5WDGN6FTS4KG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Mountain-peak name labels — peak positions onto the step-canvas wire + client draw', 'Split from T-1169 (2026-07-25, dudley''s scope finding): river labels shipped end-to-end in T-1169 (names via the FeatureNamesRequest proxy, anchored at the course mouth ring positions already on the wire), and attach_feature_names is wired into the cascade with assignments stored server-side — but ALPINE PEAK positions are not on the canvas wire anywhere, so mountain labels cannot draw. This ticket: add peak positions to the StepCanvasResponse contract (likely shape: a sparse feature list mirroring courses/cliffs per D-255(c) — positions + name-pool keys, NOT a dense plane; decide which rungs carry it — probably Global/Region/District only, labels are noise deeper), thread server encode → client protocol decode → an additive label-draw anchor in step_canvas_annotation_layer.gd reusing T-1169''s label-draw function. Wire-shape change → project.yaml version bump for the disk-cache tag (D-192/T-1183 mechanism). The server-side data already exists after T-1169 (attach_feature_names assignments on the cascade output); this is transport + presentation only. Related: T-1169, D-255(c), D-236.
EXPANDED (2026-07-25, lead ruling on dudley''s second scope finding): this ticket now covers RIVER labels as well as mountain-peak labels — T-1169''s implementation proved rivers have the identical missing link (the names pool is position-free, and client course polylines aren''t correlated with the server-side named attractors by construction; anchoring at the mouth ring has no matching basis). The single design decision to make FIRST, once, for both: does the name ride ON the course entry in the existing courses array (river-natural, but leaves mountains needing a second mechanism), or does a separate sparse feature-position list (positions + name-pool keys, mirroring courses/cliffs per D-255(c)) carry BOTH feature types uniformly? Preliminary lead lean: the uniform sparse list — one mechanism, one wire addition, mountains and rivers together, and course geometry stays name-free (names are annotation data, not hydrology). Decide at pickup with tyre/araminta input if contested. Server-side data all exists post-T-1169 (assignments on Layer1Output/BodyWorldState.feature_names; pool + proxy shipped). Transport + client draw + project.yaml bump remain.', 'backlog', 'low', NULL, 'server', NULL, '2026-07-25 13:27:51.667', '2026-07-25 13:41:04.273', NULL, '9a84cb5be52956aaa82cf5786535bacf', 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 ('06FRJD6EKPBQE08NA8VHP692W8', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Redesign the atlas visual-capture goldens for the zoom ladder (visual_scenarios/visual_capture)', 'T-1153 broke the T-1120/T-1121 golden capture harness: client/tests/visual_scenarios.gd and visual_capture.gd call app._regional_screen._viewer.get_heightmap_texture()/show_body() — both crash against AtlasWindowViewer (regional_screen now wraps the ladder viewer; AtlasViewer is unreachable from nav). Confirmed NOT wired into tests/run-godot or the pre-push hook (no reference in either), so nothing blocks, but the 12 curated atlas_* goldens and the --atlas-matrix capture path are dead until redesigned for the ladder (capture at defined rungs/zooms instead of the fixed heightmap frame; the new canonical orbital frame is the natural replacement for the old whole-body shot). Also decide the fate of the retired AtlasViewer code these harnesses were the last live consumers of — likely deletable once T-1156 (overlay port) settles what carries forward.
Live round 4 addendum: client/tests/test_atlas_window_overlay_draw_smoke.gd (3 scenarios -> now 2 after removing a flaky harness-sanity check: single-window draw + tile-mosaic draw, both asserting real composited pixels via SubViewport render) is a standalone gdUnit file with its own headless-skip guard, added because this redesign hadn''t landed yet. When this ticket lands, migrate its 2 scenarios into the redesigned tests/visual.json harness (folding the standalone file away) for consistency with the rest of the real-rendering test suite — see docs/DEVOPS.md''s ''Real-rendering test exception'' note for the interim rationale.
RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): this becomes the capture harness for the STEPPED render architecture — goldens key on (body, step) canvases, not the retired continuous-zoom frames. The T-1152/T-1153 test suites (coverage-walk, continuous-zoom seams, compute_tile_grid mosaic) retire with their code in T-1182''s change; this re-scoped harness is their replacement and the eyeball-check substrate for every step canvas. More needed than ever — keep, don''t cancel.
PR #203 fix-round inventory (2026-07-25): the retirement is now TOTAL — AtlasViewer and its whole cluster (atlas_viewer, atlas_marker_overlay, atlas_descend_geometry, atlas_legend_panel, atlas_generation_proxy, atlas_generation_state) are deleted. visual_scenarios.gd (_setup_atlas_golden_shot, 6 of 12 atlas_GJ* z2_0/z4_0 non-fit scenarios) and visual_capture.gd (_run_atlas_matrix) now call StepCanvasViewer methods that do not exist: get_heightmap_texture(), set_view(zoom, offset), get_view_offset() — the fit-zoom shots early-return before hitting them. Ruled in PR #203: NO shim (would resurrect the retired continuous-zoom API D-255 kills); the redesign here keys captures on (body, rung) via StepCanvasViewer''s real surface (enter/_scroll_rung/rung state + the echoed extent). get_overlay_defs()/set_overlay_visible() duck-type fine and survive. Until this lands, those 6 goldens + atlas_matrix are knowingly dead (manual make targets only, gate-invisible — confirmed again).
Harness techniques inventory addition (from the PR #204 / T-1183 restart-persistence eyeball, 2026-07-25) — proven live-rendering eyeball patterns for shared-desktop sessions:
1. InputSwallower pattern: root-level Node (added first, low process_priority), _input()+_unhandled_input() both call get_viewport().set_input_as_handled() unconditionally — neutralizes real desktop input reaching a `-s` SceneTree driver''s real window (X11/XWayland delivers pointer events to unfocused windows under the cursor). Verify via a per-capture-window intercept counter: N>0 intercepted AND zero resulting view drift is proof; 0 intercepted proves nothing.
2. In-process navigation via viewer methods, not OS input: call the same methods the app''s own gdUnit suite exercises (StepCanvasViewer.enter()/_scroll_rung(); RegionalScreen.enter() atlas-matrix precedent in visual_capture.gd) — identical production code path, no OS injection needed (Wayland/KWin blocks synthetic X11 injection for XWayland clients entirely).
3. View-transform logging at every capture: world_center, view_offset, the drawing Node2D''s canvas position, footprint_px alongside every screenshot — turns "pixels don''t match" into a root-causable mechanism (PR #204: drift matched PAN_SPEED_PX_S*frame_time*held-frames to 0.02px, identifying unfocused edge-scroll bleed, now T-1191).
4. Full-run-restart-on-anomaly, never partial retry: any mid-sequence assertion failure discards the whole run (cold-wipe for a cold-role attempt; fresh-process-only for a revisit-role attempt) — partial retries silently corrupt the state the comparison depends on.
5. Fixed-center revisit mode: record a prior run''s actual derived centers and re-request them literally on the comparison run, bypassing cursor-anchored re-derivation — required when run 2 must land on run 1''s exact cache keys, not merely repeat the gesture.
Harness-discipline gap found 2026-07-25 (Jeroen spotted a live orphan): agent-side kill-on-completion does not cover an agent CRASHING mid-capture (API error killed a session between server spawn and its own cleanup step — the server ran on unnoticed for ~25 min on the shared machine). Inventory addition: (6) the LEAD runs an explicit orphan sweep (ps for godot4 + settled-reach-server) at every cycle checkpoint — commit, merge, and after any agent dies abnormally — not only inside agent protocols; pr-process step 1a''s >5-min gdUnit grep does not match live-capture servers, so the sweep needs the server binary name too.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-22 09:42:47.197', '2026-07-25 14:04:02.818', NULL, '9e2fc07e90e0969bb22d9fea2bc6caa9', 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 ('06FRXKFEJMDEPQ0PZ5Q862DHKG', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas map-reading polish pass — river/nature presentation refinements (single large pass)', 'Jeroen, 2026-07-23, reviewing the T-1170 course captures: ''there are a bunch of graphical cleanups I would want to suggest around line thickness and tapering off to a point at the source to make the map read more fluent, but I feel map polish like that is better off as a single large pass later.'' This ticket is that pass''s collector — park presentation refinements here rather than folding them into feature batches. Seeded items: (1) river line thickness tuning across rungs (beyond the functional per-class defaults Araminta confirmed in T-1170); (2) SOURCE TAPERING — courses should taper to a point at their upstream source rather than starting at full class width (the classic cartographic river grammar; likely per-vertex width via Polygon2D strip or per-segment width ramp, since draw_polyline is single-width); (3) candidates declined-as-scope during T-1170 review: class-tinted color ramp (Araminta: width+opacity sufficient for now, hue is solvable-later). Accumulate further items via pql ticket append as they come up in captures/reviews. Scope discipline: correctness issues (ruled values not rendering, clip bugs) are NEVER parked here — they get fixed in their own batches; this pass is aesthetics only. Schedule: after the nature layer stands (post T-1156 wave 2 at the earliest), as one coherent Araminta-led pass.
REFERENCE (Jeroen, 2026-07-23): RimWorld''s world map as the fluency benchmark — ''the rimworld maps do roads, rivers, coasts and biomes quite well'' (hex substrate explicitly NOT us; the techniques transfer, the tiling doesn''t). Extracted techniques for this pass: (1) RIVERS AS NETWORKS — visible tributary-joins-trunk convergence, subtle width variation along the run, taper-to-source (his earlier item 2), thin/consistent/restrained overall; (2) ROADS VS RIVERS disambiguated by COLOR + STRAIGHTNESS (grey chord-like vs blue winding), not width — the wave-2 road-rendering rule; (3) COASTS as a shoreline GRADIENT — beach/transition band + subtle near-shore water shading (our coastal/transition morphology class already exists; presentation only); (4) BIOME/RELIEF TEXTURE — stipple/texture density carrying hills/mountains/forest on top of hue; strong fit for us since T-1162''s vegetation-patchiness and relief fields can DRIVE stipple density (texture as data, not decoration); (5) geographic LABELS do heavy fluency lifting — that half lives in T-1169, coordinate the passes. Screenshot reference in the session record 2026-07-23.
Reference screenshot saved in-repo: docs/design/references/rimworld-world-map-fluency.jpg (+ README note in that dir) — supersedes the ''session record'' pointer in the previous append.
RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): the per-vertex river tapering/width-grammar work lands on the NEW screen-space annotation layer (T-1182) via Polygon2D strips — draw_polyline cannot taper. The c1 styling-engine blocker is DISCHARGED: measurement ⑥ landed (Image.set_pixel 77.5 ns/cell flat) and the call resolved CPU-first; shaders deferred, not a precondition. This ticket now depends only on the annotation layer landing (blocked-by T-1182, edge added). Not cancelled — re-homed onto the new component, unblocked otherwise.', 'done', 'low', 'stig', 'client', 'D-226', '2026-07-23 11:48:08.469', '2026-07-25 14:43:55.732', NULL, '2a49ead618d1cf2b3c839b5af57de781', 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 ('06FRMPWFRCD3M8282XJ01YWGX0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Orbital coastline-position audit — derive_orbital_at_metres skips coast_warp_px', 'PR #192 review (Araminta, follow-up she rated real-not-hypothetical): derive_orbital_at_metres (district_profile.rs:1617-1692) samples the raw heightmap ocean mask via bilinear/bilinear_bool with NO coast_warp_px, while District/Quarter both apply coast_warp_px on the same mask (district_profile.rs:1139). The orbital rung therefore draws a STRUCTURALLY DIFFERENT coastline position than every finer rung — not a coarser sample of the same line — echoing the mandate''s own Lendel evidence clause (the D-166 amendment''s headline failure mode) at the orbital-to-district seam. Design doc §9 R1/R3 flagged the class as unresolved; §6 never gated T-1152/T-1153 on it, hence follow-up not blocker. Audit: quantify the divergence at real coasts (the wrap-warped line vs raw-bilinear line at region spacing), then either apply coast_warp_px at orbital sampling positions (cost: one warp eval per coastal cell — measure) or record an accepted-divergence rationale with Araminta''s sign-off. Related: Araminta also flagged the capture set never demonstrates Quarter-vs-District visual differentiation (flat plains read flat, sanctioned) — the audit''s verification captures should use a coastal/mountainous Quarter zoom to validate the finest rung at the same time.', 'review', 'high', 'dudley', 'server', 'D-226', '2026-07-22 15:04:44.227', '2026-07-25 14:59:12.751', NULL, 'c01d217704c926147dd7429abe0031bf', 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 ('06FRV2EV5QK4BCZ1P9H85JH2YM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Natural-feature name labels on the ladder (wire attach_feature_names, populate atlas_feature_names, feature-names proxy)', 'T-1156 wave 1''s ''labels if cheap'' clause, split out after Si''s audit (2026-07-23): cheap but not free — three dormant pieces exist and nothing connects them. (1) layer1::attach_feature_names (layer1.rs:213-262) is unit-tested with zero production callers — wire it into run_layer1''s call path with name pools; (2) atlas_feature_names (systems-schema.sql:537-544) is created-but-never-populated — add the INSERT step at regen time from the D-223 names-only markers.json pools, mirroring populate_atlas_city_names (generator change: import_economics/atlas.py — watchlist/regen-db implications); (3) add a feature-names read proxy mirroring atlas_data_proxy.rs handle_city_names_request (D-236 pattern — its own message type, outside the AtlasLayerResponse ceilings) + client label draw on the ladder (the retired _draw_named_features at atlas_marker_overlay.gd:239-264 reads the pre-D-223 dict shape and would silently no-op — rewrite, don''t reactivate).
Scope adjudication (2026-07-25, PR round, lead ruling): the "client label draw" clause is RE-SCOPED OUT of this ticket into T-1195, for rivers as well as mountains. Implementation found the ticket''s cheap-anchor premise false: the FeatureNamesRequest pool proxy is position-free (correctly mirroring CityNamesRequest per this ticket''s own instruction), and the client''s course polylines (river_course::InventedCourse) are not correlated with layer1::GeographicAttractor positions by construction — so anchoring a pool name at a mouth ring has no matching basis without a wire-carried position, the identical dependency mountains have. Rather than two ad-hoc wire changes, the one design decision (name rides the course array vs a separate sparse feature-position list mirroring courses/cliffs) lands once in T-1195 for both feature types. This ticket''s delivered scope: atlas_feature_names populated at regen (17,891 rows), the FeatureNamesRequest/Response proxy end-to-end, attach_feature_names wired into the cascade with assignments stored on Layer1Output/BodyWorldState — the three dormant pieces connected.', 'review', 'low', 'dudley', 'server', 'D-226', '2026-07-23 05:54:09.837', '2026-07-25 14:59:12.758', NULL, 'd47d4abfd3e49b670904fde30490e75a', 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 ('06FRMPTY517G7ZBMPAPMMCCMK8', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Retire the legacy window_granularity u32 wire field', 'PR #192 review (Tyre): window_granularity_v2 (the WindowGranularity enum) fully shadows the legacy u32 field — the server always echoes both, and the only conceivable legacy readers are pre-T-1152 clients, which do not exist (single-repo client/server pair, no external clients). Retire the u32 field + the WINDOW_GRANULARITY_REGION_KEY sentinel machinery from AtlasLayerRequest/DistrictWindowLayer once confirmed nothing reads it: server resolve_window_granularity legacy path, client encode path, the sentinel echo, and the five-touch-point key components that still carry the legacy value alongside the enum. Scheduled in the D-226 amendment refinement-semantics note (PR #192). Do AFTER T-1153''s follow-ups settle — no urgency, the dual-echo is cheap; the win is contract simplicity.', 'review', 'low', 'dudley', 'server', NULL, '2026-07-22 15:04:31.528', '2026-07-25 14:59:12.759', NULL, 'f4527851910bdab46dcee5569d9a06c1', 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 ('06FSM3MQ5GFSPKKFJF6GGZN8CM', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Alpine relief capture — Quarter-vs-District differentiation on a tectonics:high body', 'Araminta''s PR #192 companion note, kept open by her PR #208 ruling (option b): the Quarter-vs-District relief-differentiation check was accepted for the T-1160 merge on a plains/river substitution (GJ380c world center -17723392,-10532864 — Quarter clearly shows finer higher-frequency texture than District), but plains texture and jagged tectonic relief are different visual regimes and the sharper case still needs its own capture. Task: identify the body pool with tectonics:high (or planet_class values mapping to TectonicClass::Volcanic/Active) and RECORD the pool in this ticket first — the PR #208 eyeball proved GJ380c/GJ820Bc are both tectonics:low via exhaustive two-Global color scans, don''t re-run that search blind — then capture quarter-relief vs district-relief at a true Alpine/MountainPass crop (MorphologyZone 13/14) at the same world center, matched crop, and have Araminta judge the differentiation. Reuse the pr208_driver fixed-center --step protocol (T-1157 harness inventory). Related: T-1160, PR #192, PR #208.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-25 16:14:35.308', '2026-07-25 16:14:35.308', NULL, '97da3e5224078f0768cc303f876239a9', 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 ('06FRMPWFRCD3M8282XJ01YWGX0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Orbital coastline-position audit — derive_orbital_at_metres skips coast_warp_px', 'PR #192 review (Araminta, follow-up she rated real-not-hypothetical): derive_orbital_at_metres (district_profile.rs:1617-1692) samples the raw heightmap ocean mask via bilinear/bilinear_bool with NO coast_warp_px, while District/Quarter both apply coast_warp_px on the same mask (district_profile.rs:1139). The orbital rung therefore draws a STRUCTURALLY DIFFERENT coastline position than every finer rung — not a coarser sample of the same line — echoing the mandate''s own Lendel evidence clause (the D-166 amendment''s headline failure mode) at the orbital-to-district seam. Design doc §9 R1/R3 flagged the class as unresolved; §6 never gated T-1152/T-1153 on it, hence follow-up not blocker. Audit: quantify the divergence at real coasts (the wrap-warped line vs raw-bilinear line at region spacing), then either apply coast_warp_px at orbital sampling positions (cost: one warp eval per coastal cell — measure) or record an accepted-divergence rationale with Araminta''s sign-off. Related: Araminta also flagged the capture set never demonstrates Quarter-vs-District visual differentiation (flat plains read flat, sanctioned) — the audit''s verification captures should use a coastal/mountainous Quarter zoom to validate the finest rung at the same time.', 'done', 'high', 'dudley', 'server', 'D-226', '2026-07-22 15:04:44.227', '2026-07-25 16:20:22.121', NULL, '996fa3cc0107c5584ef194b46d84a30b', 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 ('06FRV2EV5QK4BCZ1P9H85JH2YM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Natural-feature name labels on the ladder (wire attach_feature_names, populate atlas_feature_names, feature-names proxy)', 'T-1156 wave 1''s ''labels if cheap'' clause, split out after Si''s audit (2026-07-23): cheap but not free — three dormant pieces exist and nothing connects them. (1) layer1::attach_feature_names (layer1.rs:213-262) is unit-tested with zero production callers — wire it into run_layer1''s call path with name pools; (2) atlas_feature_names (systems-schema.sql:537-544) is created-but-never-populated — add the INSERT step at regen time from the D-223 names-only markers.json pools, mirroring populate_atlas_city_names (generator change: import_economics/atlas.py — watchlist/regen-db implications); (3) add a feature-names read proxy mirroring atlas_data_proxy.rs handle_city_names_request (D-236 pattern — its own message type, outside the AtlasLayerResponse ceilings) + client label draw on the ladder (the retired _draw_named_features at atlas_marker_overlay.gd:239-264 reads the pre-D-223 dict shape and would silently no-op — rewrite, don''t reactivate).
Scope adjudication (2026-07-25, PR round, lead ruling): the "client label draw" clause is RE-SCOPED OUT of this ticket into T-1195, for rivers as well as mountains. Implementation found the ticket''s cheap-anchor premise false: the FeatureNamesRequest pool proxy is position-free (correctly mirroring CityNamesRequest per this ticket''s own instruction), and the client''s course polylines (river_course::InventedCourse) are not correlated with layer1::GeographicAttractor positions by construction — so anchoring a pool name at a mouth ring has no matching basis without a wire-carried position, the identical dependency mountains have. Rather than two ad-hoc wire changes, the one design decision (name rides the course array vs a separate sparse feature-position list mirroring courses/cliffs) lands once in T-1195 for both feature types. This ticket''s delivered scope: atlas_feature_names populated at regen (17,891 rows), the FeatureNamesRequest/Response proxy end-to-end, attach_feature_names wired into the cascade with assignments stored on Layer1Output/BodyWorldState — the three dormant pieces connected.', 'done', 'low', 'dudley', 'server', 'D-226', '2026-07-23 05:54:09.837', '2026-07-25 16:20:22.129', NULL, '9c327527100e094d1a6e34180ffec6e6', 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 ('06FRMPTY517G7ZBMPAPMMCCMK8', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Retire the legacy window_granularity u32 wire field', 'PR #192 review (Tyre): window_granularity_v2 (the WindowGranularity enum) fully shadows the legacy u32 field — the server always echoes both, and the only conceivable legacy readers are pre-T-1152 clients, which do not exist (single-repo client/server pair, no external clients). Retire the u32 field + the WINDOW_GRANULARITY_REGION_KEY sentinel machinery from AtlasLayerRequest/DistrictWindowLayer once confirmed nothing reads it: server resolve_window_granularity legacy path, client encode path, the sentinel echo, and the five-touch-point key components that still carry the legacy value alongside the enum. Scheduled in the D-226 amendment refinement-semantics note (PR #192). Do AFTER T-1153''s follow-ups settle — no urgency, the dual-echo is cheap; the win is contract simplicity.', 'done', 'low', 'dudley', 'server', NULL, '2026-07-22 15:04:31.528', '2026-07-25 16:20:22.131', NULL, 'f6bfff77f8268ad8002aad9f799493f0', 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 ('06FB0TNSRZ9Q7NHVDRT6ARMCWW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'D-210 SubBiomeVariant implementation', 'D-210 defines SubBiomeVariant for fine-grained biome classification within SettingType::Wilderness districts. Currently deferred — SettingType::Wilderness carries a plain String biome field. Implement SubBiomeVariant enum and wire it into the atlas skeleton generator when the biome taxonomy is finalized.
Refinement review (2026-07-25, Si): OBSOLETE — D-210''s design was fully implemented and merged in PR #953/#963 (commit d418eba8b, 2026-06-03), a month after this ticket was filed, and the ticket was never closed out. Evidence: server/src/atlas/subbiome.rs (classify/classify_variant/base_cost, all 11 variants + terrain_modification_cost from the four D-210 signals, unit-tested); SubBiomeVariant enum + GeographicAttractor.sub_biome (generator.rs:539/:567); wired into the Layer-1 orchestrator (layer1.rs:209-215, Alpine branch tested); consumed downstream (attractor_matching.rs, cascade.rs); client overlay live (atlas_overlay_colors.gd:203 sub_biome_color). The only literal residue — SettingType::Wilderness carrying the cosmetic `Biome = String` alias — is unrelated to D-210''s classification/cost system; tighten it via a separate small ticket if ever wanted. Closing as delivered-by-#953.', 'backlog', 'low', NULL, 'server', NULL, '2026-05-03 10:30:14', '2026-07-25 16:25:46.604', NULL, 'b7a8c5833ac90ea380b52b32d48580b1', 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 ('06FB0TNSRZ9Q7NHVDRT6ARMCWW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'D-210 SubBiomeVariant implementation', 'D-210 defines SubBiomeVariant for fine-grained biome classification within SettingType::Wilderness districts. Currently deferred — SettingType::Wilderness carries a plain String biome field. Implement SubBiomeVariant enum and wire it into the atlas skeleton generator when the biome taxonomy is finalized.
Refinement review (2026-07-25, Si): OBSOLETE — D-210''s design was fully implemented and merged in PR #953/#963 (commit d418eba8b, 2026-06-03), a month after this ticket was filed, and the ticket was never closed out. Evidence: server/src/atlas/subbiome.rs (classify/classify_variant/base_cost, all 11 variants + terrain_modification_cost from the four D-210 signals, unit-tested); SubBiomeVariant enum + GeographicAttractor.sub_biome (generator.rs:539/:567); wired into the Layer-1 orchestrator (layer1.rs:209-215, Alpine branch tested); consumed downstream (attractor_matching.rs, cascade.rs); client overlay live (atlas_overlay_colors.gd:203 sub_biome_color). The only literal residue — SettingType::Wilderness carrying the cosmetic `Biome = String` alias — is unrelated to D-210''s classification/cost system; tighten it via a separate small ticket if ever wanted. Closing as delivered-by-#953.', 'done', 'low', NULL, 'server', NULL, '2026-05-03 10:30:14', '2026-07-25 16:25:46.760', NULL, 'e6aa010c5e0067704611e73147fbffed', 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 ('06FB0TNSRZZ5XKET93HPHKQHKW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Phase-4 test hardening — deferred review gaps (#953/#963 review)', 'Follow-ups from the Hoshe/Tyre pre-bake review (the BLOCKER/MAJOR items were fixed inline; these are the deferred MINOR/coverage items):
TESTS:
- features.rs: full per-type attractor reachability fixtures — craft heightmaps that guarantee LakeShore (enclosed depression), PassEntrance (saddle), PlainCenter (flat), RiverCrossing (confluence). Currently only RiverMouth/CoastalAccess/ValleyFloor are exercised on the slope/sine fixtures.
- heightmap.rs: 8-bit grayscale decode path; sea_level passthrough assertion; downsample identity/upsample early-return.
- drainage.rs: basin area_pct bit-for-bit determinism (only river_cells + basin count checked today).
- layer1.rs: attach_feature_names mountain branch with a non-empty mountain pool + ≥1 Alpine attractor.
- features.rs: thin_by_spacing behavior (spacing collisions, equirectangular column wrap).
- import_economics.py: automated idempotency test — two runs on a scratch DB yield identical atlas_city_names count (not doubled), and 0 rows for system ''GJ 0'' (Sol exemption). Wire alongside check-systems-db / pre-push.
- Wire test_sim_determinism.py into pre-push or a make target.
MINOR CODE NITS:
- drainage.rs ~480-488: comment the intentional isolated-basin fallback divergence from the old merge-to-basin-0 behavior (Tyre N1).
- planet_simulation.py ~787-788: oasis_water binary_dilation iterations are fixed-pixel — scale by GRID_W/512 for biome-display consistency at 1024 (cosmetic, Atlas only, not cascade; Tyre N2).
D-256/T-1174 finding (2026-07-25): the believability harness golden is INSENSITIVE to a total basin_direction regression — an all-North basin collapse (every survey cell defaulting) produced a byte-identical believability.json at current sample density (64/2048 districts; basin only reaches voxel meander phase/channel width, too localized for the coarse aggregate stats). The cross-namespace keying seam is now covered by the restored derive_all_districts_threads_supplied_basin_directions test (production-shaped survey keys), but the harness itself has no basin-sensitive metric. Candidate hardening: a basin-direction distribution stat (distinct directions >= 2 on a drained body) or a voxel-transect metric that moves with meander phase.
Refinement trim (2026-07-25, Si — every listed gap re-verified against current code before this batch): (1) DROP the test_sim_determinism.py wiring item — already done, Makefile:256 wires it into make test-tooling which the push gate runs on tooling changes. (2) The drainage.rs isolated-basin comment nit moved to ~line 654 (file grew; the old ~480-488 cite is stale) — still open, just relocate. (3) The layer1.rs attach_feature_names mountain-branch item: implicit coverage may already exist via run_layer1_* tests — check before adding a redundant test. (4) The D-256/T-1174 addendum''s "basin-direction distribution stat" candidate hardening is a genuine open design question (which stat, what threshold) — PROPOSE the stat to the lead before coding it, don''t invent silently. All other items (features.rs per-type reachability fixtures + thin_by_spacing, heightmap.rs 8-bit/sea-level-passthrough/upsample-identity, drainage.rs area_pct bit-determinism, import_economics.py idempotency test, oasis_water binary_dilation nit at planet_simulation.py:784-788) verified still open as written.', 'backlog', 'medium', NULL, 'server', NULL, '2026-05-23 06:09:45', '2026-07-25 16:25:56.990', NULL, 'e89f228c33aefc802abd1b95dabe078e', 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 ('06FB0TNSRWTP633SM8XB302MBM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas agent control channel — observe/act named-intent navigation (D-226)', 'D-226 layer 4 (apex). A client-side AtlasAgentInterface exposing a JSON control channel so an automated agent (terminal/curl, headless Godot) can navigate the REAL Atlas UI: observe -> JSON of (a) current data state shown and (b) a walkable UI affordance tree (toggle controls: id/label/state/locked); act -> named SEMANTIC intents (select_system/open_system/select_body/open_regional/select_city/set_overlay/reset_view/back/close_atlas) backed by the same handlers a click calls (the map uses _gui_input, NOT pixel-clicks), plus get_layer_data_summary (attractor/river/basin counts as JSON without rendering). Single AtlasAgentInterface autoload/static class for a stable contract; generic Control-tree walk so economics/saves plug in later. Turns human-eyeball review into an agent QA sweep. Depends on #960 (the viewer) + the auto-pause substrate. Screenshot capture is a separate ticket (kept off the critical path).
Re-scope (2026-07-25, Si refinement + lead ruling): both cited blockers are satisfied (T-960, T-970 done) and the D-255(d) deep-rung cache cap is already built defensively for this exact consumer (step_canvas_disk_cache.gd:61 names the AtlasAgentInterface QA sweep). But D-226''s named-intent list was designed against the retired pre-D-255 continuous-zoom Atlas — the ticket''s intent surface is STALE. Implementer instruction: derive the actual intent list from the CURRENT stepped-rung API before building the wrapper, and post the derived list to the team lead for confirmation before implementation. Known mapping start (verify, don''t trust): select_system → atlas_app._on_system_selected(system_id); select_body → _on_body_selected(body); reset_view → StepCanvasViewer._reset_to_global(); set_overlay → set_overlay_visible(); back → _on_regional_back(); zoom step → _scroll_rung(direction, cursor_local); open_regional/select_city/open_system need tracing through screens/regional_screen.gd, district_screen.gd, system_screen.gd. SEQUENCING: T-1157 (stepped capture-harness redesign) needs the same walk-the-Atlas-headlessly primitive — its harness-techniques inventory (appended 2026-07-25) documents five proven eyeball-driver patterns (InputSwallower, in-process viewer-method navigation, fixed-center revisit, view-transform logging) that are the PROTOTYPE for this channel; build the T-971 intent layer as the productionization of that pattern so T-1157''s harness can consume it rather than the two designing overlapping scaffolding independently.', 'backlog', 'medium', NULL, 'client', 'D-226', '2026-05-24 09:22:38', '2026-07-25 16:26:05.492', NULL, 'ebc75290cb615984581a87c04a0d88b5', 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 ('06FB0TNSRZZ5XKET93HPHKQHKW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Phase-4 test hardening — deferred review gaps (#953/#963 review)', 'Follow-ups from the Hoshe/Tyre pre-bake review (the BLOCKER/MAJOR items were fixed inline; these are the deferred MINOR/coverage items):
TESTS:
- features.rs: full per-type attractor reachability fixtures — craft heightmaps that guarantee LakeShore (enclosed depression), PassEntrance (saddle), PlainCenter (flat), RiverCrossing (confluence). Currently only RiverMouth/CoastalAccess/ValleyFloor are exercised on the slope/sine fixtures.
- heightmap.rs: 8-bit grayscale decode path; sea_level passthrough assertion; downsample identity/upsample early-return.
- drainage.rs: basin area_pct bit-for-bit determinism (only river_cells + basin count checked today).
- layer1.rs: attach_feature_names mountain branch with a non-empty mountain pool + ≥1 Alpine attractor.
- features.rs: thin_by_spacing behavior (spacing collisions, equirectangular column wrap).
- import_economics.py: automated idempotency test — two runs on a scratch DB yield identical atlas_city_names count (not doubled), and 0 rows for system ''GJ 0'' (Sol exemption). Wire alongside check-systems-db / pre-push.
- Wire test_sim_determinism.py into pre-push or a make target.
MINOR CODE NITS:
- drainage.rs ~480-488: comment the intentional isolated-basin fallback divergence from the old merge-to-basin-0 behavior (Tyre N1).
- planet_simulation.py ~787-788: oasis_water binary_dilation iterations are fixed-pixel — scale by GRID_W/512 for biome-display consistency at 1024 (cosmetic, Atlas only, not cascade; Tyre N2).
D-256/T-1174 finding (2026-07-25): the believability harness golden is INSENSITIVE to a total basin_direction regression — an all-North basin collapse (every survey cell defaulting) produced a byte-identical believability.json at current sample density (64/2048 districts; basin only reaches voxel meander phase/channel width, too localized for the coarse aggregate stats). The cross-namespace keying seam is now covered by the restored derive_all_districts_threads_supplied_basin_directions test (production-shaped survey keys), but the harness itself has no basin-sensitive metric. Candidate hardening: a basin-direction distribution stat (distinct directions >= 2 on a drained body) or a voxel-transect metric that moves with meander phase.
Refinement trim (2026-07-25, Si — every listed gap re-verified against current code before this batch): (1) DROP the test_sim_determinism.py wiring item — already done, Makefile:256 wires it into make test-tooling which the push gate runs on tooling changes. (2) The drainage.rs isolated-basin comment nit moved to ~line 654 (file grew; the old ~480-488 cite is stale) — still open, just relocate. (3) The layer1.rs attach_feature_names mountain-branch item: implicit coverage may already exist via run_layer1_* tests — check before adding a redundant test. (4) The D-256/T-1174 addendum''s "basin-direction distribution stat" candidate hardening is a genuine open design question (which stat, what threshold) — PROPOSE the stat to the lead before coding it, don''t invent silently. All other items (features.rs per-type reachability fixtures + thin_by_spacing, heightmap.rs 8-bit/sea-level-passthrough/upsample-identity, drainage.rs area_pct bit-determinism, import_economics.py idempotency test, oasis_water binary_dilation nit at planet_simulation.py:784-788) verified still open as written.', 'in_progress', 'medium', NULL, 'server', NULL, '2026-05-23 06:09:45', '2026-07-25 16:26:10.181', NULL, '1330970c9d7ec160af9d9b6f116152a4', 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 ('06FB0TNSRWTP633SM8XB302MBM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas agent control channel — observe/act named-intent navigation (D-226)', 'D-226 layer 4 (apex). A client-side AtlasAgentInterface exposing a JSON control channel so an automated agent (terminal/curl, headless Godot) can navigate the REAL Atlas UI: observe -> JSON of (a) current data state shown and (b) a walkable UI affordance tree (toggle controls: id/label/state/locked); act -> named SEMANTIC intents (select_system/open_system/select_body/open_regional/select_city/set_overlay/reset_view/back/close_atlas) backed by the same handlers a click calls (the map uses _gui_input, NOT pixel-clicks), plus get_layer_data_summary (attractor/river/basin counts as JSON without rendering). Single AtlasAgentInterface autoload/static class for a stable contract; generic Control-tree walk so economics/saves plug in later. Turns human-eyeball review into an agent QA sweep. Depends on #960 (the viewer) + the auto-pause substrate. Screenshot capture is a separate ticket (kept off the critical path).
Re-scope (2026-07-25, Si refinement + lead ruling): both cited blockers are satisfied (T-960, T-970 done) and the D-255(d) deep-rung cache cap is already built defensively for this exact consumer (step_canvas_disk_cache.gd:61 names the AtlasAgentInterface QA sweep). But D-226''s named-intent list was designed against the retired pre-D-255 continuous-zoom Atlas — the ticket''s intent surface is STALE. Implementer instruction: derive the actual intent list from the CURRENT stepped-rung API before building the wrapper, and post the derived list to the team lead for confirmation before implementation. Known mapping start (verify, don''t trust): select_system → atlas_app._on_system_selected(system_id); select_body → _on_body_selected(body); reset_view → StepCanvasViewer._reset_to_global(); set_overlay → set_overlay_visible(); back → _on_regional_back(); zoom step → _scroll_rung(direction, cursor_local); open_regional/select_city/open_system need tracing through screens/regional_screen.gd, district_screen.gd, system_screen.gd. SEQUENCING: T-1157 (stepped capture-harness redesign) needs the same walk-the-Atlas-headlessly primitive — its harness-techniques inventory (appended 2026-07-25) documents five proven eyeball-driver patterns (InputSwallower, in-process viewer-method navigation, fixed-center revisit, view-transform logging) that are the PROTOTYPE for this channel; build the T-971 intent layer as the productionization of that pattern so T-1157''s harness can consume it rather than the two designing overlapping scaffolding independently.', 'in_progress', 'medium', NULL, 'client', 'D-226', '2026-05-24 09:22:38', '2026-07-25 16:26:10.188', NULL, 'e6121a05892f9e4e72c7974d4b0faf96', 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 ('06FB0TNSRZZ5XKET93HPHKQHKW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Phase-4 test hardening — deferred review gaps (#953/#963 review)', 'Follow-ups from the Hoshe/Tyre pre-bake review (the BLOCKER/MAJOR items were fixed inline; these are the deferred MINOR/coverage items):
TESTS:
- features.rs: full per-type attractor reachability fixtures — craft heightmaps that guarantee LakeShore (enclosed depression), PassEntrance (saddle), PlainCenter (flat), RiverCrossing (confluence). Currently only RiverMouth/CoastalAccess/ValleyFloor are exercised on the slope/sine fixtures.
- heightmap.rs: 8-bit grayscale decode path; sea_level passthrough assertion; downsample identity/upsample early-return.
- drainage.rs: basin area_pct bit-for-bit determinism (only river_cells + basin count checked today).
- layer1.rs: attach_feature_names mountain branch with a non-empty mountain pool + ≥1 Alpine attractor.
- features.rs: thin_by_spacing behavior (spacing collisions, equirectangular column wrap).
- import_economics.py: automated idempotency test — two runs on a scratch DB yield identical atlas_city_names count (not doubled), and 0 rows for system ''GJ 0'' (Sol exemption). Wire alongside check-systems-db / pre-push.
- Wire test_sim_determinism.py into pre-push or a make target.
MINOR CODE NITS:
- drainage.rs ~480-488: comment the intentional isolated-basin fallback divergence from the old merge-to-basin-0 behavior (Tyre N1).
- planet_simulation.py ~787-788: oasis_water binary_dilation iterations are fixed-pixel — scale by GRID_W/512 for biome-display consistency at 1024 (cosmetic, Atlas only, not cascade; Tyre N2).
D-256/T-1174 finding (2026-07-25): the believability harness golden is INSENSITIVE to a total basin_direction regression — an all-North basin collapse (every survey cell defaulting) produced a byte-identical believability.json at current sample density (64/2048 districts; basin only reaches voxel meander phase/channel width, too localized for the coarse aggregate stats). The cross-namespace keying seam is now covered by the restored derive_all_districts_threads_supplied_basin_directions test (production-shaped survey keys), but the harness itself has no basin-sensitive metric. Candidate hardening: a basin-direction distribution stat (distinct directions >= 2 on a drained body) or a voxel-transect metric that moves with meander phase.
Refinement trim (2026-07-25, Si — every listed gap re-verified against current code before this batch): (1) DROP the test_sim_determinism.py wiring item — already done, Makefile:256 wires it into make test-tooling which the push gate runs on tooling changes. (2) The drainage.rs isolated-basin comment nit moved to ~line 654 (file grew; the old ~480-488 cite is stale) — still open, just relocate. (3) The layer1.rs attach_feature_names mountain-branch item: implicit coverage may already exist via run_layer1_* tests — check before adding a redundant test. (4) The D-256/T-1174 addendum''s "basin-direction distribution stat" candidate hardening is a genuine open design question (which stat, what threshold) — PROPOSE the stat to the lead before coding it, don''t invent silently. All other items (features.rs per-type reachability fixtures + thin_by_spacing, heightmap.rs 8-bit/sea-level-passthrough/upsample-identity, drainage.rs area_pct bit-determinism, import_economics.py idempotency test, oasis_water binary_dilation nit at planet_simulation.py:784-788) verified still open as written.', 'in_progress', 'medium', 'dudley', 'server', NULL, '2026-05-23 06:09:45', '2026-07-25 16:26:11.159', NULL, 'd2bc3750b255e45527ac695220f93c8d', 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 ('06FB0TNSRWTP633SM8XB302MBM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas agent control channel — observe/act named-intent navigation (D-226)', 'D-226 layer 4 (apex). A client-side AtlasAgentInterface exposing a JSON control channel so an automated agent (terminal/curl, headless Godot) can navigate the REAL Atlas UI: observe -> JSON of (a) current data state shown and (b) a walkable UI affordance tree (toggle controls: id/label/state/locked); act -> named SEMANTIC intents (select_system/open_system/select_body/open_regional/select_city/set_overlay/reset_view/back/close_atlas) backed by the same handlers a click calls (the map uses _gui_input, NOT pixel-clicks), plus get_layer_data_summary (attractor/river/basin counts as JSON without rendering). Single AtlasAgentInterface autoload/static class for a stable contract; generic Control-tree walk so economics/saves plug in later. Turns human-eyeball review into an agent QA sweep. Depends on #960 (the viewer) + the auto-pause substrate. Screenshot capture is a separate ticket (kept off the critical path).
Re-scope (2026-07-25, Si refinement + lead ruling): both cited blockers are satisfied (T-960, T-970 done) and the D-255(d) deep-rung cache cap is already built defensively for this exact consumer (step_canvas_disk_cache.gd:61 names the AtlasAgentInterface QA sweep). But D-226''s named-intent list was designed against the retired pre-D-255 continuous-zoom Atlas — the ticket''s intent surface is STALE. Implementer instruction: derive the actual intent list from the CURRENT stepped-rung API before building the wrapper, and post the derived list to the team lead for confirmation before implementation. Known mapping start (verify, don''t trust): select_system → atlas_app._on_system_selected(system_id); select_body → _on_body_selected(body); reset_view → StepCanvasViewer._reset_to_global(); set_overlay → set_overlay_visible(); back → _on_regional_back(); zoom step → _scroll_rung(direction, cursor_local); open_regional/select_city/open_system need tracing through screens/regional_screen.gd, district_screen.gd, system_screen.gd. SEQUENCING: T-1157 (stepped capture-harness redesign) needs the same walk-the-Atlas-headlessly primitive — its harness-techniques inventory (appended 2026-07-25) documents five proven eyeball-driver patterns (InputSwallower, in-process viewer-method navigation, fixed-center revisit, view-transform logging) that are the PROTOTYPE for this channel; build the T-971 intent layer as the productionization of that pattern so T-1157''s harness can consume it rather than the two designing overlapping scaffolding independently.', 'in_progress', 'medium', 'stig', 'client', 'D-226', '2026-05-24 09:22:38', '2026-07-25 16:26:11.308', NULL, 'c61776c96a1110fae0686f2eb4927fcd', 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 ('06FB0TNSRWTP633SM8XB302MBM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas agent control channel — observe/act named-intent navigation (D-226)', 'D-226 layer 4 (apex). A client-side AtlasAgentInterface exposing a JSON control channel so an automated agent (terminal/curl, headless Godot) can navigate the REAL Atlas UI: observe -> JSON of (a) current data state shown and (b) a walkable UI affordance tree (toggle controls: id/label/state/locked); act -> named SEMANTIC intents (select_system/open_system/select_body/open_regional/select_city/set_overlay/reset_view/back/close_atlas) backed by the same handlers a click calls (the map uses _gui_input, NOT pixel-clicks), plus get_layer_data_summary (attractor/river/basin counts as JSON without rendering). Single AtlasAgentInterface autoload/static class for a stable contract; generic Control-tree walk so economics/saves plug in later. Turns human-eyeball review into an agent QA sweep. Depends on #960 (the viewer) + the auto-pause substrate. Screenshot capture is a separate ticket (kept off the critical path).
Re-scope (2026-07-25, Si refinement + lead ruling): both cited blockers are satisfied (T-960, T-970 done) and the D-255(d) deep-rung cache cap is already built defensively for this exact consumer (step_canvas_disk_cache.gd:61 names the AtlasAgentInterface QA sweep). But D-226''s named-intent list was designed against the retired pre-D-255 continuous-zoom Atlas — the ticket''s intent surface is STALE. Implementer instruction: derive the actual intent list from the CURRENT stepped-rung API before building the wrapper, and post the derived list to the team lead for confirmation before implementation. Known mapping start (verify, don''t trust): select_system → atlas_app._on_system_selected(system_id); select_body → _on_body_selected(body); reset_view → StepCanvasViewer._reset_to_global(); set_overlay → set_overlay_visible(); back → _on_regional_back(); zoom step → _scroll_rung(direction, cursor_local); open_regional/select_city/open_system need tracing through screens/regional_screen.gd, district_screen.gd, system_screen.gd. SEQUENCING: T-1157 (stepped capture-harness redesign) needs the same walk-the-Atlas-headlessly primitive — its harness-techniques inventory (appended 2026-07-25) documents five proven eyeball-driver patterns (InputSwallower, in-process viewer-method navigation, fixed-center revisit, view-transform logging) that are the PROTOTYPE for this channel; build the T-971 intent layer as the productionization of that pattern so T-1157''s harness can consume it rather than the two designing overlapping scaffolding independently.
Intent-vocabulary diff vs D-226''s original list (recorded at implementation, 2026-07-25): select_city and open_regional DROPPED — post-D-255, settlements are settlement_id canvas cell values with no hit-test affordance (StepCanvasAnnotationLayer._draw_settlements() draws markers from cell data; no server get-settlement-by-id affordance, no client hit-test). A settlement-hit-test intent becomes its own ticket when a real consumer needs it — not filed speculatively. ADDED beyond D-226: open_atlas (a driver must be able to start a session), jump_to_center (the fixed-center revisit pattern proven by five T-1157-inventory eyeball drivers, now first-class via the new StepCanvasViewer.jump_to seam), and open_body replacing open_regional (Region..Chunk is one screen; descent is scroll_rung, not screen pushes). get_layer_data_summary reports the real EncodedStepCanvas fields (courses/cliffs/settlements) — D-226''s attractor/river/basin-count phrasing was retired-AtlasViewer vocabulary. Transport: in-process only this ticket; remote JSON transport is a follow-up when a remote consumer exists.', 'in_progress', 'medium', 'stig', 'client', 'D-226', '2026-05-24 09:22:38', '2026-07-25 16:50:40.290', NULL, '5889e54d53d1d2a8f1457cfb56c2f699', 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 ('06FB0TNSRWTP633SM8XB302MBM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas agent control channel — observe/act named-intent navigation (D-226)', 'D-226 layer 4 (apex). A client-side AtlasAgentInterface exposing a JSON control channel so an automated agent (terminal/curl, headless Godot) can navigate the REAL Atlas UI: observe -> JSON of (a) current data state shown and (b) a walkable UI affordance tree (toggle controls: id/label/state/locked); act -> named SEMANTIC intents (select_system/open_system/select_body/open_regional/select_city/set_overlay/reset_view/back/close_atlas) backed by the same handlers a click calls (the map uses _gui_input, NOT pixel-clicks), plus get_layer_data_summary (attractor/river/basin counts as JSON without rendering). Single AtlasAgentInterface autoload/static class for a stable contract; generic Control-tree walk so economics/saves plug in later. Turns human-eyeball review into an agent QA sweep. Depends on #960 (the viewer) + the auto-pause substrate. Screenshot capture is a separate ticket (kept off the critical path).
Re-scope (2026-07-25, Si refinement + lead ruling): both cited blockers are satisfied (T-960, T-970 done) and the D-255(d) deep-rung cache cap is already built defensively for this exact consumer (step_canvas_disk_cache.gd:61 names the AtlasAgentInterface QA sweep). But D-226''s named-intent list was designed against the retired pre-D-255 continuous-zoom Atlas — the ticket''s intent surface is STALE. Implementer instruction: derive the actual intent list from the CURRENT stepped-rung API before building the wrapper, and post the derived list to the team lead for confirmation before implementation. Known mapping start (verify, don''t trust): select_system → atlas_app._on_system_selected(system_id); select_body → _on_body_selected(body); reset_view → StepCanvasViewer._reset_to_global(); set_overlay → set_overlay_visible(); back → _on_regional_back(); zoom step → _scroll_rung(direction, cursor_local); open_regional/select_city/open_system need tracing through screens/regional_screen.gd, district_screen.gd, system_screen.gd. SEQUENCING: T-1157 (stepped capture-harness redesign) needs the same walk-the-Atlas-headlessly primitive — its harness-techniques inventory (appended 2026-07-25) documents five proven eyeball-driver patterns (InputSwallower, in-process viewer-method navigation, fixed-center revisit, view-transform logging) that are the PROTOTYPE for this channel; build the T-971 intent layer as the productionization of that pattern so T-1157''s harness can consume it rather than the two designing overlapping scaffolding independently.
Intent-vocabulary diff vs D-226''s original list (recorded at implementation, 2026-07-25): select_city and open_regional DROPPED — post-D-255, settlements are settlement_id canvas cell values with no hit-test affordance (StepCanvasAnnotationLayer._draw_settlements() draws markers from cell data; no server get-settlement-by-id affordance, no client hit-test). A settlement-hit-test intent becomes its own ticket when a real consumer needs it — not filed speculatively. ADDED beyond D-226: open_atlas (a driver must be able to start a session), jump_to_center (the fixed-center revisit pattern proven by five T-1157-inventory eyeball drivers, now first-class via the new StepCanvasViewer.jump_to seam), and open_body replacing open_regional (Region..Chunk is one screen; descent is scroll_rung, not screen pushes). get_layer_data_summary reports the real EncodedStepCanvas fields (courses/cliffs/settlements) — D-226''s attractor/river/basin-count phrasing was retired-AtlasViewer vocabulary. Transport: in-process only this ticket; remote JSON transport is a follow-up when a remote consumer exists.', 'review', 'medium', 'stig', 'client', 'D-226', '2026-05-24 09:22:38', '2026-07-25 16:57:34.622', NULL, 'e938b340830f851c004b4271226430ec', 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 ('06FB0TNSRZZ5XKET93HPHKQHKW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Phase-4 test hardening — deferred review gaps (#953/#963 review)', 'Follow-ups from the Hoshe/Tyre pre-bake review (the BLOCKER/MAJOR items were fixed inline; these are the deferred MINOR/coverage items):
TESTS:
- features.rs: full per-type attractor reachability fixtures — craft heightmaps that guarantee LakeShore (enclosed depression), PassEntrance (saddle), PlainCenter (flat), RiverCrossing (confluence). Currently only RiverMouth/CoastalAccess/ValleyFloor are exercised on the slope/sine fixtures.
- heightmap.rs: 8-bit grayscale decode path; sea_level passthrough assertion; downsample identity/upsample early-return.
- drainage.rs: basin area_pct bit-for-bit determinism (only river_cells + basin count checked today).
- layer1.rs: attach_feature_names mountain branch with a non-empty mountain pool + ≥1 Alpine attractor.
- features.rs: thin_by_spacing behavior (spacing collisions, equirectangular column wrap).
- import_economics.py: automated idempotency test — two runs on a scratch DB yield identical atlas_city_names count (not doubled), and 0 rows for system ''GJ 0'' (Sol exemption). Wire alongside check-systems-db / pre-push.
- Wire test_sim_determinism.py into pre-push or a make target.
MINOR CODE NITS:
- drainage.rs ~480-488: comment the intentional isolated-basin fallback divergence from the old merge-to-basin-0 behavior (Tyre N1).
- planet_simulation.py ~787-788: oasis_water binary_dilation iterations are fixed-pixel — scale by GRID_W/512 for biome-display consistency at 1024 (cosmetic, Atlas only, not cascade; Tyre N2).
D-256/T-1174 finding (2026-07-25): the believability harness golden is INSENSITIVE to a total basin_direction regression — an all-North basin collapse (every survey cell defaulting) produced a byte-identical believability.json at current sample density (64/2048 districts; basin only reaches voxel meander phase/channel width, too localized for the coarse aggregate stats). The cross-namespace keying seam is now covered by the restored derive_all_districts_threads_supplied_basin_directions test (production-shaped survey keys), but the harness itself has no basin-sensitive metric. Candidate hardening: a basin-direction distribution stat (distinct directions >= 2 on a drained body) or a voxel-transect metric that moves with meander phase.
Refinement trim (2026-07-25, Si — every listed gap re-verified against current code before this batch): (1) DROP the test_sim_determinism.py wiring item — already done, Makefile:256 wires it into make test-tooling which the push gate runs on tooling changes. (2) The drainage.rs isolated-basin comment nit moved to ~line 654 (file grew; the old ~480-488 cite is stale) — still open, just relocate. (3) The layer1.rs attach_feature_names mountain-branch item: implicit coverage may already exist via run_layer1_* tests — check before adding a redundant test. (4) The D-256/T-1174 addendum''s "basin-direction distribution stat" candidate hardening is a genuine open design question (which stat, what threshold) — PROPOSE the stat to the lead before coding it, don''t invent silently. All other items (features.rs per-type reachability fixtures + thin_by_spacing, heightmap.rs 8-bit/sea-level-passthrough/upsample-identity, drainage.rs area_pct bit-determinism, import_economics.py idempotency test, oasis_water binary_dilation nit at planet_simulation.py:784-788) verified still open as written.', 'review', 'medium', 'dudley', 'server', NULL, '2026-05-23 06:09:45', '2026-07-25 17:07:16.381', NULL, 'bea2ddb9c8e0377e59cc005cfe9cb7e5', 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 ('06FB0TNSRWTP633SM8XB302MBM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas agent control channel — observe/act named-intent navigation (D-226)', 'D-226 layer 4 (apex). A client-side AtlasAgentInterface exposing a JSON control channel so an automated agent (terminal/curl, headless Godot) can navigate the REAL Atlas UI: observe -> JSON of (a) current data state shown and (b) a walkable UI affordance tree (toggle controls: id/label/state/locked); act -> named SEMANTIC intents (select_system/open_system/select_body/open_regional/select_city/set_overlay/reset_view/back/close_atlas) backed by the same handlers a click calls (the map uses _gui_input, NOT pixel-clicks), plus get_layer_data_summary (attractor/river/basin counts as JSON without rendering). Single AtlasAgentInterface autoload/static class for a stable contract; generic Control-tree walk so economics/saves plug in later. Turns human-eyeball review into an agent QA sweep. Depends on #960 (the viewer) + the auto-pause substrate. Screenshot capture is a separate ticket (kept off the critical path).
Re-scope (2026-07-25, Si refinement + lead ruling): both cited blockers are satisfied (T-960, T-970 done) and the D-255(d) deep-rung cache cap is already built defensively for this exact consumer (step_canvas_disk_cache.gd:61 names the AtlasAgentInterface QA sweep). But D-226''s named-intent list was designed against the retired pre-D-255 continuous-zoom Atlas — the ticket''s intent surface is STALE. Implementer instruction: derive the actual intent list from the CURRENT stepped-rung API before building the wrapper, and post the derived list to the team lead for confirmation before implementation. Known mapping start (verify, don''t trust): select_system → atlas_app._on_system_selected(system_id); select_body → _on_body_selected(body); reset_view → StepCanvasViewer._reset_to_global(); set_overlay → set_overlay_visible(); back → _on_regional_back(); zoom step → _scroll_rung(direction, cursor_local); open_regional/select_city/open_system need tracing through screens/regional_screen.gd, district_screen.gd, system_screen.gd. SEQUENCING: T-1157 (stepped capture-harness redesign) needs the same walk-the-Atlas-headlessly primitive — its harness-techniques inventory (appended 2026-07-25) documents five proven eyeball-driver patterns (InputSwallower, in-process viewer-method navigation, fixed-center revisit, view-transform logging) that are the PROTOTYPE for this channel; build the T-971 intent layer as the productionization of that pattern so T-1157''s harness can consume it rather than the two designing overlapping scaffolding independently.
Intent-vocabulary diff vs D-226''s original list (recorded at implementation, 2026-07-25): select_city and open_regional DROPPED — post-D-255, settlements are settlement_id canvas cell values with no hit-test affordance (StepCanvasAnnotationLayer._draw_settlements() draws markers from cell data; no server get-settlement-by-id affordance, no client hit-test). A settlement-hit-test intent becomes its own ticket when a real consumer needs it — not filed speculatively. ADDED beyond D-226: open_atlas (a driver must be able to start a session), jump_to_center (the fixed-center revisit pattern proven by five T-1157-inventory eyeball drivers, now first-class via the new StepCanvasViewer.jump_to seam), and open_body replacing open_regional (Region..Chunk is one screen; descent is scroll_rung, not screen pushes). get_layer_data_summary reports the real EncodedStepCanvas fields (courses/cliffs/settlements) — D-226''s attractor/river/basin-count phrasing was retired-AtlasViewer vocabulary. Transport: in-process only this ticket; remote JSON transport is a follow-up when a remote consumer exists.', 'done', 'medium', 'stig', 'client', 'D-226', '2026-05-24 09:22:38', '2026-07-25 17:36:09.273', NULL, '6c94e1e1c5e957cad1f8a9047359f666', 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 ('06FB0TNSRZZ5XKET93HPHKQHKW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Phase-4 test hardening — deferred review gaps (#953/#963 review)', 'Follow-ups from the Hoshe/Tyre pre-bake review (the BLOCKER/MAJOR items were fixed inline; these are the deferred MINOR/coverage items):
TESTS:
- features.rs: full per-type attractor reachability fixtures — craft heightmaps that guarantee LakeShore (enclosed depression), PassEntrance (saddle), PlainCenter (flat), RiverCrossing (confluence). Currently only RiverMouth/CoastalAccess/ValleyFloor are exercised on the slope/sine fixtures.
- heightmap.rs: 8-bit grayscale decode path; sea_level passthrough assertion; downsample identity/upsample early-return.
- drainage.rs: basin area_pct bit-for-bit determinism (only river_cells + basin count checked today).
- layer1.rs: attach_feature_names mountain branch with a non-empty mountain pool + ≥1 Alpine attractor.
- features.rs: thin_by_spacing behavior (spacing collisions, equirectangular column wrap).
- import_economics.py: automated idempotency test — two runs on a scratch DB yield identical atlas_city_names count (not doubled), and 0 rows for system ''GJ 0'' (Sol exemption). Wire alongside check-systems-db / pre-push.
- Wire test_sim_determinism.py into pre-push or a make target.
MINOR CODE NITS:
- drainage.rs ~480-488: comment the intentional isolated-basin fallback divergence from the old merge-to-basin-0 behavior (Tyre N1).
- planet_simulation.py ~787-788: oasis_water binary_dilation iterations are fixed-pixel — scale by GRID_W/512 for biome-display consistency at 1024 (cosmetic, Atlas only, not cascade; Tyre N2).
D-256/T-1174 finding (2026-07-25): the believability harness golden is INSENSITIVE to a total basin_direction regression — an all-North basin collapse (every survey cell defaulting) produced a byte-identical believability.json at current sample density (64/2048 districts; basin only reaches voxel meander phase/channel width, too localized for the coarse aggregate stats). The cross-namespace keying seam is now covered by the restored derive_all_districts_threads_supplied_basin_directions test (production-shaped survey keys), but the harness itself has no basin-sensitive metric. Candidate hardening: a basin-direction distribution stat (distinct directions >= 2 on a drained body) or a voxel-transect metric that moves with meander phase.
Refinement trim (2026-07-25, Si — every listed gap re-verified against current code before this batch): (1) DROP the test_sim_determinism.py wiring item — already done, Makefile:256 wires it into make test-tooling which the push gate runs on tooling changes. (2) The drainage.rs isolated-basin comment nit moved to ~line 654 (file grew; the old ~480-488 cite is stale) — still open, just relocate. (3) The layer1.rs attach_feature_names mountain-branch item: implicit coverage may already exist via run_layer1_* tests — check before adding a redundant test. (4) The D-256/T-1174 addendum''s "basin-direction distribution stat" candidate hardening is a genuine open design question (which stat, what threshold) — PROPOSE the stat to the lead before coding it, don''t invent silently. All other items (features.rs per-type reachability fixtures + thin_by_spacing, heightmap.rs 8-bit/sea-level-passthrough/upsample-identity, drainage.rs area_pct bit-determinism, import_economics.py idempotency test, oasis_water binary_dilation nit at planet_simulation.py:784-788) verified still open as written.', 'done', 'medium', 'dudley', 'server', NULL, '2026-05-23 06:09:45', '2026-07-25 17:56:13.098', NULL, '1f23c5d80e2bae847ea4cb39ce6f9b48', 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 ('06FB0TNSRZ8Z6NN74YKCCRCPMR', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Generation validation pass + Phase 5 dependency gate', 'Validation pass: generated content reads correctly (settlements legible, walkable, named). Establishes the dependency gate that locks Phase 5 (player control, epic #749) behind Phase 4 completion per the cascade rule (D-166 amended 2026-05-22).
Gate trigger updated per D-166 amendment (2026-06-12): Phase 5 ramps in parallel once OUTSIDE generation produces deterministic, walkable exteriors — the gate this ticket holds is ''outsides generate deterministically and are walkable'' (validation pass: settlements legible, walkable, named), not ''all Phase-4 polish complete''. Door-boundary contracts and remaining Phase-4 detail may continue alongside early Phase-5 work.
Si refinement pass (2026-07-25, batch-5 candidate review): NEEDS-JEROEN before activation. The ticket''s own text is internally consistent (D-166 2026-06-12 trigger folded in; listed blockers T-952/T-959 both done), but "walkable exteriors" predates D-255 and the believability workstream, and three gate-criteria questions are unresolved:
(1) Is Q-123''s threshold calibration (sampling power, non-stationarity thresholds, relief/coherence floors, and the advisory->strict ratchet trigger for the D-245 gate) a prerequisite for this validation pass, or does the pass only need "mechanism exists and demonstrably fires" (T-1079''s six mechanism children T-1080..T-1085 are all done)?
(2) Do T-1116 (0 routable road edges on water-heavy bodies) and T-1098 (interstitial fill between building footprints incomplete) block "walkable" as this ticket defines it, or are they Phase-4 polish that continues in parallel per the D-166 amendment''s explicit allowance?
(3) Post-D-255, should the validation pass probe cascade output directly (aliveness_probe.rs / T-1083 harness) rather than an Atlas-eyeball check — the Atlas being presentation, not the generation under validation?
Graph note: this ticket currently has NO blocker edges to T-1079/Q-123, T-1116, or T-1098 — deliberately left unwired pending the ruling above; wire whichever the ruling designates as gating.', 'backlog', 'medium', NULL, 'server', 'D-166', '2026-05-22 17:42:34', '2026-07-25 18:03:56.875', NULL, '8c9a6891db17cc3826113077eec4d566', 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 ('06FBPTXPJ910NNR5T2R9F9NWQR', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Create the visual production-status catalog; fix glb-gen production path + naming conventions', '(description follows in first append)
---
Refinement (2026-06-16, /whats-next Si pass) — FULL scope (user decision). Two deliverables + conventions.
(1) CATALOG: instantiate the 5 docs/assets/visual/*.md files (sprites, furniture, tilesets, icons, effects) with per-category status tables. Template: docs/assets/_templates/sprite.md (SPR/FUR/TILE/ICO/FX prefixes); status vocab planned/in-progress/placeholder/final (docs/assets/README.md). Audio side (docs/assets/audio/README.md) is the working model.
(2) glb-gen PRODUCTION PATH: SKILL.md:115 names client/assets/models/ which does NOT exist. Create it + furniture/ + props/ subdirs (mirror the spike) + .gitkeep. Define client/assets/models/manifest.json schema MIRRORING the character manifest (docs/architecture/character-asset-organization.md) — per-asset entries: name, category, source-model path, subdirectory. Promotion convention per D-241 (promotion = repo-canonical). Update SKILL.md to point at the new path + reference the manifest.
(3) CONVENTIONS (author NOW, full scope): door state-frame suffixes (e.g. <model>_open / <model>_closed), Godot TileSet resource organization (<tileset>/<tile_id>.tres), multi-tile footprint metadata, and renderer/textures/ naming keyed to D-235 tokens (current renderer/README.md:80-95 covers sprites only: <model>_<direction>_<resolution>.png). Author these in docs/assets/visual/conventions.md (single home) and cross-link from the SKILL.
OVERLAP: T-1053 (rewrite araminta briefing) softly depends on this catalog — note the edge. T-1049''s per-class stories will reference these conventions — do T-1050 first / concurrently so T-1049 can cite them.
---
HELD 2026-06-16 (separated from the cascade batch; needs re-scoping before resuming).
The asset pipeline this ticket assumes is sprite-centric (docs/assets/visual/sprites.md
"Entities", _templates/sprite.md, SPR/FUR/TILE/ICO/FX prefixes, the /sprite-gen
render-3D->2D-sprites step). Direction correction: we are NOT producing sprites — Trellis
produces 3D objects (.glb) displayed in-world; the only flattened 2D content is TEXTURES.
Re-scope this ticket around a 3D-model + texture catalog (not a sprite catalog) before
activating. See the sprite-vs-3D root-cause note (this session).
Batch-5 Si pass correction (2026-07-25): the 2026-06-16 refinement block above is INVALIDATED by the HELD note that follows it — the sprite-centric scope (SPR/FUR prefixes, sprite catalog, /sprite-gen) contradicts the recorded direction (Trellis produces 3D .glb models; only flattened 2D content is textures). Do not implement as written. A re-scope pass (3D-model + texture catalog, manifest mirroring character-asset-organization, D-241 promotion, D-235-keyed texture naming) is running as part of batch 5; T-1052 (style bible) and T-1051 (title-only) get refinement passes in the same sweep. Sequencing per T-1049''s own note stands: this ticket''s conventions land first, T-1049/T-1053 consume them.', 'backlog', 'high', NULL, 'visual', NULL, '2026-06-12 10:40:59', '2026-07-25 18:04:03.964', NULL, 'feb7e2180b9e94f62003e1afb7550563', 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 ('06FRJD6EKPBQE08NA8VHP692W8', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Redesign the atlas visual-capture goldens for the zoom ladder (visual_scenarios/visual_capture)', 'T-1153 broke the T-1120/T-1121 golden capture harness: client/tests/visual_scenarios.gd and visual_capture.gd call app._regional_screen._viewer.get_heightmap_texture()/show_body() — both crash against AtlasWindowViewer (regional_screen now wraps the ladder viewer; AtlasViewer is unreachable from nav). Confirmed NOT wired into tests/run-godot or the pre-push hook (no reference in either), so nothing blocks, but the 12 curated atlas_* goldens and the --atlas-matrix capture path are dead until redesigned for the ladder (capture at defined rungs/zooms instead of the fixed heightmap frame; the new canonical orbital frame is the natural replacement for the old whole-body shot). Also decide the fate of the retired AtlasViewer code these harnesses were the last live consumers of — likely deletable once T-1156 (overlay port) settles what carries forward.
Live round 4 addendum: client/tests/test_atlas_window_overlay_draw_smoke.gd (3 scenarios -> now 2 after removing a flaky harness-sanity check: single-window draw + tile-mosaic draw, both asserting real composited pixels via SubViewport render) is a standalone gdUnit file with its own headless-skip guard, added because this redesign hadn''t landed yet. When this ticket lands, migrate its 2 scenarios into the redesigned tests/visual.json harness (folding the standalone file away) for consistency with the rest of the real-rendering test suite — see docs/DEVOPS.md''s ''Real-rendering test exception'' note for the interim rationale.
RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): this becomes the capture harness for the STEPPED render architecture — goldens key on (body, step) canvases, not the retired continuous-zoom frames. The T-1152/T-1153 test suites (coverage-walk, continuous-zoom seams, compute_tile_grid mosaic) retire with their code in T-1182''s change; this re-scoped harness is their replacement and the eyeball-check substrate for every step canvas. More needed than ever — keep, don''t cancel.
PR #203 fix-round inventory (2026-07-25): the retirement is now TOTAL — AtlasViewer and its whole cluster (atlas_viewer, atlas_marker_overlay, atlas_descend_geometry, atlas_legend_panel, atlas_generation_proxy, atlas_generation_state) are deleted. visual_scenarios.gd (_setup_atlas_golden_shot, 6 of 12 atlas_GJ* z2_0/z4_0 non-fit scenarios) and visual_capture.gd (_run_atlas_matrix) now call StepCanvasViewer methods that do not exist: get_heightmap_texture(), set_view(zoom, offset), get_view_offset() — the fit-zoom shots early-return before hitting them. Ruled in PR #203: NO shim (would resurrect the retired continuous-zoom API D-255 kills); the redesign here keys captures on (body, rung) via StepCanvasViewer''s real surface (enter/_scroll_rung/rung state + the echoed extent). get_overlay_defs()/set_overlay_visible() duck-type fine and survive. Until this lands, those 6 goldens + atlas_matrix are knowingly dead (manual make targets only, gate-invisible — confirmed again).
Harness techniques inventory addition (from the PR #204 / T-1183 restart-persistence eyeball, 2026-07-25) — proven live-rendering eyeball patterns for shared-desktop sessions:
1. InputSwallower pattern: root-level Node (added first, low process_priority), _input()+_unhandled_input() both call get_viewport().set_input_as_handled() unconditionally — neutralizes real desktop input reaching a `-s` SceneTree driver''s real window (X11/XWayland delivers pointer events to unfocused windows under the cursor). Verify via a per-capture-window intercept counter: N>0 intercepted AND zero resulting view drift is proof; 0 intercepted proves nothing.
2. In-process navigation via viewer methods, not OS input: call the same methods the app''s own gdUnit suite exercises (StepCanvasViewer.enter()/_scroll_rung(); RegionalScreen.enter() atlas-matrix precedent in visual_capture.gd) — identical production code path, no OS injection needed (Wayland/KWin blocks synthetic X11 injection for XWayland clients entirely).
3. View-transform logging at every capture: world_center, view_offset, the drawing Node2D''s canvas position, footprint_px alongside every screenshot — turns "pixels don''t match" into a root-causable mechanism (PR #204: drift matched PAN_SPEED_PX_S*frame_time*held-frames to 0.02px, identifying unfocused edge-scroll bleed, now T-1191).
4. Full-run-restart-on-anomaly, never partial retry: any mid-sequence assertion failure discards the whole run (cold-wipe for a cold-role attempt; fresh-process-only for a revisit-role attempt) — partial retries silently corrupt the state the comparison depends on.
5. Fixed-center revisit mode: record a prior run''s actual derived centers and re-request them literally on the comparison run, bypassing cursor-anchored re-derivation — required when run 2 must land on run 1''s exact cache keys, not merely repeat the gesture.
Harness-discipline gap found 2026-07-25 (Jeroen spotted a live orphan): agent-side kill-on-completion does not cover an agent CRASHING mid-capture (API error killed a session between server spawn and its own cleanup step — the server ran on unnoticed for ~25 min on the shared machine). Inventory addition: (6) the LEAD runs an explicit orphan sweep (ps for godot4 + settled-reach-server) at every cycle checkpoint — commit, merge, and after any agent dies abnormally — not only inside agent protocols; pr-process step 1a''s >5-min gdUnit grep does not match live-capture servers, so the sweep needs the server binary name too.', 'in_progress', 'medium', NULL, 'client', NULL, '2026-07-22 09:42:47.197', '2026-07-25 18:04:06.731', NULL, 'f08a1606d820c4d87bc7c1c94983521e', 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 ('06FSM3MQ5GFSPKKFJF6GGZN8CM', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Alpine relief capture — Quarter-vs-District differentiation on a tectonics:high body', 'Araminta''s PR #192 companion note, kept open by her PR #208 ruling (option b): the Quarter-vs-District relief-differentiation check was accepted for the T-1160 merge on a plains/river substitution (GJ380c world center -17723392,-10532864 — Quarter clearly shows finer higher-frequency texture than District), but plains texture and jagged tectonic relief are different visual regimes and the sharper case still needs its own capture. Task: identify the body pool with tectonics:high (or planet_class values mapping to TectonicClass::Volcanic/Active) and RECORD the pool in this ticket first — the PR #208 eyeball proved GJ380c/GJ820Bc are both tectonics:low via exhaustive two-Global color scans, don''t re-run that search blind — then capture quarter-relief vs district-relief at a true Alpine/MountainPass crop (MorphologyZone 13/14) at the same world center, matched crop, and have Araminta judge the differentiation. Reuse the pr208_driver fixed-center --step protocol (T-1157 harness inventory). Related: T-1160, PR #192, PR #208.', 'ready', 'low', NULL, 'client', NULL, '2026-07-25 16:14:35.308', '2026-07-25 18:04:07.273', NULL, 'd0f97fe8188febe0bb8c230b0b957317', 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 ('06FRJD6EKPBQE08NA8VHP692W8', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Redesign the atlas visual-capture goldens for the zoom ladder (visual_scenarios/visual_capture)', 'T-1153 broke the T-1120/T-1121 golden capture harness: client/tests/visual_scenarios.gd and visual_capture.gd call app._regional_screen._viewer.get_heightmap_texture()/show_body() — both crash against AtlasWindowViewer (regional_screen now wraps the ladder viewer; AtlasViewer is unreachable from nav). Confirmed NOT wired into tests/run-godot or the pre-push hook (no reference in either), so nothing blocks, but the 12 curated atlas_* goldens and the --atlas-matrix capture path are dead until redesigned for the ladder (capture at defined rungs/zooms instead of the fixed heightmap frame; the new canonical orbital frame is the natural replacement for the old whole-body shot). Also decide the fate of the retired AtlasViewer code these harnesses were the last live consumers of — likely deletable once T-1156 (overlay port) settles what carries forward.
Live round 4 addendum: client/tests/test_atlas_window_overlay_draw_smoke.gd (3 scenarios -> now 2 after removing a flaky harness-sanity check: single-window draw + tile-mosaic draw, both asserting real composited pixels via SubViewport render) is a standalone gdUnit file with its own headless-skip guard, added because this redesign hadn''t landed yet. When this ticket lands, migrate its 2 scenarios into the redesigned tests/visual.json harness (folding the standalone file away) for consistency with the rest of the real-rendering test suite — see docs/DEVOPS.md''s ''Real-rendering test exception'' note for the interim rationale.
RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): this becomes the capture harness for the STEPPED render architecture — goldens key on (body, step) canvases, not the retired continuous-zoom frames. The T-1152/T-1153 test suites (coverage-walk, continuous-zoom seams, compute_tile_grid mosaic) retire with their code in T-1182''s change; this re-scoped harness is their replacement and the eyeball-check substrate for every step canvas. More needed than ever — keep, don''t cancel.
PR #203 fix-round inventory (2026-07-25): the retirement is now TOTAL — AtlasViewer and its whole cluster (atlas_viewer, atlas_marker_overlay, atlas_descend_geometry, atlas_legend_panel, atlas_generation_proxy, atlas_generation_state) are deleted. visual_scenarios.gd (_setup_atlas_golden_shot, 6 of 12 atlas_GJ* z2_0/z4_0 non-fit scenarios) and visual_capture.gd (_run_atlas_matrix) now call StepCanvasViewer methods that do not exist: get_heightmap_texture(), set_view(zoom, offset), get_view_offset() — the fit-zoom shots early-return before hitting them. Ruled in PR #203: NO shim (would resurrect the retired continuous-zoom API D-255 kills); the redesign here keys captures on (body, rung) via StepCanvasViewer''s real surface (enter/_scroll_rung/rung state + the echoed extent). get_overlay_defs()/set_overlay_visible() duck-type fine and survive. Until this lands, those 6 goldens + atlas_matrix are knowingly dead (manual make targets only, gate-invisible — confirmed again).
Harness techniques inventory addition (from the PR #204 / T-1183 restart-persistence eyeball, 2026-07-25) — proven live-rendering eyeball patterns for shared-desktop sessions:
1. InputSwallower pattern: root-level Node (added first, low process_priority), _input()+_unhandled_input() both call get_viewport().set_input_as_handled() unconditionally — neutralizes real desktop input reaching a `-s` SceneTree driver''s real window (X11/XWayland delivers pointer events to unfocused windows under the cursor). Verify via a per-capture-window intercept counter: N>0 intercepted AND zero resulting view drift is proof; 0 intercepted proves nothing.
2. In-process navigation via viewer methods, not OS input: call the same methods the app''s own gdUnit suite exercises (StepCanvasViewer.enter()/_scroll_rung(); RegionalScreen.enter() atlas-matrix precedent in visual_capture.gd) — identical production code path, no OS injection needed (Wayland/KWin blocks synthetic X11 injection for XWayland clients entirely).
3. View-transform logging at every capture: world_center, view_offset, the drawing Node2D''s canvas position, footprint_px alongside every screenshot — turns "pixels don''t match" into a root-causable mechanism (PR #204: drift matched PAN_SPEED_PX_S*frame_time*held-frames to 0.02px, identifying unfocused edge-scroll bleed, now T-1191).
4. Full-run-restart-on-anomaly, never partial retry: any mid-sequence assertion failure discards the whole run (cold-wipe for a cold-role attempt; fresh-process-only for a revisit-role attempt) — partial retries silently corrupt the state the comparison depends on.
5. Fixed-center revisit mode: record a prior run''s actual derived centers and re-request them literally on the comparison run, bypassing cursor-anchored re-derivation — required when run 2 must land on run 1''s exact cache keys, not merely repeat the gesture.
Harness-discipline gap found 2026-07-25 (Jeroen spotted a live orphan): agent-side kill-on-completion does not cover an agent CRASHING mid-capture (API error killed a session between server spawn and its own cleanup step — the server ran on unnoticed for ~25 min on the shared machine). Inventory addition: (6) the LEAD runs an explicit orphan sweep (ps for godot4 + settled-reach-server) at every cycle checkpoint — commit, merge, and after any agent dies abnormally — not only inside agent protocols; pr-process step 1a''s >5-min gdUnit grep does not match live-capture servers, so the sweep needs the server binary name too.', 'in_progress', 'medium', 'hoshe', 'client', NULL, '2026-07-22 09:42:47.197', '2026-07-25 18:04:09.369', NULL, 'bfec1c0e51d38b2b61d89e92c62540a2', 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 ('06FBPTXPJ910NNR5T2R9F9NWQR', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Create the visual production-status catalog; fix glb-gen production path + naming conventions', '(description follows in first append)
---
Refinement (2026-06-16, /whats-next Si pass) — FULL scope (user decision). Two deliverables + conventions.
(1) CATALOG: instantiate the 5 docs/assets/visual/*.md files (sprites, furniture, tilesets, icons, effects) with per-category status tables. Template: docs/assets/_templates/sprite.md (SPR/FUR/TILE/ICO/FX prefixes); status vocab planned/in-progress/placeholder/final (docs/assets/README.md). Audio side (docs/assets/audio/README.md) is the working model.
(2) glb-gen PRODUCTION PATH: SKILL.md:115 names client/assets/models/ which does NOT exist. Create it + furniture/ + props/ subdirs (mirror the spike) + .gitkeep. Define client/assets/models/manifest.json schema MIRRORING the character manifest (docs/architecture/character-asset-organization.md) — per-asset entries: name, category, source-model path, subdirectory. Promotion convention per D-241 (promotion = repo-canonical). Update SKILL.md to point at the new path + reference the manifest.
(3) CONVENTIONS (author NOW, full scope): door state-frame suffixes (e.g. <model>_open / <model>_closed), Godot TileSet resource organization (<tileset>/<tile_id>.tres), multi-tile footprint metadata, and renderer/textures/ naming keyed to D-235 tokens (current renderer/README.md:80-95 covers sprites only: <model>_<direction>_<resolution>.png). Author these in docs/assets/visual/conventions.md (single home) and cross-link from the SKILL.
OVERLAP: T-1053 (rewrite araminta briefing) softly depends on this catalog — note the edge. T-1049''s per-class stories will reference these conventions — do T-1050 first / concurrently so T-1049 can cite them.
---
HELD 2026-06-16 (separated from the cascade batch; needs re-scoping before resuming).
The asset pipeline this ticket assumes is sprite-centric (docs/assets/visual/sprites.md
"Entities", _templates/sprite.md, SPR/FUR/TILE/ICO/FX prefixes, the /sprite-gen
render-3D->2D-sprites step). Direction correction: we are NOT producing sprites — Trellis
produces 3D objects (.glb) displayed in-world; the only flattened 2D content is TEXTURES.
Re-scope this ticket around a 3D-model + texture catalog (not a sprite catalog) before
activating. See the sprite-vs-3D root-cause note (this session).
Batch-5 Si pass correction (2026-07-25): the 2026-06-16 refinement block above is INVALIDATED by the HELD note that follows it — the sprite-centric scope (SPR/FUR prefixes, sprite catalog, /sprite-gen) contradicts the recorded direction (Trellis produces 3D .glb models; only flattened 2D content is textures). Do not implement as written. A re-scope pass (3D-model + texture catalog, manifest mirroring character-asset-organization, D-241 promotion, D-235-keyed texture naming) is running as part of batch 5; T-1052 (style bible) and T-1051 (title-only) get refinement passes in the same sweep. Sequencing per T-1049''s own note stands: this ticket''s conventions land first, T-1049/T-1053 consume them.
Refinement (batch-5 Si pass, 2026-07-25) — SUPERSEDES the 2026-06-16 block above (sprite-centric, invalidated by D-244). READY.
CONTEXT: D-244 (confirmed, 2026-06-16) already re-scoped the catalog structure and repointed
renderer/README.md, sprite-gen SKILL.md, docs/assets/README.md, docs/assets/visual/{README,palette}.md,
and docs/assets/_templates/sprite.md (marked legacy) at the 3D-model reality. This ticket is now
narrower than its original draft: instantiate what D-244 named but did not fill in, plus author
conventions.md (net-new — no D-244 amendment touched it).
DELIVERABLE (a) — Category catalog files, per docs/assets/README.md''s already-updated structure:
- docs/assets/visual/models.md — 3D objects/props (.glb via /glb-gen). Row template TBD (see
conventions.md below) since _templates/sprite.md is explicitly legacy/retired for this use.
- docs/assets/visual/textures.md — PBR surface maps (BaseColor/Normal/Roughness) for 3D models.
- docs/assets/visual/artwork.md — flat 2D art (paintings/flags/billboards/signage) via /sprite-gen''s
repurposed 2D-artwork role.
- docs/assets/visual/icons.md — UI icons (13 SVGs already exist in client/assets/icons/ — this file
should reflect that as-built inventory, not start at zero).
- docs/assets/visual/effects.md — shaders/particles/overlays.
Each file follows docs/assets/audio/README.md''s model: per-category status table (planned/in-progress/
placeholder/final per docs/assets/README.md''s vocab), current counts. Update docs/assets/visual/README.md''s
category table counts once files exist (currently all show 0, which will stay accurate at initial creation
— do not invent placeholder rows).
ACCEPTANCE: 5 files exist, cross-linked from visual/README.md (already has the links — verify they resolve),
each with a real (possibly empty) status table matching the audio/README.md pattern.
DELIVERABLE (b) — glb-gen production path:
- Create client/assets/models/ with furniture/ and props/ subdirectories (mirroring the proven
spike layout at spikes/3dpipeline/models/{furniture,props}/) + .gitkeep in each.
- Define client/assets/models/manifest.json, schema mirroring docs/architecture/character-asset-organization.md''s
per-asset-entry pattern (that doc''s clothing/accessory entries are the closest precedent — a flat
per-item record, not per-body-type variants since environment props aren''t body-fitted). Minimum
fields: name, category (furniture|props), source_model (path under client/assets/models/), subdirectory,
status (planned/in-progress/placeholder/final — reuse the docs/assets/README.md vocab so catalog and
manifest never disagree). Promotion = repo-canonical per D-241 — an asset entering manifest.json AND
client/assets/models/ IS the promotion act; nothing stays "promoted" in spikes/.
- Update .claude/skills/glb-gen/SKILL.md — replace the "client/assets/models/ does not exist... that''s
a Phase-5 concern" paragraph (lines ~96-102) with the new path + a pointer to the manifest. NOTE:
this contradicts nothing already in D-244/T-750 — T-750''s own epic description lists furniture/props
production as Phase-4 parallel work, so populating the production path (not populating the world with
them — that''s Phase 5 per T-961) is in-scope now.
ACCEPTANCE: client/assets/models/{furniture,props}/.gitkeep exist; manifest.json schema documented
(either inline in SKILL.md or as a comment/example in the file itself, following the character
manifest''s convention); SKILL.md''s stale "does not exist" paragraph is corrected.
DELIVERABLE (c) — docs/assets/visual/conventions.md (NEW FILE — no prior art to amend):
- Model naming: <category>_<descriptor>.glb (e.g. table_baroque.glb, chair_modernist.glb) —
mirrors the spike''s existing baroque_table.glb/modernist_chair.glb pattern loosely; pick one
order and state it (category-first matches the character-asset clothing convention
<category>_<descriptor>; the spike used descriptor-first — this is a naming decision to make,
not silently pick — see lead note below on who rules it).
- Mask sidecar: <model>_mask.png, single-channel per the character-asset convention (Section 3
of character-asset-organization.md) UNLESS the item needs multi-region tinting, in which case follow
the RGBA channel-routed convention from that doc''s Section 11 (T-1089) rather than reinventing one.
- Texture naming keyed to D-235 tokens: PBR maps for building-exterior surfaces should name by
the ratified ObjectTag vocabulary (D-235 amendment, wiki/economics/object_tag_vocabulary.toml) —
e.g. concrete_wall_basecolor.png, pitched_roof_normal.png — NOT the old renderer/README.md
<model>_<direction>_<resolution>.png scheme, which is direction-indexed for the retired 4-direction
sprite render and does not apply to PBR texture maps at all (there is no "direction" for a texture,
only map type: basecolor/normal/roughness).
- Door state handling — DO NOT port a state-frame suffix convention. The 2026-06-16 draft proposed
<model>_open/<model>_closed GLB variants; this is sprite-era thinking (baked state = baked frame).
For a 3D object the door''s open/closed/locked/sealed state (T-979/T-998''s DoorSpec: Open/Closed/
TemporalWindow/Locked/Sealed) is a RUNTIME transform/animation on one door .glb, not separate baked
models — but the actual mechanism (AnimationPlayer swing vs. procedural hinge rotation vs. a
door-model convention with a named "open" bone) is a Phase-5 rendering decision (player control +
in-world rendering, per the cascade), not a Phase-4 asset-catalog concern. This ticket
should record ONLY the negative constraint (no state-frame suffix files) and defer the positive
mechanism to whichever Phase-5 ticket handles door rendering. Do not invent a runtime convention here.
- Godot TileSet resource organization — OUT OF SCOPE, removed from ticket. client/scripts/rendering/
tile_renderer.gd''s TileSet is an explicitly-labeled placeholder ("programmatic TileSet with placeholder
colored rectangles," D-014) and the cascade rule states current in-world rendering code is
"left as-is until Phase 5 — do not build on it or rip it out before then." A <tileset>/<tile_id>.tres
convention would be building on Phase-5-owned code from Phase 4. Do not author this section.
- Multi-tile footprint metadata: still relevant (a table/couch spans multiple sim tiles regardless
of sprite vs 3D) — keep this item, express as a manifest.json field (e.g. footprint_tiles: [w, h])
rather than a filename convention.
ACCEPTANCE: conventions.md exists, covers model naming / mask sidecars / D-235-keyed texture naming /
multi-tile footprint metadata; explicitly states the door-state and TileSet items are deferred (with
reasons) rather than silently omitted, so a future refinement pass doesn''t have to re-derive why.
SEQUENCING (unchanged from 2026-06-16 note): T-1053 (araminta briefing rewrite) softly depends on this
catalog. T-1049''s per-class stories reference these conventions — this ticket lands first / concurrently.
Lead note (batch-5 activation): the model-naming-order call (category-first vs descriptor-first) is
delegated to araminta (art seat) this batch — her ruling lands in conventions.md with a one-line
rationale; surfaced to Jeroen in the batch report for cheap veto. VERDICT: READY.', 'backlog', 'high', NULL, 'visual', NULL, '2026-06-12 10:40:59', '2026-07-25 18:11:31.583', NULL, '9308e8dc46545e41dc298a34500e7621', 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 ('06FBPTXPV39JX54HYP83RZJFDM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Write the Phase-4 visual style bible into docs/assets/visual/palette.md', '(description follows in first append)
Refinement (batch-5 Si pass, 2026-07-25) — READY.
CURRENT STATE: docs/assets/visual/palette.md exists as a STUB (correctly re-scoped by D-244 — no
sprite-era content survives in it) — 4 bullets: camera angle (D-019, now superseded by D-148 for
gameplay — palette.md''s bullet is stale and must be corrected), entity colors (D-033), environmental
neutrality (D-045), "functional warmth." It explicitly defers to "art direction workshop synthesis and
first visual sprint" — this ticket IS that synthesis pass for Phase 4.
WHAT D-235 ALREADY FIXES (do not re-litigate): the building-exterior material/roof/facade/street
vocabulary (WallMaterial/RoofForm/FacadeRhythm/StreetSurface, ratified as the 28-template
architecture_trait_catalog.toml / wiki/economics/object_tag_vocabulary.toml) and the fallback-to-generic
mechanism. D-235 is the LOGICAL token layer ("what material is this wall") — Phase 4+ scope per its own
Implementation note ("textured render + the bulk of the theme library are Phase 5+"). T-1052 is the
RENDERED-LOOK layer on top: given a WallMaterial token, what does concrete_wall actually look like
(hue range, texture grain, weathering baseline) when art is eventually produced against it.
WHAT T-1052 MUST STILL AUTHOR (net-new, no existing decision covers this):
- Correct the camera-angle bullet: D-148 (30 deg low-angle Camera3D, supersedes D-019) is the GAMEPLAY
camera; D-019''s -72.5 deg survives only for the offline renderer (2D-artwork / retired sprite mode,
per renderer/README.md). palette.md''s current bullet conflates these — fix it to name both and
which applies where.
- A concrete color/material register for the D-235 token vocabulary: not new tokens, but the RANGE
each token maps to (e.g. "stone_wall reads as warm grey-tan, not cool grey" / "corrugated_metal
weathers to rust-orange, not staying silver") — the thing D-235 explicitly parked for Phase 5+ art
production but which a style bible can pre-specify so future asset generation has a target.
- Reconcile with docs/design/visual-grammar-v01.md and docs/workshops/art-direction-mood-board/
workshop-outcomes.md (both pre-cascade, v0.1-era, sprite-framed) — extract what survives (color
relationships, "functional warmth" identity, D-033 palette) vs what''s superseded by D-235/D-244
(era-based material ladder — explicitly retired by D-235''s own text: "Era is NOT a material filter
here"). Do not leave two contradictory style documents live without a note in one pointing at the
other''s supersession.
- Texture "grain"/rendering treatment for the D-244 3D-object reality: since objects are lit 3D meshes
now (not flat sprite fills), the style bible should say something about the toon/PBR balance —
precedent exists in client/assets/characters/shaders/ (toon.gdshader, toon_masked.gdshader,
outline.gdshader) and the glb-gen postprocess convention (roughness=1.0/specular=0.0 "for toon
compatibility" per .claude/skills/glb-gen/SKILL.md''s setup_materials() note). Confirm whether
environment props share this toon treatment with characters, or diverge — this is a real open
question the style bible should answer, not assume.
ACCEPTANCE: docs/assets/visual/palette.md is no longer a stub; camera-angle bullet corrected;
color/material register for D-235 tokens documented; toon-vs-PBR treatment for props stated;
supersession relationship to visual-grammar-v01.md / workshop-outcomes.md is explicit (either by
archiving/marking those superseded, or by palette.md stating exactly what it inherits from each).
Lead note (batch-5 activation): the toon-vs-PBR treatment call for environment props is delegated to
araminta (art seat) this batch — her ruling lands in palette.md with rationale; surfaced to Jeroen in
the batch report for cheap veto. VERDICT: READY.', 'backlog', 'medium', NULL, 'visual', 'D-235', '2026-06-12 10:40:59', '2026-07-25 18:11:34.859', NULL, '65d7ab732c3d9e6c912962245db39d28', 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 ('06FBPTXPPHC9FZ4T0GNKR615PR', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Author one-page briefs (or record formal deferral) for lamp posts, barns, TVs, billboards', '(description follows in first append)
Refinement (batch-5 Si pass, 2026-07-25) — GAPS. Formally deferred rather than refined into an
actionable brief-writing task now.
WHY: T-1051 was filed as a child of T-1049''s per-class breakdown (the fable-ous.md audit T-1049 cites,
2026-06-12). T-1049 itself is HELD pending the same sprite-to-3D re-scope as T-1050 (its "Refinement
2026-06-16 — READY" block still frames the per-class stories, briefs, and catalog files in sprite-era
language, and its own text says "sequence T-1049 to consume T-1050''s conventions where they overlap" —
so T-1049 cannot be un-held before T-1050 lands). T-1051 is one layer further down that same
dependency: it cannot be meaningfully scoped (what does a "one-page brief" even template against?)
until (1) T-1050 lands conventions.md + the catalog structure a brief would reference, AND (2) T-1049
is re-scoped and un-held (it owns the per-class story breakdown T-1051 is part of).
Zero repo hits exist for lamp posts, barns, TVs, or billboards beyond the T-750 epic description text
(confirmed by T-1049''s own audit matrix) — there is no existing partial work this ticket would be
resuming, so there is no cost to deferring it cleanly.
DISPOSITION: blocker edge wired (T-1051 blocked-by T-1049). When T-1049 is re-scoped (a future Si
pass, after T-1050 lands), that re-scope explicitly decides whether lamp posts/barns/TVs/billboards
get real briefs or a formal deferral note (T-1049''s existing block already leaned toward "explicitly
record which classes are deferred" for cars via Q-067 — the same treatment likely applies to these
four, but that is T-1049''s call when re-scoped, not this ticket''s in isolation).', 'backlog', 'medium', NULL, 'visual', NULL, '2026-06-12 10:40:59', '2026-07-25 18:11:38.173', NULL, '6b2a870d348502d5ea42f134e1e12e85', 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 ('06FBPTXPJ910NNR5T2R9F9NWQR', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Create the visual production-status catalog; fix glb-gen production path + naming conventions', '(description follows in first append)
---
Refinement (2026-06-16, /whats-next Si pass) — FULL scope (user decision). Two deliverables + conventions.
(1) CATALOG: instantiate the 5 docs/assets/visual/*.md files (sprites, furniture, tilesets, icons, effects) with per-category status tables. Template: docs/assets/_templates/sprite.md (SPR/FUR/TILE/ICO/FX prefixes); status vocab planned/in-progress/placeholder/final (docs/assets/README.md). Audio side (docs/assets/audio/README.md) is the working model.
(2) glb-gen PRODUCTION PATH: SKILL.md:115 names client/assets/models/ which does NOT exist. Create it + furniture/ + props/ subdirs (mirror the spike) + .gitkeep. Define client/assets/models/manifest.json schema MIRRORING the character manifest (docs/architecture/character-asset-organization.md) — per-asset entries: name, category, source-model path, subdirectory. Promotion convention per D-241 (promotion = repo-canonical). Update SKILL.md to point at the new path + reference the manifest.
(3) CONVENTIONS (author NOW, full scope): door state-frame suffixes (e.g. <model>_open / <model>_closed), Godot TileSet resource organization (<tileset>/<tile_id>.tres), multi-tile footprint metadata, and renderer/textures/ naming keyed to D-235 tokens (current renderer/README.md:80-95 covers sprites only: <model>_<direction>_<resolution>.png). Author these in docs/assets/visual/conventions.md (single home) and cross-link from the SKILL.
OVERLAP: T-1053 (rewrite araminta briefing) softly depends on this catalog — note the edge. T-1049''s per-class stories will reference these conventions — do T-1050 first / concurrently so T-1049 can cite them.
---
HELD 2026-06-16 (separated from the cascade batch; needs re-scoping before resuming).
The asset pipeline this ticket assumes is sprite-centric (docs/assets/visual/sprites.md
"Entities", _templates/sprite.md, SPR/FUR/TILE/ICO/FX prefixes, the /sprite-gen
render-3D->2D-sprites step). Direction correction: we are NOT producing sprites — Trellis
produces 3D objects (.glb) displayed in-world; the only flattened 2D content is TEXTURES.
Re-scope this ticket around a 3D-model + texture catalog (not a sprite catalog) before
activating. See the sprite-vs-3D root-cause note (this session).
Batch-5 Si pass correction (2026-07-25): the 2026-06-16 refinement block above is INVALIDATED by the HELD note that follows it — the sprite-centric scope (SPR/FUR prefixes, sprite catalog, /sprite-gen) contradicts the recorded direction (Trellis produces 3D .glb models; only flattened 2D content is textures). Do not implement as written. A re-scope pass (3D-model + texture catalog, manifest mirroring character-asset-organization, D-241 promotion, D-235-keyed texture naming) is running as part of batch 5; T-1052 (style bible) and T-1051 (title-only) get refinement passes in the same sweep. Sequencing per T-1049''s own note stands: this ticket''s conventions land first, T-1049/T-1053 consume them.
Refinement (batch-5 Si pass, 2026-07-25) — SUPERSEDES the 2026-06-16 block above (sprite-centric, invalidated by D-244). READY.
CONTEXT: D-244 (confirmed, 2026-06-16) already re-scoped the catalog structure and repointed
renderer/README.md, sprite-gen SKILL.md, docs/assets/README.md, docs/assets/visual/{README,palette}.md,
and docs/assets/_templates/sprite.md (marked legacy) at the 3D-model reality. This ticket is now
narrower than its original draft: instantiate what D-244 named but did not fill in, plus author
conventions.md (net-new — no D-244 amendment touched it).
DELIVERABLE (a) — Category catalog files, per docs/assets/README.md''s already-updated structure:
- docs/assets/visual/models.md — 3D objects/props (.glb via /glb-gen). Row template TBD (see
conventions.md below) since _templates/sprite.md is explicitly legacy/retired for this use.
- docs/assets/visual/textures.md — PBR surface maps (BaseColor/Normal/Roughness) for 3D models.
- docs/assets/visual/artwork.md — flat 2D art (paintings/flags/billboards/signage) via /sprite-gen''s
repurposed 2D-artwork role.
- docs/assets/visual/icons.md — UI icons (13 SVGs already exist in client/assets/icons/ — this file
should reflect that as-built inventory, not start at zero).
- docs/assets/visual/effects.md — shaders/particles/overlays.
Each file follows docs/assets/audio/README.md''s model: per-category status table (planned/in-progress/
placeholder/final per docs/assets/README.md''s vocab), current counts. Update docs/assets/visual/README.md''s
category table counts once files exist (currently all show 0, which will stay accurate at initial creation
— do not invent placeholder rows).
ACCEPTANCE: 5 files exist, cross-linked from visual/README.md (already has the links — verify they resolve),
each with a real (possibly empty) status table matching the audio/README.md pattern.
DELIVERABLE (b) — glb-gen production path:
- Create client/assets/models/ with furniture/ and props/ subdirectories (mirroring the proven
spike layout at spikes/3dpipeline/models/{furniture,props}/) + .gitkeep in each.
- Define client/assets/models/manifest.json, schema mirroring docs/architecture/character-asset-organization.md''s
per-asset-entry pattern (that doc''s clothing/accessory entries are the closest precedent — a flat
per-item record, not per-body-type variants since environment props aren''t body-fitted). Minimum
fields: name, category (furniture|props), source_model (path under client/assets/models/), subdirectory,
status (planned/in-progress/placeholder/final — reuse the docs/assets/README.md vocab so catalog and
manifest never disagree). Promotion = repo-canonical per D-241 — an asset entering manifest.json AND
client/assets/models/ IS the promotion act; nothing stays "promoted" in spikes/.
- Update .claude/skills/glb-gen/SKILL.md — replace the "client/assets/models/ does not exist... that''s
a Phase-5 concern" paragraph (lines ~96-102) with the new path + a pointer to the manifest. NOTE:
this contradicts nothing already in D-244/T-750 — T-750''s own epic description lists furniture/props
production as Phase-4 parallel work, so populating the production path (not populating the world with
them — that''s Phase 5 per T-961) is in-scope now.
ACCEPTANCE: client/assets/models/{furniture,props}/.gitkeep exist; manifest.json schema documented
(either inline in SKILL.md or as a comment/example in the file itself, following the character
manifest''s convention); SKILL.md''s stale "does not exist" paragraph is corrected.
DELIVERABLE (c) — docs/assets/visual/conventions.md (NEW FILE — no prior art to amend):
- Model naming: <category>_<descriptor>.glb (e.g. table_baroque.glb, chair_modernist.glb) —
mirrors the spike''s existing baroque_table.glb/modernist_chair.glb pattern loosely; pick one
order and state it (category-first matches the character-asset clothing convention
<category>_<descriptor>; the spike used descriptor-first — this is a naming decision to make,
not silently pick — see lead note below on who rules it).
- Mask sidecar: <model>_mask.png, single-channel per the character-asset convention (Section 3
of character-asset-organization.md) UNLESS the item needs multi-region tinting, in which case follow
the RGBA channel-routed convention from that doc''s Section 11 (T-1089) rather than reinventing one.
- Texture naming keyed to D-235 tokens: PBR maps for building-exterior surfaces should name by
the ratified ObjectTag vocabulary (D-235 amendment, wiki/economics/object_tag_vocabulary.toml) —
e.g. concrete_wall_basecolor.png, pitched_roof_normal.png — NOT the old renderer/README.md
<model>_<direction>_<resolution>.png scheme, which is direction-indexed for the retired 4-direction
sprite render and does not apply to PBR texture maps at all (there is no "direction" for a texture,
only map type: basecolor/normal/roughness).
- Door state handling — DO NOT port a state-frame suffix convention. The 2026-06-16 draft proposed
<model>_open/<model>_closed GLB variants; this is sprite-era thinking (baked state = baked frame).
For a 3D object the door''s open/closed/locked/sealed state (T-979/T-998''s DoorSpec: Open/Closed/
TemporalWindow/Locked/Sealed) is a RUNTIME transform/animation on one door .glb, not separate baked
models — but the actual mechanism (AnimationPlayer swing vs. procedural hinge rotation vs. a
door-model convention with a named "open" bone) is a Phase-5 rendering decision (player control +
in-world rendering, per the cascade), not a Phase-4 asset-catalog concern. This ticket
should record ONLY the negative constraint (no state-frame suffix files) and defer the positive
mechanism to whichever Phase-5 ticket handles door rendering. Do not invent a runtime convention here.
- Godot TileSet resource organization — OUT OF SCOPE, removed from ticket. client/scripts/rendering/
tile_renderer.gd''s TileSet is an explicitly-labeled placeholder ("programmatic TileSet with placeholder
colored rectangles," D-014) and the cascade rule states current in-world rendering code is
"left as-is until Phase 5 — do not build on it or rip it out before then." A <tileset>/<tile_id>.tres
convention would be building on Phase-5-owned code from Phase 4. Do not author this section.
- Multi-tile footprint metadata: still relevant (a table/couch spans multiple sim tiles regardless
of sprite vs 3D) — keep this item, express as a manifest.json field (e.g. footprint_tiles: [w, h])
rather than a filename convention.
ACCEPTANCE: conventions.md exists, covers model naming / mask sidecars / D-235-keyed texture naming /
multi-tile footprint metadata; explicitly states the door-state and TileSet items are deferred (with
reasons) rather than silently omitted, so a future refinement pass doesn''t have to re-derive why.
SEQUENCING (unchanged from 2026-06-16 note): T-1053 (araminta briefing rewrite) softly depends on this
catalog. T-1049''s per-class stories reference these conventions — this ticket lands first / concurrently.
Lead note (batch-5 activation): the model-naming-order call (category-first vs descriptor-first) is
delegated to araminta (art seat) this batch — her ruling lands in conventions.md with a one-line
rationale; surfaced to Jeroen in the batch report for cheap veto. VERDICT: READY.', 'in_progress', 'high', NULL, 'visual', NULL, '2026-06-12 10:40:59', '2026-07-25 18:11:42.941', NULL, '59842f2c6f871ac65a35da2af2d26258', 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 ('06FBPTXPV39JX54HYP83RZJFDM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Write the Phase-4 visual style bible into docs/assets/visual/palette.md', '(description follows in first append)
Refinement (batch-5 Si pass, 2026-07-25) — READY.
CURRENT STATE: docs/assets/visual/palette.md exists as a STUB (correctly re-scoped by D-244 — no
sprite-era content survives in it) — 4 bullets: camera angle (D-019, now superseded by D-148 for
gameplay — palette.md''s bullet is stale and must be corrected), entity colors (D-033), environmental
neutrality (D-045), "functional warmth." It explicitly defers to "art direction workshop synthesis and
first visual sprint" — this ticket IS that synthesis pass for Phase 4.
WHAT D-235 ALREADY FIXES (do not re-litigate): the building-exterior material/roof/facade/street
vocabulary (WallMaterial/RoofForm/FacadeRhythm/StreetSurface, ratified as the 28-template
architecture_trait_catalog.toml / wiki/economics/object_tag_vocabulary.toml) and the fallback-to-generic
mechanism. D-235 is the LOGICAL token layer ("what material is this wall") — Phase 4+ scope per its own
Implementation note ("textured render + the bulk of the theme library are Phase 5+"). T-1052 is the
RENDERED-LOOK layer on top: given a WallMaterial token, what does concrete_wall actually look like
(hue range, texture grain, weathering baseline) when art is eventually produced against it.
WHAT T-1052 MUST STILL AUTHOR (net-new, no existing decision covers this):
- Correct the camera-angle bullet: D-148 (30 deg low-angle Camera3D, supersedes D-019) is the GAMEPLAY
camera; D-019''s -72.5 deg survives only for the offline renderer (2D-artwork / retired sprite mode,
per renderer/README.md). palette.md''s current bullet conflates these — fix it to name both and
which applies where.
- A concrete color/material register for the D-235 token vocabulary: not new tokens, but the RANGE
each token maps to (e.g. "stone_wall reads as warm grey-tan, not cool grey" / "corrugated_metal
weathers to rust-orange, not staying silver") — the thing D-235 explicitly parked for Phase 5+ art
production but which a style bible can pre-specify so future asset generation has a target.
- Reconcile with docs/design/visual-grammar-v01.md and docs/workshops/art-direction-mood-board/
workshop-outcomes.md (both pre-cascade, v0.1-era, sprite-framed) — extract what survives (color
relationships, "functional warmth" identity, D-033 palette) vs what''s superseded by D-235/D-244
(era-based material ladder — explicitly retired by D-235''s own text: "Era is NOT a material filter
here"). Do not leave two contradictory style documents live without a note in one pointing at the
other''s supersession.
- Texture "grain"/rendering treatment for the D-244 3D-object reality: since objects are lit 3D meshes
now (not flat sprite fills), the style bible should say something about the toon/PBR balance —
precedent exists in client/assets/characters/shaders/ (toon.gdshader, toon_masked.gdshader,
outline.gdshader) and the glb-gen postprocess convention (roughness=1.0/specular=0.0 "for toon
compatibility" per .claude/skills/glb-gen/SKILL.md''s setup_materials() note). Confirm whether
environment props share this toon treatment with characters, or diverge — this is a real open
question the style bible should answer, not assume.
ACCEPTANCE: docs/assets/visual/palette.md is no longer a stub; camera-angle bullet corrected;
color/material register for D-235 tokens documented; toon-vs-PBR treatment for props stated;
supersession relationship to visual-grammar-v01.md / workshop-outcomes.md is explicit (either by
archiving/marking those superseded, or by palette.md stating exactly what it inherits from each).
Lead note (batch-5 activation): the toon-vs-PBR treatment call for environment props is delegated to
araminta (art seat) this batch — her ruling lands in palette.md with rationale; surfaced to Jeroen in
the batch report for cheap veto. VERDICT: READY.', 'in_progress', 'medium', NULL, 'visual', 'D-235', '2026-06-12 10:40:59', '2026-07-25 18:11:42.948', NULL, 'ec166c5d4c83097a1d51136ebf37380d', 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 ('06FBPTXPJ910NNR5T2R9F9NWQR', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Create the visual production-status catalog; fix glb-gen production path + naming conventions', '(description follows in first append)
---
Refinement (2026-06-16, /whats-next Si pass) — FULL scope (user decision). Two deliverables + conventions.
(1) CATALOG: instantiate the 5 docs/assets/visual/*.md files (sprites, furniture, tilesets, icons, effects) with per-category status tables. Template: docs/assets/_templates/sprite.md (SPR/FUR/TILE/ICO/FX prefixes); status vocab planned/in-progress/placeholder/final (docs/assets/README.md). Audio side (docs/assets/audio/README.md) is the working model.
(2) glb-gen PRODUCTION PATH: SKILL.md:115 names client/assets/models/ which does NOT exist. Create it + furniture/ + props/ subdirs (mirror the spike) + .gitkeep. Define client/assets/models/manifest.json schema MIRRORING the character manifest (docs/architecture/character-asset-organization.md) — per-asset entries: name, category, source-model path, subdirectory. Promotion convention per D-241 (promotion = repo-canonical). Update SKILL.md to point at the new path + reference the manifest.
(3) CONVENTIONS (author NOW, full scope): door state-frame suffixes (e.g. <model>_open / <model>_closed), Godot TileSet resource organization (<tileset>/<tile_id>.tres), multi-tile footprint metadata, and renderer/textures/ naming keyed to D-235 tokens (current renderer/README.md:80-95 covers sprites only: <model>_<direction>_<resolution>.png). Author these in docs/assets/visual/conventions.md (single home) and cross-link from the SKILL.
OVERLAP: T-1053 (rewrite araminta briefing) softly depends on this catalog — note the edge. T-1049''s per-class stories will reference these conventions — do T-1050 first / concurrently so T-1049 can cite them.
---
HELD 2026-06-16 (separated from the cascade batch; needs re-scoping before resuming).
The asset pipeline this ticket assumes is sprite-centric (docs/assets/visual/sprites.md
"Entities", _templates/sprite.md, SPR/FUR/TILE/ICO/FX prefixes, the /sprite-gen
render-3D->2D-sprites step). Direction correction: we are NOT producing sprites — Trellis
produces 3D objects (.glb) displayed in-world; the only flattened 2D content is TEXTURES.
Re-scope this ticket around a 3D-model + texture catalog (not a sprite catalog) before
activating. See the sprite-vs-3D root-cause note (this session).
Batch-5 Si pass correction (2026-07-25): the 2026-06-16 refinement block above is INVALIDATED by the HELD note that follows it — the sprite-centric scope (SPR/FUR prefixes, sprite catalog, /sprite-gen) contradicts the recorded direction (Trellis produces 3D .glb models; only flattened 2D content is textures). Do not implement as written. A re-scope pass (3D-model + texture catalog, manifest mirroring character-asset-organization, D-241 promotion, D-235-keyed texture naming) is running as part of batch 5; T-1052 (style bible) and T-1051 (title-only) get refinement passes in the same sweep. Sequencing per T-1049''s own note stands: this ticket''s conventions land first, T-1049/T-1053 consume them.
Refinement (batch-5 Si pass, 2026-07-25) — SUPERSEDES the 2026-06-16 block above (sprite-centric, invalidated by D-244). READY.
CONTEXT: D-244 (confirmed, 2026-06-16) already re-scoped the catalog structure and repointed
renderer/README.md, sprite-gen SKILL.md, docs/assets/README.md, docs/assets/visual/{README,palette}.md,
and docs/assets/_templates/sprite.md (marked legacy) at the 3D-model reality. This ticket is now
narrower than its original draft: instantiate what D-244 named but did not fill in, plus author
conventions.md (net-new — no D-244 amendment touched it).
DELIVERABLE (a) — Category catalog files, per docs/assets/README.md''s already-updated structure:
- docs/assets/visual/models.md — 3D objects/props (.glb via /glb-gen). Row template TBD (see
conventions.md below) since _templates/sprite.md is explicitly legacy/retired for this use.
- docs/assets/visual/textures.md — PBR surface maps (BaseColor/Normal/Roughness) for 3D models.
- docs/assets/visual/artwork.md — flat 2D art (paintings/flags/billboards/signage) via /sprite-gen''s
repurposed 2D-artwork role.
- docs/assets/visual/icons.md — UI icons (13 SVGs already exist in client/assets/icons/ — this file
should reflect that as-built inventory, not start at zero).
- docs/assets/visual/effects.md — shaders/particles/overlays.
Each file follows docs/assets/audio/README.md''s model: per-category status table (planned/in-progress/
placeholder/final per docs/assets/README.md''s vocab), current counts. Update docs/assets/visual/README.md''s
category table counts once files exist (currently all show 0, which will stay accurate at initial creation
— do not invent placeholder rows).
ACCEPTANCE: 5 files exist, cross-linked from visual/README.md (already has the links — verify they resolve),
each with a real (possibly empty) status table matching the audio/README.md pattern.
DELIVERABLE (b) — glb-gen production path:
- Create client/assets/models/ with furniture/ and props/ subdirectories (mirroring the proven
spike layout at spikes/3dpipeline/models/{furniture,props}/) + .gitkeep in each.
- Define client/assets/models/manifest.json, schema mirroring docs/architecture/character-asset-organization.md''s
per-asset-entry pattern (that doc''s clothing/accessory entries are the closest precedent — a flat
per-item record, not per-body-type variants since environment props aren''t body-fitted). Minimum
fields: name, category (furniture|props), source_model (path under client/assets/models/), subdirectory,
status (planned/in-progress/placeholder/final — reuse the docs/assets/README.md vocab so catalog and
manifest never disagree). Promotion = repo-canonical per D-241 — an asset entering manifest.json AND
client/assets/models/ IS the promotion act; nothing stays "promoted" in spikes/.
- Update .claude/skills/glb-gen/SKILL.md — replace the "client/assets/models/ does not exist... that''s
a Phase-5 concern" paragraph (lines ~96-102) with the new path + a pointer to the manifest. NOTE:
this contradicts nothing already in D-244/T-750 — T-750''s own epic description lists furniture/props
production as Phase-4 parallel work, so populating the production path (not populating the world with
them — that''s Phase 5 per T-961) is in-scope now.
ACCEPTANCE: client/assets/models/{furniture,props}/.gitkeep exist; manifest.json schema documented
(either inline in SKILL.md or as a comment/example in the file itself, following the character
manifest''s convention); SKILL.md''s stale "does not exist" paragraph is corrected.
DELIVERABLE (c) — docs/assets/visual/conventions.md (NEW FILE — no prior art to amend):
- Model naming: <category>_<descriptor>.glb (e.g. table_baroque.glb, chair_modernist.glb) —
mirrors the spike''s existing baroque_table.glb/modernist_chair.glb pattern loosely; pick one
order and state it (category-first matches the character-asset clothing convention
<category>_<descriptor>; the spike used descriptor-first — this is a naming decision to make,
not silently pick — see lead note below on who rules it).
- Mask sidecar: <model>_mask.png, single-channel per the character-asset convention (Section 3
of character-asset-organization.md) UNLESS the item needs multi-region tinting, in which case follow
the RGBA channel-routed convention from that doc''s Section 11 (T-1089) rather than reinventing one.
- Texture naming keyed to D-235 tokens: PBR maps for building-exterior surfaces should name by
the ratified ObjectTag vocabulary (D-235 amendment, wiki/economics/object_tag_vocabulary.toml) —
e.g. concrete_wall_basecolor.png, pitched_roof_normal.png — NOT the old renderer/README.md
<model>_<direction>_<resolution>.png scheme, which is direction-indexed for the retired 4-direction
sprite render and does not apply to PBR texture maps at all (there is no "direction" for a texture,
only map type: basecolor/normal/roughness).
- Door state handling — DO NOT port a state-frame suffix convention. The 2026-06-16 draft proposed
<model>_open/<model>_closed GLB variants; this is sprite-era thinking (baked state = baked frame).
For a 3D object the door''s open/closed/locked/sealed state (T-979/T-998''s DoorSpec: Open/Closed/
TemporalWindow/Locked/Sealed) is a RUNTIME transform/animation on one door .glb, not separate baked
models — but the actual mechanism (AnimationPlayer swing vs. procedural hinge rotation vs. a
door-model convention with a named "open" bone) is a Phase-5 rendering decision (player control +
in-world rendering, per the cascade), not a Phase-4 asset-catalog concern. This ticket
should record ONLY the negative constraint (no state-frame suffix files) and defer the positive
mechanism to whichever Phase-5 ticket handles door rendering. Do not invent a runtime convention here.
- Godot TileSet resource organization — OUT OF SCOPE, removed from ticket. client/scripts/rendering/
tile_renderer.gd''s TileSet is an explicitly-labeled placeholder ("programmatic TileSet with placeholder
colored rectangles," D-014) and the cascade rule states current in-world rendering code is
"left as-is until Phase 5 — do not build on it or rip it out before then." A <tileset>/<tile_id>.tres
convention would be building on Phase-5-owned code from Phase 4. Do not author this section.
- Multi-tile footprint metadata: still relevant (a table/couch spans multiple sim tiles regardless
of sprite vs 3D) — keep this item, express as a manifest.json field (e.g. footprint_tiles: [w, h])
rather than a filename convention.
ACCEPTANCE: conventions.md exists, covers model naming / mask sidecars / D-235-keyed texture naming /
multi-tile footprint metadata; explicitly states the door-state and TileSet items are deferred (with
reasons) rather than silently omitted, so a future refinement pass doesn''t have to re-derive why.
SEQUENCING (unchanged from 2026-06-16 note): T-1053 (araminta briefing rewrite) softly depends on this
catalog. T-1049''s per-class stories reference these conventions — this ticket lands first / concurrently.
Lead note (batch-5 activation): the model-naming-order call (category-first vs descriptor-first) is
delegated to araminta (art seat) this batch — her ruling lands in conventions.md with a one-line
rationale; surfaced to Jeroen in the batch report for cheap veto. VERDICT: READY.', 'in_progress', 'high', 'justine', 'visual', NULL, '2026-06-12 10:40:59', '2026-07-25 18:11:45.570', NULL, '1c88b124701c748774a3a03ef3a0bc18', 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 ('06FBPTXPV39JX54HYP83RZJFDM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Write the Phase-4 visual style bible into docs/assets/visual/palette.md', '(description follows in first append)
Refinement (batch-5 Si pass, 2026-07-25) — READY.
CURRENT STATE: docs/assets/visual/palette.md exists as a STUB (correctly re-scoped by D-244 — no
sprite-era content survives in it) — 4 bullets: camera angle (D-019, now superseded by D-148 for
gameplay — palette.md''s bullet is stale and must be corrected), entity colors (D-033), environmental
neutrality (D-045), "functional warmth." It explicitly defers to "art direction workshop synthesis and
first visual sprint" — this ticket IS that synthesis pass for Phase 4.
WHAT D-235 ALREADY FIXES (do not re-litigate): the building-exterior material/roof/facade/street
vocabulary (WallMaterial/RoofForm/FacadeRhythm/StreetSurface, ratified as the 28-template
architecture_trait_catalog.toml / wiki/economics/object_tag_vocabulary.toml) and the fallback-to-generic
mechanism. D-235 is the LOGICAL token layer ("what material is this wall") — Phase 4+ scope per its own
Implementation note ("textured render + the bulk of the theme library are Phase 5+"). T-1052 is the
RENDERED-LOOK layer on top: given a WallMaterial token, what does concrete_wall actually look like
(hue range, texture grain, weathering baseline) when art is eventually produced against it.
WHAT T-1052 MUST STILL AUTHOR (net-new, no existing decision covers this):
- Correct the camera-angle bullet: D-148 (30 deg low-angle Camera3D, supersedes D-019) is the GAMEPLAY
camera; D-019''s -72.5 deg survives only for the offline renderer (2D-artwork / retired sprite mode,
per renderer/README.md). palette.md''s current bullet conflates these — fix it to name both and
which applies where.
- A concrete color/material register for the D-235 token vocabulary: not new tokens, but the RANGE
each token maps to (e.g. "stone_wall reads as warm grey-tan, not cool grey" / "corrugated_metal
weathers to rust-orange, not staying silver") — the thing D-235 explicitly parked for Phase 5+ art
production but which a style bible can pre-specify so future asset generation has a target.
- Reconcile with docs/design/visual-grammar-v01.md and docs/workshops/art-direction-mood-board/
workshop-outcomes.md (both pre-cascade, v0.1-era, sprite-framed) — extract what survives (color
relationships, "functional warmth" identity, D-033 palette) vs what''s superseded by D-235/D-244
(era-based material ladder — explicitly retired by D-235''s own text: "Era is NOT a material filter
here"). Do not leave two contradictory style documents live without a note in one pointing at the
other''s supersession.
- Texture "grain"/rendering treatment for the D-244 3D-object reality: since objects are lit 3D meshes
now (not flat sprite fills), the style bible should say something about the toon/PBR balance —
precedent exists in client/assets/characters/shaders/ (toon.gdshader, toon_masked.gdshader,
outline.gdshader) and the glb-gen postprocess convention (roughness=1.0/specular=0.0 "for toon
compatibility" per .claude/skills/glb-gen/SKILL.md''s setup_materials() note). Confirm whether
environment props share this toon treatment with characters, or diverge — this is a real open
question the style bible should answer, not assume.
ACCEPTANCE: docs/assets/visual/palette.md is no longer a stub; camera-angle bullet corrected;
color/material register for D-235 tokens documented; toon-vs-PBR treatment for props stated;
supersession relationship to visual-grammar-v01.md / workshop-outcomes.md is explicit (either by
archiving/marking those superseded, or by palette.md stating exactly what it inherits from each).
Lead note (batch-5 activation): the toon-vs-PBR treatment call for environment props is delegated to
araminta (art seat) this batch — her ruling lands in palette.md with rationale; surfaced to Jeroen in
the batch report for cheap veto. VERDICT: READY.', 'in_progress', 'medium', 'araminta', 'visual', 'D-235', '2026-06-12 10:40:59', '2026-07-25 18:11:46.095', NULL, '4df3e92b2bc003a7b73f860dd47704f2', 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 ('06FBPTXPV39JX54HYP83RZJFDM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Write the Phase-4 visual style bible into docs/assets/visual/palette.md', '(description follows in first append)
Refinement (batch-5 Si pass, 2026-07-25) — READY.
CURRENT STATE: docs/assets/visual/palette.md exists as a STUB (correctly re-scoped by D-244 — no
sprite-era content survives in it) — 4 bullets: camera angle (D-019, now superseded by D-148 for
gameplay — palette.md''s bullet is stale and must be corrected), entity colors (D-033), environmental
neutrality (D-045), "functional warmth." It explicitly defers to "art direction workshop synthesis and
first visual sprint" — this ticket IS that synthesis pass for Phase 4.
WHAT D-235 ALREADY FIXES (do not re-litigate): the building-exterior material/roof/facade/street
vocabulary (WallMaterial/RoofForm/FacadeRhythm/StreetSurface, ratified as the 28-template
architecture_trait_catalog.toml / wiki/economics/object_tag_vocabulary.toml) and the fallback-to-generic
mechanism. D-235 is the LOGICAL token layer ("what material is this wall") — Phase 4+ scope per its own
Implementation note ("textured render + the bulk of the theme library are Phase 5+"). T-1052 is the
RENDERED-LOOK layer on top: given a WallMaterial token, what does concrete_wall actually look like
(hue range, texture grain, weathering baseline) when art is eventually produced against it.
WHAT T-1052 MUST STILL AUTHOR (net-new, no existing decision covers this):
- Correct the camera-angle bullet: D-148 (30 deg low-angle Camera3D, supersedes D-019) is the GAMEPLAY
camera; D-019''s -72.5 deg survives only for the offline renderer (2D-artwork / retired sprite mode,
per renderer/README.md). palette.md''s current bullet conflates these — fix it to name both and
which applies where.
- A concrete color/material register for the D-235 token vocabulary: not new tokens, but the RANGE
each token maps to (e.g. "stone_wall reads as warm grey-tan, not cool grey" / "corrugated_metal
weathers to rust-orange, not staying silver") — the thing D-235 explicitly parked for Phase 5+ art
production but which a style bible can pre-specify so future asset generation has a target.
- Reconcile with docs/design/visual-grammar-v01.md and docs/workshops/art-direction-mood-board/
workshop-outcomes.md (both pre-cascade, v0.1-era, sprite-framed) — extract what survives (color
relationships, "functional warmth" identity, D-033 palette) vs what''s superseded by D-235/D-244
(era-based material ladder — explicitly retired by D-235''s own text: "Era is NOT a material filter
here"). Do not leave two contradictory style documents live without a note in one pointing at the
other''s supersession.
- Texture "grain"/rendering treatment for the D-244 3D-object reality: since objects are lit 3D meshes
now (not flat sprite fills), the style bible should say something about the toon/PBR balance —
precedent exists in client/assets/characters/shaders/ (toon.gdshader, toon_masked.gdshader,
outline.gdshader) and the glb-gen postprocess convention (roughness=1.0/specular=0.0 "for toon
compatibility" per .claude/skills/glb-gen/SKILL.md''s setup_materials() note). Confirm whether
environment props share this toon treatment with characters, or diverge — this is a real open
question the style bible should answer, not assume.
ACCEPTANCE: docs/assets/visual/palette.md is no longer a stub; camera-angle bullet corrected;
color/material register for D-235 tokens documented; toon-vs-PBR treatment for props stated;
supersession relationship to visual-grammar-v01.md / workshop-outcomes.md is explicit (either by
archiving/marking those superseded, or by palette.md stating exactly what it inherits from each).
Lead note (batch-5 activation): the toon-vs-PBR treatment call for environment props is delegated to
araminta (art seat) this batch — her ruling lands in palette.md with rationale; surfaced to Jeroen in
the batch report for cheap veto. VERDICT: READY.', 'review', 'medium', 'araminta', 'visual', 'D-235', '2026-06-12 10:40:59', '2026-07-25 18:17:34.364', NULL, '580bf084c1ac42555f672f44b96b6b4a', 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 ('06FBPTXPJ910NNR5T2R9F9NWQR', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Create the visual production-status catalog; fix glb-gen production path + naming conventions', '(description follows in first append)
---
Refinement (2026-06-16, /whats-next Si pass) — FULL scope (user decision). Two deliverables + conventions.
(1) CATALOG: instantiate the 5 docs/assets/visual/*.md files (sprites, furniture, tilesets, icons, effects) with per-category status tables. Template: docs/assets/_templates/sprite.md (SPR/FUR/TILE/ICO/FX prefixes); status vocab planned/in-progress/placeholder/final (docs/assets/README.md). Audio side (docs/assets/audio/README.md) is the working model.
(2) glb-gen PRODUCTION PATH: SKILL.md:115 names client/assets/models/ which does NOT exist. Create it + furniture/ + props/ subdirs (mirror the spike) + .gitkeep. Define client/assets/models/manifest.json schema MIRRORING the character manifest (docs/architecture/character-asset-organization.md) — per-asset entries: name, category, source-model path, subdirectory. Promotion convention per D-241 (promotion = repo-canonical). Update SKILL.md to point at the new path + reference the manifest.
(3) CONVENTIONS (author NOW, full scope): door state-frame suffixes (e.g. <model>_open / <model>_closed), Godot TileSet resource organization (<tileset>/<tile_id>.tres), multi-tile footprint metadata, and renderer/textures/ naming keyed to D-235 tokens (current renderer/README.md:80-95 covers sprites only: <model>_<direction>_<resolution>.png). Author these in docs/assets/visual/conventions.md (single home) and cross-link from the SKILL.
OVERLAP: T-1053 (rewrite araminta briefing) softly depends on this catalog — note the edge. T-1049''s per-class stories will reference these conventions — do T-1050 first / concurrently so T-1049 can cite them.
---
HELD 2026-06-16 (separated from the cascade batch; needs re-scoping before resuming).
The asset pipeline this ticket assumes is sprite-centric (docs/assets/visual/sprites.md
"Entities", _templates/sprite.md, SPR/FUR/TILE/ICO/FX prefixes, the /sprite-gen
render-3D->2D-sprites step). Direction correction: we are NOT producing sprites — Trellis
produces 3D objects (.glb) displayed in-world; the only flattened 2D content is TEXTURES.
Re-scope this ticket around a 3D-model + texture catalog (not a sprite catalog) before
activating. See the sprite-vs-3D root-cause note (this session).
Batch-5 Si pass correction (2026-07-25): the 2026-06-16 refinement block above is INVALIDATED by the HELD note that follows it — the sprite-centric scope (SPR/FUR prefixes, sprite catalog, /sprite-gen) contradicts the recorded direction (Trellis produces 3D .glb models; only flattened 2D content is textures). Do not implement as written. A re-scope pass (3D-model + texture catalog, manifest mirroring character-asset-organization, D-241 promotion, D-235-keyed texture naming) is running as part of batch 5; T-1052 (style bible) and T-1051 (title-only) get refinement passes in the same sweep. Sequencing per T-1049''s own note stands: this ticket''s conventions land first, T-1049/T-1053 consume them.
Refinement (batch-5 Si pass, 2026-07-25) — SUPERSEDES the 2026-06-16 block above (sprite-centric, invalidated by D-244). READY.
CONTEXT: D-244 (confirmed, 2026-06-16) already re-scoped the catalog structure and repointed
renderer/README.md, sprite-gen SKILL.md, docs/assets/README.md, docs/assets/visual/{README,palette}.md,
and docs/assets/_templates/sprite.md (marked legacy) at the 3D-model reality. This ticket is now
narrower than its original draft: instantiate what D-244 named but did not fill in, plus author
conventions.md (net-new — no D-244 amendment touched it).
DELIVERABLE (a) — Category catalog files, per docs/assets/README.md''s already-updated structure:
- docs/assets/visual/models.md — 3D objects/props (.glb via /glb-gen). Row template TBD (see
conventions.md below) since _templates/sprite.md is explicitly legacy/retired for this use.
- docs/assets/visual/textures.md — PBR surface maps (BaseColor/Normal/Roughness) for 3D models.
- docs/assets/visual/artwork.md — flat 2D art (paintings/flags/billboards/signage) via /sprite-gen''s
repurposed 2D-artwork role.
- docs/assets/visual/icons.md — UI icons (13 SVGs already exist in client/assets/icons/ — this file
should reflect that as-built inventory, not start at zero).
- docs/assets/visual/effects.md — shaders/particles/overlays.
Each file follows docs/assets/audio/README.md''s model: per-category status table (planned/in-progress/
placeholder/final per docs/assets/README.md''s vocab), current counts. Update docs/assets/visual/README.md''s
category table counts once files exist (currently all show 0, which will stay accurate at initial creation
— do not invent placeholder rows).
ACCEPTANCE: 5 files exist, cross-linked from visual/README.md (already has the links — verify they resolve),
each with a real (possibly empty) status table matching the audio/README.md pattern.
DELIVERABLE (b) — glb-gen production path:
- Create client/assets/models/ with furniture/ and props/ subdirectories (mirroring the proven
spike layout at spikes/3dpipeline/models/{furniture,props}/) + .gitkeep in each.
- Define client/assets/models/manifest.json, schema mirroring docs/architecture/character-asset-organization.md''s
per-asset-entry pattern (that doc''s clothing/accessory entries are the closest precedent — a flat
per-item record, not per-body-type variants since environment props aren''t body-fitted). Minimum
fields: name, category (furniture|props), source_model (path under client/assets/models/), subdirectory,
status (planned/in-progress/placeholder/final — reuse the docs/assets/README.md vocab so catalog and
manifest never disagree). Promotion = repo-canonical per D-241 — an asset entering manifest.json AND
client/assets/models/ IS the promotion act; nothing stays "promoted" in spikes/.
- Update .claude/skills/glb-gen/SKILL.md — replace the "client/assets/models/ does not exist... that''s
a Phase-5 concern" paragraph (lines ~96-102) with the new path + a pointer to the manifest. NOTE:
this contradicts nothing already in D-244/T-750 — T-750''s own epic description lists furniture/props
production as Phase-4 parallel work, so populating the production path (not populating the world with
them — that''s Phase 5 per T-961) is in-scope now.
ACCEPTANCE: client/assets/models/{furniture,props}/.gitkeep exist; manifest.json schema documented
(either inline in SKILL.md or as a comment/example in the file itself, following the character
manifest''s convention); SKILL.md''s stale "does not exist" paragraph is corrected.
DELIVERABLE (c) — docs/assets/visual/conventions.md (NEW FILE — no prior art to amend):
- Model naming: <category>_<descriptor>.glb (e.g. table_baroque.glb, chair_modernist.glb) —
mirrors the spike''s existing baroque_table.glb/modernist_chair.glb pattern loosely; pick one
order and state it (category-first matches the character-asset clothing convention
<category>_<descriptor>; the spike used descriptor-first — this is a naming decision to make,
not silently pick — see lead note below on who rules it).
- Mask sidecar: <model>_mask.png, single-channel per the character-asset convention (Section 3
of character-asset-organization.md) UNLESS the item needs multi-region tinting, in which case follow
the RGBA channel-routed convention from that doc''s Section 11 (T-1089) rather than reinventing one.
- Texture naming keyed to D-235 tokens: PBR maps for building-exterior surfaces should name by
the ratified ObjectTag vocabulary (D-235 amendment, wiki/economics/object_tag_vocabulary.toml) —
e.g. concrete_wall_basecolor.png, pitched_roof_normal.png — NOT the old renderer/README.md
<model>_<direction>_<resolution>.png scheme, which is direction-indexed for the retired 4-direction
sprite render and does not apply to PBR texture maps at all (there is no "direction" for a texture,
only map type: basecolor/normal/roughness).
- Door state handling — DO NOT port a state-frame suffix convention. The 2026-06-16 draft proposed
<model>_open/<model>_closed GLB variants; this is sprite-era thinking (baked state = baked frame).
For a 3D object the door''s open/closed/locked/sealed state (T-979/T-998''s DoorSpec: Open/Closed/
TemporalWindow/Locked/Sealed) is a RUNTIME transform/animation on one door .glb, not separate baked
models — but the actual mechanism (AnimationPlayer swing vs. procedural hinge rotation vs. a
door-model convention with a named "open" bone) is a Phase-5 rendering decision (player control +
in-world rendering, per the cascade), not a Phase-4 asset-catalog concern. This ticket
should record ONLY the negative constraint (no state-frame suffix files) and defer the positive
mechanism to whichever Phase-5 ticket handles door rendering. Do not invent a runtime convention here.
- Godot TileSet resource organization — OUT OF SCOPE, removed from ticket. client/scripts/rendering/
tile_renderer.gd''s TileSet is an explicitly-labeled placeholder ("programmatic TileSet with placeholder
colored rectangles," D-014) and the cascade rule states current in-world rendering code is
"left as-is until Phase 5 — do not build on it or rip it out before then." A <tileset>/<tile_id>.tres
convention would be building on Phase-5-owned code from Phase 4. Do not author this section.
- Multi-tile footprint metadata: still relevant (a table/couch spans multiple sim tiles regardless
of sprite vs 3D) — keep this item, express as a manifest.json field (e.g. footprint_tiles: [w, h])
rather than a filename convention.
ACCEPTANCE: conventions.md exists, covers model naming / mask sidecars / D-235-keyed texture naming /
multi-tile footprint metadata; explicitly states the door-state and TileSet items are deferred (with
reasons) rather than silently omitted, so a future refinement pass doesn''t have to re-derive why.
SEQUENCING (unchanged from 2026-06-16 note): T-1053 (araminta briefing rewrite) softly depends on this
catalog. T-1049''s per-class stories reference these conventions — this ticket lands first / concurrently.
Lead note (batch-5 activation): the model-naming-order call (category-first vs descriptor-first) is
delegated to araminta (art seat) this batch — her ruling lands in conventions.md with a one-line
rationale; surfaced to Jeroen in the batch report for cheap veto. VERDICT: READY.', 'review', 'high', 'justine', 'visual', NULL, '2026-06-12 10:40:59', '2026-07-25 18:19:48.052', NULL, '111c7b03cb5bc345c306a64fcd8a76d0', 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 ('06FSHSYM96N2M9GJTQMF58E4WC', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'StepCanvasViewer test suite lacks an injected disk-cache-root seam — tests share the real user://atlas_cache', 'Flagged by stig during the T-1189/T-1192 round (2026-07-25): test_step_canvas_viewer.gd has no injected disk-cache-root seam (unlike test_step_canvas_request.gd, which injects a disposable root), so viewer tests that exercise the disk-cache path against a real-looking body_id (e.g. GJ1c) read/write the REAL user://atlas_cache/ shared across runs — a stale real entry from earlier manual sessions can leak into test behavior. Current mitigation (used by the new T-1189/T-1192 tests and the existing sweep smoke test): distinctive synthetic body_ids. Proper fix: give the viewer suite the same injectable cache-root seam the request suite has (constructor/setter injection through StepCanvasRequest), and migrate the synthetic-body_id tests onto it. Small, test-only.
First slice landed 2026-07-25 (main, 9ac98542d) via a live incident: PR #211''s gate failed on test_step_canvas_viewer''s two before-any-canvas tests because user://atlas_cache/ is machine-shared (every worktree gate, live capture driver, and play session write the same directory) and the T-1183 disk lookup short-circuits before test_mode''s no-op IPC — a concurrent GJ380c Global live capture (T-1157 eyeball work) warmed the exact key the tests assume cold. Fix: StepCanvasViewer.disk_cache_root_override threading into StepCanvasRequest''s existing test-injection seam; viewer + legend suites route construction through _make_viewer() helpers pinning isolated roots. REMAINING for this ticket: screen-constructed viewers (RegionalScreen builds its viewer internally — no override path), and any future suite constructing viewers directly should use the helper pattern; consider a gdUnit-wide guard (e.g. a shared test base or a lint) so new suites cannot silently ride the shared root.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-25 10:52:37.833', '2026-07-25 18:45:59.847', NULL, '00c87aff1d2d9c4406944b52fd04c6f0', 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 ('06FBPTXPJ910NNR5T2R9F9NWQR', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Create the visual production-status catalog; fix glb-gen production path + naming conventions', '(description follows in first append)
---
Refinement (2026-06-16, /whats-next Si pass) — FULL scope (user decision). Two deliverables + conventions.
(1) CATALOG: instantiate the 5 docs/assets/visual/*.md files (sprites, furniture, tilesets, icons, effects) with per-category status tables. Template: docs/assets/_templates/sprite.md (SPR/FUR/TILE/ICO/FX prefixes); status vocab planned/in-progress/placeholder/final (docs/assets/README.md). Audio side (docs/assets/audio/README.md) is the working model.
(2) glb-gen PRODUCTION PATH: SKILL.md:115 names client/assets/models/ which does NOT exist. Create it + furniture/ + props/ subdirs (mirror the spike) + .gitkeep. Define client/assets/models/manifest.json schema MIRRORING the character manifest (docs/architecture/character-asset-organization.md) — per-asset entries: name, category, source-model path, subdirectory. Promotion convention per D-241 (promotion = repo-canonical). Update SKILL.md to point at the new path + reference the manifest.
(3) CONVENTIONS (author NOW, full scope): door state-frame suffixes (e.g. <model>_open / <model>_closed), Godot TileSet resource organization (<tileset>/<tile_id>.tres), multi-tile footprint metadata, and renderer/textures/ naming keyed to D-235 tokens (current renderer/README.md:80-95 covers sprites only: <model>_<direction>_<resolution>.png). Author these in docs/assets/visual/conventions.md (single home) and cross-link from the SKILL.
OVERLAP: T-1053 (rewrite araminta briefing) softly depends on this catalog — note the edge. T-1049''s per-class stories will reference these conventions — do T-1050 first / concurrently so T-1049 can cite them.
---
HELD 2026-06-16 (separated from the cascade batch; needs re-scoping before resuming).
The asset pipeline this ticket assumes is sprite-centric (docs/assets/visual/sprites.md
"Entities", _templates/sprite.md, SPR/FUR/TILE/ICO/FX prefixes, the /sprite-gen
render-3D->2D-sprites step). Direction correction: we are NOT producing sprites — Trellis
produces 3D objects (.glb) displayed in-world; the only flattened 2D content is TEXTURES.
Re-scope this ticket around a 3D-model + texture catalog (not a sprite catalog) before
activating. See the sprite-vs-3D root-cause note (this session).
Batch-5 Si pass correction (2026-07-25): the 2026-06-16 refinement block above is INVALIDATED by the HELD note that follows it — the sprite-centric scope (SPR/FUR prefixes, sprite catalog, /sprite-gen) contradicts the recorded direction (Trellis produces 3D .glb models; only flattened 2D content is textures). Do not implement as written. A re-scope pass (3D-model + texture catalog, manifest mirroring character-asset-organization, D-241 promotion, D-235-keyed texture naming) is running as part of batch 5; T-1052 (style bible) and T-1051 (title-only) get refinement passes in the same sweep. Sequencing per T-1049''s own note stands: this ticket''s conventions land first, T-1049/T-1053 consume them.
Refinement (batch-5 Si pass, 2026-07-25) — SUPERSEDES the 2026-06-16 block above (sprite-centric, invalidated by D-244). READY.
CONTEXT: D-244 (confirmed, 2026-06-16) already re-scoped the catalog structure and repointed
renderer/README.md, sprite-gen SKILL.md, docs/assets/README.md, docs/assets/visual/{README,palette}.md,
and docs/assets/_templates/sprite.md (marked legacy) at the 3D-model reality. This ticket is now
narrower than its original draft: instantiate what D-244 named but did not fill in, plus author
conventions.md (net-new — no D-244 amendment touched it).
DELIVERABLE (a) — Category catalog files, per docs/assets/README.md''s already-updated structure:
- docs/assets/visual/models.md — 3D objects/props (.glb via /glb-gen). Row template TBD (see
conventions.md below) since _templates/sprite.md is explicitly legacy/retired for this use.
- docs/assets/visual/textures.md — PBR surface maps (BaseColor/Normal/Roughness) for 3D models.
- docs/assets/visual/artwork.md — flat 2D art (paintings/flags/billboards/signage) via /sprite-gen''s
repurposed 2D-artwork role.
- docs/assets/visual/icons.md — UI icons (13 SVGs already exist in client/assets/icons/ — this file
should reflect that as-built inventory, not start at zero).
- docs/assets/visual/effects.md — shaders/particles/overlays.
Each file follows docs/assets/audio/README.md''s model: per-category status table (planned/in-progress/
placeholder/final per docs/assets/README.md''s vocab), current counts. Update docs/assets/visual/README.md''s
category table counts once files exist (currently all show 0, which will stay accurate at initial creation
— do not invent placeholder rows).
ACCEPTANCE: 5 files exist, cross-linked from visual/README.md (already has the links — verify they resolve),
each with a real (possibly empty) status table matching the audio/README.md pattern.
DELIVERABLE (b) — glb-gen production path:
- Create client/assets/models/ with furniture/ and props/ subdirectories (mirroring the proven
spike layout at spikes/3dpipeline/models/{furniture,props}/) + .gitkeep in each.
- Define client/assets/models/manifest.json, schema mirroring docs/architecture/character-asset-organization.md''s
per-asset-entry pattern (that doc''s clothing/accessory entries are the closest precedent — a flat
per-item record, not per-body-type variants since environment props aren''t body-fitted). Minimum
fields: name, category (furniture|props), source_model (path under client/assets/models/), subdirectory,
status (planned/in-progress/placeholder/final — reuse the docs/assets/README.md vocab so catalog and
manifest never disagree). Promotion = repo-canonical per D-241 — an asset entering manifest.json AND
client/assets/models/ IS the promotion act; nothing stays "promoted" in spikes/.
- Update .claude/skills/glb-gen/SKILL.md — replace the "client/assets/models/ does not exist... that''s
a Phase-5 concern" paragraph (lines ~96-102) with the new path + a pointer to the manifest. NOTE:
this contradicts nothing already in D-244/T-750 — T-750''s own epic description lists furniture/props
production as Phase-4 parallel work, so populating the production path (not populating the world with
them — that''s Phase 5 per T-961) is in-scope now.
ACCEPTANCE: client/assets/models/{furniture,props}/.gitkeep exist; manifest.json schema documented
(either inline in SKILL.md or as a comment/example in the file itself, following the character
manifest''s convention); SKILL.md''s stale "does not exist" paragraph is corrected.
DELIVERABLE (c) — docs/assets/visual/conventions.md (NEW FILE — no prior art to amend):
- Model naming: <category>_<descriptor>.glb (e.g. table_baroque.glb, chair_modernist.glb) —
mirrors the spike''s existing baroque_table.glb/modernist_chair.glb pattern loosely; pick one
order and state it (category-first matches the character-asset clothing convention
<category>_<descriptor>; the spike used descriptor-first — this is a naming decision to make,
not silently pick — see lead note below on who rules it).
- Mask sidecar: <model>_mask.png, single-channel per the character-asset convention (Section 3
of character-asset-organization.md) UNLESS the item needs multi-region tinting, in which case follow
the RGBA channel-routed convention from that doc''s Section 11 (T-1089) rather than reinventing one.
- Texture naming keyed to D-235 tokens: PBR maps for building-exterior surfaces should name by
the ratified ObjectTag vocabulary (D-235 amendment, wiki/economics/object_tag_vocabulary.toml) —
e.g. concrete_wall_basecolor.png, pitched_roof_normal.png — NOT the old renderer/README.md
<model>_<direction>_<resolution>.png scheme, which is direction-indexed for the retired 4-direction
sprite render and does not apply to PBR texture maps at all (there is no "direction" for a texture,
only map type: basecolor/normal/roughness).
- Door state handling — DO NOT port a state-frame suffix convention. The 2026-06-16 draft proposed
<model>_open/<model>_closed GLB variants; this is sprite-era thinking (baked state = baked frame).
For a 3D object the door''s open/closed/locked/sealed state (T-979/T-998''s DoorSpec: Open/Closed/
TemporalWindow/Locked/Sealed) is a RUNTIME transform/animation on one door .glb, not separate baked
models — but the actual mechanism (AnimationPlayer swing vs. procedural hinge rotation vs. a
door-model convention with a named "open" bone) is a Phase-5 rendering decision (player control +
in-world rendering, per the cascade), not a Phase-4 asset-catalog concern. This ticket
should record ONLY the negative constraint (no state-frame suffix files) and defer the positive
mechanism to whichever Phase-5 ticket handles door rendering. Do not invent a runtime convention here.
- Godot TileSet resource organization — OUT OF SCOPE, removed from ticket. client/scripts/rendering/
tile_renderer.gd''s TileSet is an explicitly-labeled placeholder ("programmatic TileSet with placeholder
colored rectangles," D-014) and the cascade rule states current in-world rendering code is
"left as-is until Phase 5 — do not build on it or rip it out before then." A <tileset>/<tile_id>.tres
convention would be building on Phase-5-owned code from Phase 4. Do not author this section.
- Multi-tile footprint metadata: still relevant (a table/couch spans multiple sim tiles regardless
of sprite vs 3D) — keep this item, express as a manifest.json field (e.g. footprint_tiles: [w, h])
rather than a filename convention.
ACCEPTANCE: conventions.md exists, covers model naming / mask sidecars / D-235-keyed texture naming /
multi-tile footprint metadata; explicitly states the door-state and TileSet items are deferred (with
reasons) rather than silently omitted, so a future refinement pass doesn''t have to re-derive why.
SEQUENCING (unchanged from 2026-06-16 note): T-1053 (araminta briefing rewrite) softly depends on this
catalog. T-1049''s per-class stories reference these conventions — this ticket lands first / concurrently.
Lead note (batch-5 activation): the model-naming-order call (category-first vs descriptor-first) is
delegated to araminta (art seat) this batch — her ruling lands in conventions.md with a one-line
rationale; surfaced to Jeroen in the batch report for cheap veto. VERDICT: READY.', 'done', 'high', 'justine', 'visual', NULL, '2026-06-12 10:40:59', '2026-07-25 18:55:08.780', NULL, '6001e87cac297aa603a16cdf4724057a', 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 ('06FBPTXPV39JX54HYP83RZJFDM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Write the Phase-4 visual style bible into docs/assets/visual/palette.md', '(description follows in first append)
Refinement (batch-5 Si pass, 2026-07-25) — READY.
CURRENT STATE: docs/assets/visual/palette.md exists as a STUB (correctly re-scoped by D-244 — no
sprite-era content survives in it) — 4 bullets: camera angle (D-019, now superseded by D-148 for
gameplay — palette.md''s bullet is stale and must be corrected), entity colors (D-033), environmental
neutrality (D-045), "functional warmth." It explicitly defers to "art direction workshop synthesis and
first visual sprint" — this ticket IS that synthesis pass for Phase 4.
WHAT D-235 ALREADY FIXES (do not re-litigate): the building-exterior material/roof/facade/street
vocabulary (WallMaterial/RoofForm/FacadeRhythm/StreetSurface, ratified as the 28-template
architecture_trait_catalog.toml / wiki/economics/object_tag_vocabulary.toml) and the fallback-to-generic
mechanism. D-235 is the LOGICAL token layer ("what material is this wall") — Phase 4+ scope per its own
Implementation note ("textured render + the bulk of the theme library are Phase 5+"). T-1052 is the
RENDERED-LOOK layer on top: given a WallMaterial token, what does concrete_wall actually look like
(hue range, texture grain, weathering baseline) when art is eventually produced against it.
WHAT T-1052 MUST STILL AUTHOR (net-new, no existing decision covers this):
- Correct the camera-angle bullet: D-148 (30 deg low-angle Camera3D, supersedes D-019) is the GAMEPLAY
camera; D-019''s -72.5 deg survives only for the offline renderer (2D-artwork / retired sprite mode,
per renderer/README.md). palette.md''s current bullet conflates these — fix it to name both and
which applies where.
- A concrete color/material register for the D-235 token vocabulary: not new tokens, but the RANGE
each token maps to (e.g. "stone_wall reads as warm grey-tan, not cool grey" / "corrugated_metal
weathers to rust-orange, not staying silver") — the thing D-235 explicitly parked for Phase 5+ art
production but which a style bible can pre-specify so future asset generation has a target.
- Reconcile with docs/design/visual-grammar-v01.md and docs/workshops/art-direction-mood-board/
workshop-outcomes.md (both pre-cascade, v0.1-era, sprite-framed) — extract what survives (color
relationships, "functional warmth" identity, D-033 palette) vs what''s superseded by D-235/D-244
(era-based material ladder — explicitly retired by D-235''s own text: "Era is NOT a material filter
here"). Do not leave two contradictory style documents live without a note in one pointing at the
other''s supersession.
- Texture "grain"/rendering treatment for the D-244 3D-object reality: since objects are lit 3D meshes
now (not flat sprite fills), the style bible should say something about the toon/PBR balance —
precedent exists in client/assets/characters/shaders/ (toon.gdshader, toon_masked.gdshader,
outline.gdshader) and the glb-gen postprocess convention (roughness=1.0/specular=0.0 "for toon
compatibility" per .claude/skills/glb-gen/SKILL.md''s setup_materials() note). Confirm whether
environment props share this toon treatment with characters, or diverge — this is a real open
question the style bible should answer, not assume.
ACCEPTANCE: docs/assets/visual/palette.md is no longer a stub; camera-angle bullet corrected;
color/material register for D-235 tokens documented; toon-vs-PBR treatment for props stated;
supersession relationship to visual-grammar-v01.md / workshop-outcomes.md is explicit (either by
archiving/marking those superseded, or by palette.md stating exactly what it inherits from each).
Lead note (batch-5 activation): the toon-vs-PBR treatment call for environment props is delegated to
araminta (art seat) this batch — her ruling lands in palette.md with rationale; surfaced to Jeroen in
the batch report for cheap veto. VERDICT: READY.', 'done', 'medium', 'araminta', 'visual', 'D-235', '2026-06-12 10:40:59', '2026-07-25 18:55:08.787', NULL, '0c2f33e6e2b24c61cff46c61d2df9a8f', 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 ('06FBPTXPE8FR7TGEM7X6QVPV4M', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Refine T-961 into per-asset-class stories, each gated on a written brief', '(description follows in first append)
Filed 2026-06-12 from the fable-ous.md audit (S-43; citations adversarially verified).
T-961 (two-sentence description, no acceptance criteria, no brief links) is the only asset-production ticket under T-750, while the epic names eight classes; cars, barns, TVs, billboards appear in NO ticket at all (verified against the full corpus). Refine into child stories per class — station walls, rural walls, doors, floors, furniture/props, street furniture (lamp posts), signage/screens (billboards/TVs), rural structures (barns) — each gated on a one-page brief; explicitly record which classes are deferred (cars: see Q-067 audit note). Decide Q-119 (generic-parent placeholder set as first milestone) before sequencing.
Audit per-class status matrix (verified 2026-06-12):
# docs/assets/ survey (13 files + downloads/)
| File | One-line summary |
|---|---|
| README.md | Master index: audio/visual/video pipelines, status vocabulary (planned/in-progress/placeholder/final), structure that promises visual/{sprites,furniture,tilesets,icons,effects}.md |
| _templates/audio.md | Row + detailed-entry template for audio assets |
| _templates/sprite.md | Row + detailed-entry template for sprites (ID prefixes SPR/FUR/TILE/ICO/FX, status, method, directions columns) |
| _templates/video.md | Row + detailed-entry template for video |
| audio/README.md | ACTIVE production-status catalog — per-category counts table (12 assets tracked), generation tools, bus architecture |
| audio/palette.md | Sonic style bible |
| audio/ambient.md / sfx.md / ui.md | Per-category audio asset tables |
| visual/README.md | STUB — links 5 category files that do not exist, all counts 0 |
| visual/palette.md | STUB — 4 bullet decisions (D-019/D-033/D-045, "functional warmth"), defers to art-direction workshop |
| video/README.md / palette.md | Stubs |
| downloads/ | Purchased character sources (Quaternius Universal Base Characters, Modular Character Outfits FBX) — characters only |
# Per-asset-class matrix
| Class | Written brief | Style guide | Naming/res conventions | Gen path | Produced assets | Tickets |
|---|---|---|---|---|---|---|
| Walls (station) | art-direction workshop-outcomes.md §3.3 (v0.1.1 set, pre-cascade) + docs/design/placeholder-art-spec-v01.md §2.1 | visual-grammar-v01.md §1/§4.3, workshop-outcomes §1.8 era palette (partly superseded by D-235), D-235 WallMaterial vocab | renderer/README.md:80-95 (`<model>_<dir>_<res>.png`, 1024→256→64 chain) | working — renderer/models/wall_structural.tscn rendered | wall_structural ×4 dirs in client/assets/sprites/; wall_bar_green model unrendered; 2 textures | T-961 (backlog), T-988 (token vocab, backlog) |
| Walls (rural) | none found | D-235 tokens only (timber/rammed-earth etc.) | renderer/README.md (generic) | same path, unused | none | T-961 text mention only |
| Doors | placeholder-art-spec-v01.md §2.1 (procedural rects, v0.1) + workshop-outcomes §3.3 `door_sliding` | none for generated world | none for open/closed state frames | same path, unused | none | T-979/T-998 (DoorSpec — logical only), T-961 |
| Floors | placeholder-art-spec-v01.md §2.1 + workshop-outcomes §3.3 (4 floor sprites) | D-228 FloorMaterial axis (open vocab) | 64px visual tile (placeholder-art-spec §1) | same path, unused | none — client/assets/tilesets/ is .gitkeep only | T-959 (tile fill, logical), T-961 |
| Styled buildings | D-232 + D-235 + wiki/economics/architecture_trait_catalog.toml (T-1005 done, ~35 visual bundles) | D-235 material/color-register grammar — tokens only, no rendered art direction | token→fallback-parent chain (catalog header :34-44) | no model/texture path defined for building exteriors | none | T-977, T-988, T-994, T-1004 (all backlog), T-1005 (done) |
| Furniture | docs/assets/visual/furniture.md MISSING (linked); workshop-outcomes §3.3 lists 8-10 objects (pre-cascade) | era table §1.8 only | character-asset-organization.md explicitly excludes furniture ("follow Trellis spike conventions") | proven in spike | spikes/3dpipeline/models/furniture/ ×3 GLB (unpromoted); zero in client/assets | T-961, T-995 (ObjectTag vocab, backlog) |
| Items | docs/design/icon-set-v01.md (HUD/inventory icons only) | D-086 icon constraints | SVG icon_ prefix (client/assets/icons/) | n/a for icons | 13 SVGs in client/assets/icons/ | none for in-world item sprites |
| Cars/vehicles | none found | none | none | spike only | spikes/3dpipeline/models/props/vw_beetle.glb (spike) | none — Q-067 open ("driveable vs NPC traffic?") |
| Lamp posts | none found (zero repo hits) | none | none | unused | none | T-961 text mention only |
| Barns | none found (only T-750 epic text) | none | none | unused | none | none |
| TVs | none found (only T-750 epic text) | none | none | unused | none | none |
| Billboards | none found (only T-750 epic text; other hits = LOD impostors) | none | none | unused | none | none |
| Characters (reference class — the working model) | docs/architecture/character-asset-organization.md + docs/design/character-visuals-spec.md | yes | full (manifest.json, seg_ naming, body-type keys) | proven end-to-end | 1002 files in client/assets/characters/ | done |
# Generation path (verified files)
image-gen (.claude/skills/image-gen/SKILL.md + scripts/image_connector.py, Gemini) → glb-gen (SKILL.md + tooling/db/trellis_connector.py + tooling/trellis-batch.sh + Blender postprocess scripts) → sprite-gen (SKILL.md + scripts/render.sh) → renderer/ (project.godot, render_scene.tscn, render_export.gd; D-019 camera, 3-light rig, 1024/256/64 chain) → client/assets/sprites/. renderer/output/ holds only .gdignore (intermediates gitignored).
# Produced assets in client/assets (non-.import files)
audio 16 · characters 1002 · fonts 3 · icons 13 · planets 7 · sprites 9 (npc_generic ×4, wall_structural ×4, .gitkeep) · theme 1 · tilesets 1 (.gitkeep only)
---
Refinement (2026-06-16, /whats-next Si pass) — READY. This is a PLANNING task: break T-961 into per-class child stories.
CLASS LIST (8 stories, parented under T-961, each gated on a one-page brief): station walls, rural walls, doors, floors, furniture/props, street furniture (lamp posts), signage/screens (TVs/billboards), rural structures (barns).
- CARS (9th class named in T-750): DEFERRED — Q-067 needs a phase decision (Phase-4 deliverable vs Phase-5+) before any car story. File a deferral note, not a story.
- Q-119 (generic-parent placeholder set as milestone-0): NOT a blocker — sequencing only. On each relevant story note that the generic parents (generic_wall/roof/facade/street + generic_door/floor/furniture; D-235 / wiki/economics/architecture_trait_catalog.toml:33-41) are milestone-0 IF Q-119 resolves "yes".
BRIEF TEMPLATE: none exists. Create docs/assets/_templates/asset-brief.md (scope · visual reference · naming/resolution conventions · generation path · acceptance), or adopt docs/_templates/design.md. Each story states where its brief + catalog file live under docs/assets/visual/.
READ before filing: docs/assets/README.md, docs/design/{placeholder-art-spec-v01,visual-grammar-v01}.md, docs/workshops/art-direction-mood-board/workshop-outcomes.md, D-235/D-228/D-232, docs/architecture/character-asset-organization.md (the reference model).
NOTE: T-1050 (sibling) owns the catalog + naming conventions these stories reference — sequence T-1049 to consume T-1050''s conventions where they overlap.
---
HELD 2026-06-16 (separated from the cascade batch; needs re-scoping before resuming).
The asset pipeline this ticket assumes is sprite-centric (docs/assets/visual/sprites.md
"Entities", _templates/sprite.md, SPR/FUR/TILE/ICO/FX prefixes, the /sprite-gen
render-3D->2D-sprites step). Direction correction: we are NOT producing sprites — Trellis
produces 3D objects (.glb) displayed in-world; the only flattened 2D content is TEXTURES.
Re-scope this ticket around a 3D-model + texture catalog (not a sprite catalog) before
activating. See the sprite-vs-3D root-cause note (this session).
Refinement (batch-6 Si pass, 2026-07-25) — SUPERSEDES the 2026-06-16 READY block
(sprite-era, invalidated by D-244) and the 2026-06-16 HELD note. T-1050 (catalog +
conventions.md + manifest.json) and T-1052 (palette.md style bible) are both DONE
(PR #211, merged 2026-07-25) — the conventions this ticket needed to consume now
exist and are read, not hypothetical.
SCOPE, 3D REALITY: T-961 breaks into per-asset-class stories, each gated on a
one-page brief. A "written brief" is now a short doc that states, per class:
(1) scope — which manifest category (furniture|props) and which D-235 ObjectTag
axis if any; (2) visual reference — cites palette.md §3''s hue/grain/weathering
register for the relevant token(s), or palette.md §2 toon/PBR default for classes
with no D-235 token; (3) naming — conventions.md §1 model naming
(<category>_<descriptor>.glb), §2 mask sidecar if tintable, §4 footprint_tiles if
multi-tile; (4) generation path — /image-gen concept → /glb-gen (Trellis) →
postprocess → promotion into client/assets/models/{furniture,props}/ +
manifest.json entry (conventions.md, models.md); (5) acceptance — N assets at
"planned" status registered in the manifest with a real manifest.json entry,
status flips as production proceeds (models.md''s shared status vocab).
BRIEF TEMPLATE (create docs/assets/_templates/asset-brief.md): the five headers
above (scope / visual reference / naming / gen path / acceptance), one page,
adopting docs/_templates/design.md frontmatter. Do NOT reuse _templates/sprite.md
(SPR/FUR/TILE/ICO/FX prefixes) — explicitly retired by D-244/T-1050.
CLASS LIST — station walls, rural walls, doors, floors, furniture/props already
have partial prior art (station walls: wall_structural GLB exists unrendered per
the 2026-06-12 audit matrix; doors: DoorSpec logical model exists, T-979/T-998;
floors: D-228 FloorMaterial axis exists) — each gets a real story + brief, citing
its relevant D-235 axis (wall/roof/facade/street) or, for doors/floors/furniture,
the non-ObjectTag conventions (mask sidecars, footprint_tiles).
STREET FURNITURE / SIGNAGE-SCREENS / RURAL STRUCTURES (lamp posts, barns,
TVs, billboards — T-1051''s four classes): RECOMMEND FORMAL DEFERRAL, not real
briefs, this batch. Rationale: Phase 4''s deliverable is "deterministic
walkable-world data + per-layer Atlas maps + asset catalog" (cascade table) —
outside-in generation (D-166) needs building EXTERIOR templates
(D-235 wall/roof/facade/street axes) to make districts/blocks walkable and
Atlas-legible. Lamp posts (street furniture), TVs/billboards (signage/screens
placed as interior or exterior set-dressing), and barns (a rural building
*type*, not a wall/roof/facade axis token) are furniture-tier detail objects
that populate a walkable exterior once it exists — they read as Phase 6
"detail coloring / room-level population" (cascade table) or, at best, late
Phase 4 polish, not blocking asset classes for getting the world walkable.
Zero repo hits exist for any of the four beyond epic text (T-1049''s own 2026-06-12
audit matrix, unchanged) — there is no partial work to protect by deferring.
Treat identically to Q-067''s car deferral: file a deferral note on the story
(or fold into one placeholder story "T-1051 classes — deferred pending Phase 4
exterior-generation completion"), do not write real briefs now. Distinguish from
cars only in degree, not in kind — Q-067 is a still-open decision question
(vehicle simulation is a bigger unresolved design surface); the T-1051 four are
simpler set-dressing whose deferral needs no open question, just a scope note,
because nothing about their eventual brief depends on an undecided mechanic.
Q-119 (generic-parent placeholder set as milestone-0) is STILL OPEN (unresolved
as of this pass) — unchanged from the prior block: not a blocker, sequencing-only.
Each relevant story notes the generic_wall/roof/facade/street (+ generic
door/floor/furniture) parents as milestone-0 IF Q-119 resolves yes.
READ before filing: docs/assets/visual/{conventions,palette,models,textures,
artwork}.md (T-1050/T-1052, merged), D-244, D-257, D-235 (ratified token list),
D-243 (footprint unit), docs/architecture/character-asset-organization.md
(mask/manifest precedent).
VERDICT: READY (activation = batch 6; the T-1051 deferral recommendation above is
ratified at activation, then T-1051 is updated to match the ruling this ticket records).', 'backlog', 'high', NULL, 'visual', NULL, '2026-06-12 10:40:58', '2026-07-25 19:00:33.468', NULL, 'e7ab3b683997f5e8bf5e80ab8d270840', 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 ('06FBPTXPZ0ZTM1QQXD5K972CMM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Rewrite docs/briefings/araminta.md around the Phase-4 asset pipeline', '(description follows in first append)
Refinement (batch-6 Si pass, 2026-07-25) — READY.
WHAT''S WRONG WITH THE CURRENT BRIEFING: frozen at the 2026-03-13 v0.2 pivot.
(1) Frames the role around "v0.2 Pivot — Key Changes" — v0.2 is formally dropped
("No scoping negotiations. Build the base systems fully"), so the framing section
is obsolete, not just outdated. (2) Assumes the sprite-era pipeline throughout
("Phase 1-3 placeholder art pipeline," "sprite replacements" in Phase 4) —
contradicted by D-244 (3D objects in-world, no per-object sprite layer) and D-149
(live 3D characters). (3) Cites D-019 as simply "top-down art style" with no note
that D-148 superseded it for gameplay (palette.md §1 had to correct exactly this
conflation in its own stub — the briefing repeats the same error). (4) Zone visual
grammar / tile palette framing predates D-235''s ObjectTag vocabulary and D-232''s
trait-template catalog entirely. (5) Character customisation section (D-126,
Q-WTF-039/040) predates the shipped D-159D-164 body-type + slot-compositing
system and the wardrobe/garment-token pipeline — describes a design question that
has since been built. (6) No mention of the Atlas at all (D-255 stepped ladder,
D-169/D-170 implant UI) despite araminta being a named co-designer of D-255''s
named-feature encoding/payload schema/lakes work.
WHAT THE REWRITE MUST CONTAIN:
- Role framing: visual art direction for the Phase-4 asset pipeline + Atlas
map-art rendering (not "generator spike prerequisites").
- palette.md (T-1052, merged) as the SINGLE LIVE STYLE AUTHORITY — point to it,
do not restate camera/color/material content (avoids a second drifting copy;
visual-grammar-v01.md / workshop-outcomes.md supersession is palette.md §5''s
to state, the briefing defers).
- conventions.md (T-1050, merged) for naming/mask/manifest mechanics — note the
category-first naming ruling (conventions.md §1) and the D-257 toon/PBR ruling
with glazing carve-out as HER standing rulings she''ll be asked to extend.
- D-235 ObjectTag vocabulary as the logical layer she renders a look on top of —
co-maintained with Miri per D-235''s own text.
- The character wardrobe pipeline (character-asset-organization.md) as the proven
reference model conventions.md imitates (mask sidecars, manifest schema).
- D-255 stepped Atlas as active/ongoing work she co-designed.
- Current open items: T-1049 (per-class asset stories, re-scoped this batch), the
D-257 glazing shader implementation (unassigned per palette.md §2.3), Q-119
(generic-parent milestone question, hers to weigh in on per D-235
co-maintenance).
- Drop: v0.2 pivot section, Q-WTF-039/040, tycoon-bookmark/apartment-wakeup
framing, and the v0.2-era decision list (D-114/D-117/D-119/D-122/D-128/D-131/
D-135) — verify each against current decision status before dropping outright;
a few (D-033 entity color, D-126 customisation dimensions) may still have live
sub-parts even though the surrounding section is obsolete.
ACCEPTANCE: rewritten briefing reflects the current Phase-4 landscape (D-235,
D-244, D-257, D-255), points to palette.md/conventions.md as live authorities
rather than duplicating content, drops v0.2-pivot and superseded-question
framing, lists araminta''s actual current/open tickets.
SEQUENCING: independent of T-1049 (different files, no shared input) — both can
run in the same batch.', 'backlog', 'medium', NULL, 'visual', NULL, '2026-06-12 10:40:59', '2026-07-25 19:00:36.463', NULL, '6080da556144a938b435764dd8ed6a18', 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 ('06FSM3MQ5GFSPKKFJF6GGZN8CM', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Alpine relief capture — Quarter-vs-District differentiation on a tectonics:high body', 'Araminta''s PR #192 companion note, kept open by her PR #208 ruling (option b): the Quarter-vs-District relief-differentiation check was accepted for the T-1160 merge on a plains/river substitution (GJ380c world center -17723392,-10532864 — Quarter clearly shows finer higher-frequency texture than District), but plains texture and jagged tectonic relief are different visual regimes and the sharper case still needs its own capture. Task: identify the body pool with tectonics:high (or planet_class values mapping to TectonicClass::Volcanic/Active) and RECORD the pool in this ticket first — the PR #208 eyeball proved GJ380c/GJ820Bc are both tectonics:low via exhaustive two-Global color scans, don''t re-run that search blind — then capture quarter-relief vs district-relief at a true Alpine/MountainPass crop (MorphologyZone 13/14) at the same world center, matched crop, and have Araminta judge the differentiation. Reuse the pr208_driver fixed-center --step protocol (T-1157 harness inventory). Related: T-1160, PR #192, PR #208.
POOL RECORDED (2026-07-25, read-only survey per this ticket''s own first step —
do not re-run the search blind).
MAPPING MECHANISM: server/src/atlas/district_profile.rs:296-312
derive_tectonic_class(): BodyParams::tectonic_activity authored override first
("volcanic"/"active"/"tidally_forced", NO column in current systems.db schema and
no authored overrides in the wiki TOMLs — so today the override path is dead),
else planet_class fallback: "volcanic" → Volcanic; "oceanic"/"ocean_world" →
Active; everything else → Stable. Sanity anchors agree with the PR #208 eyeball:
GJ380c (arid) and GJ820Bc (arid) both → Stable.
THE FULL POOL (7 bodies):
- Volcanic: GJ0c (Venus, no heightmap), GJ0f-1 (Io, no heightmap), GJ4056b
(heightmap, airless, relief_energy_q=1.0), GJ660Ad (Seongho Prime, heightmap,
thin atmosphere, relief_energy_q=1.0)
- Active: GJ1002b (Caparica, heightmap, island world, standard atmosphere),
GJ103c (Vlieland, heightmap, island world, standard atmosphere), GJ447c
(Marevna, heightmap, ocean world) — all relief_energy_q=0.70 per D-239 §5.
CAPTURE RECOMMENDATION (lead): for the true Alpine/MountainPass (MorphologyZone
13/14) crop this ticket wants, the Active terrestrial island worlds GJ1002b and
GJ103c are the primary candidates (coastal orogeny, standard climate — closest
analogue to the GJ380c plains/river substitution being replaced). The Volcanic
pair (GJ4056b, GJ660Ad) are alternates with maximum relief energy but
airless/thin atmospheres — shield/lava-field morphology, likely NOT
Alpine-classed; use only if the Active pair fails to produce MorphologyZone
13/14 crops. GJ0c/GJ0f-1 lack terrain_reference and cannot open in the Atlas.', 'ready', 'low', NULL, 'client', NULL, '2026-07-25 16:14:35.308', '2026-07-25 19:00:39.730', NULL, 'c56c7d2a7a030ee4e959478459ee3d12', 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 ('06FRJD6EKPBQE08NA8VHP692W8', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Redesign the atlas visual-capture goldens for the zoom ladder (visual_scenarios/visual_capture)', 'T-1153 broke the T-1120/T-1121 golden capture harness: client/tests/visual_scenarios.gd and visual_capture.gd call app._regional_screen._viewer.get_heightmap_texture()/show_body() — both crash against AtlasWindowViewer (regional_screen now wraps the ladder viewer; AtlasViewer is unreachable from nav). Confirmed NOT wired into tests/run-godot or the pre-push hook (no reference in either), so nothing blocks, but the 12 curated atlas_* goldens and the --atlas-matrix capture path are dead until redesigned for the ladder (capture at defined rungs/zooms instead of the fixed heightmap frame; the new canonical orbital frame is the natural replacement for the old whole-body shot). Also decide the fate of the retired AtlasViewer code these harnesses were the last live consumers of — likely deletable once T-1156 (overlay port) settles what carries forward.
Live round 4 addendum: client/tests/test_atlas_window_overlay_draw_smoke.gd (3 scenarios -> now 2 after removing a flaky harness-sanity check: single-window draw + tile-mosaic draw, both asserting real composited pixels via SubViewport render) is a standalone gdUnit file with its own headless-skip guard, added because this redesign hadn''t landed yet. When this ticket lands, migrate its 2 scenarios into the redesigned tests/visual.json harness (folding the standalone file away) for consistency with the rest of the real-rendering test suite — see docs/DEVOPS.md''s ''Real-rendering test exception'' note for the interim rationale.
RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): this becomes the capture harness for the STEPPED render architecture — goldens key on (body, step) canvases, not the retired continuous-zoom frames. The T-1152/T-1153 test suites (coverage-walk, continuous-zoom seams, compute_tile_grid mosaic) retire with their code in T-1182''s change; this re-scoped harness is their replacement and the eyeball-check substrate for every step canvas. More needed than ever — keep, don''t cancel.
PR #203 fix-round inventory (2026-07-25): the retirement is now TOTAL — AtlasViewer and its whole cluster (atlas_viewer, atlas_marker_overlay, atlas_descend_geometry, atlas_legend_panel, atlas_generation_proxy, atlas_generation_state) are deleted. visual_scenarios.gd (_setup_atlas_golden_shot, 6 of 12 atlas_GJ* z2_0/z4_0 non-fit scenarios) and visual_capture.gd (_run_atlas_matrix) now call StepCanvasViewer methods that do not exist: get_heightmap_texture(), set_view(zoom, offset), get_view_offset() — the fit-zoom shots early-return before hitting them. Ruled in PR #203: NO shim (would resurrect the retired continuous-zoom API D-255 kills); the redesign here keys captures on (body, rung) via StepCanvasViewer''s real surface (enter/_scroll_rung/rung state + the echoed extent). get_overlay_defs()/set_overlay_visible() duck-type fine and survive. Until this lands, those 6 goldens + atlas_matrix are knowingly dead (manual make targets only, gate-invisible — confirmed again).
Harness techniques inventory addition (from the PR #204 / T-1183 restart-persistence eyeball, 2026-07-25) — proven live-rendering eyeball patterns for shared-desktop sessions:
1. InputSwallower pattern: root-level Node (added first, low process_priority), _input()+_unhandled_input() both call get_viewport().set_input_as_handled() unconditionally — neutralizes real desktop input reaching a `-s` SceneTree driver''s real window (X11/XWayland delivers pointer events to unfocused windows under the cursor). Verify via a per-capture-window intercept counter: N>0 intercepted AND zero resulting view drift is proof; 0 intercepted proves nothing.
2. In-process navigation via viewer methods, not OS input: call the same methods the app''s own gdUnit suite exercises (StepCanvasViewer.enter()/_scroll_rung(); RegionalScreen.enter() atlas-matrix precedent in visual_capture.gd) — identical production code path, no OS injection needed (Wayland/KWin blocks synthetic X11 injection for XWayland clients entirely).
3. View-transform logging at every capture: world_center, view_offset, the drawing Node2D''s canvas position, footprint_px alongside every screenshot — turns "pixels don''t match" into a root-causable mechanism (PR #204: drift matched PAN_SPEED_PX_S*frame_time*held-frames to 0.02px, identifying unfocused edge-scroll bleed, now T-1191).
4. Full-run-restart-on-anomaly, never partial retry: any mid-sequence assertion failure discards the whole run (cold-wipe for a cold-role attempt; fresh-process-only for a revisit-role attempt) — partial retries silently corrupt the state the comparison depends on.
5. Fixed-center revisit mode: record a prior run''s actual derived centers and re-request them literally on the comparison run, bypassing cursor-anchored re-derivation — required when run 2 must land on run 1''s exact cache keys, not merely repeat the gesture.
Harness-discipline gap found 2026-07-25 (Jeroen spotted a live orphan): agent-side kill-on-completion does not cover an agent CRASHING mid-capture (API error killed a session between server spawn and its own cleanup step — the server ran on unnoticed for ~25 min on the shared machine). Inventory addition: (6) the LEAD runs an explicit orphan sweep (ps for godot4 + settled-reach-server) at every cycle checkpoint — commit, merge, and after any agent dies abnormally — not only inside agent protocols; pr-process step 1a''s >5-min gdUnit grep does not match live-capture servers, so the sweep needs the server binary name too.', 'review', 'medium', 'hoshe', 'client', NULL, '2026-07-22 09:42:47.197', '2026-07-25 20:13:40.919', NULL, '14425bb157c10e8cd85ef57d16270b0e', 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 ('06FRJD6EKPBQE08NA8VHP692W8', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Redesign the atlas visual-capture goldens for the zoom ladder (visual_scenarios/visual_capture)', 'T-1153 broke the T-1120/T-1121 golden capture harness: client/tests/visual_scenarios.gd and visual_capture.gd call app._regional_screen._viewer.get_heightmap_texture()/show_body() — both crash against AtlasWindowViewer (regional_screen now wraps the ladder viewer; AtlasViewer is unreachable from nav). Confirmed NOT wired into tests/run-godot or the pre-push hook (no reference in either), so nothing blocks, but the 12 curated atlas_* goldens and the --atlas-matrix capture path are dead until redesigned for the ladder (capture at defined rungs/zooms instead of the fixed heightmap frame; the new canonical orbital frame is the natural replacement for the old whole-body shot). Also decide the fate of the retired AtlasViewer code these harnesses were the last live consumers of — likely deletable once T-1156 (overlay port) settles what carries forward.
Live round 4 addendum: client/tests/test_atlas_window_overlay_draw_smoke.gd (3 scenarios -> now 2 after removing a flaky harness-sanity check: single-window draw + tile-mosaic draw, both asserting real composited pixels via SubViewport render) is a standalone gdUnit file with its own headless-skip guard, added because this redesign hadn''t landed yet. When this ticket lands, migrate its 2 scenarios into the redesigned tests/visual.json harness (folding the standalone file away) for consistency with the rest of the real-rendering test suite — see docs/DEVOPS.md''s ''Real-rendering test exception'' note for the interim rationale.
RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): this becomes the capture harness for the STEPPED render architecture — goldens key on (body, step) canvases, not the retired continuous-zoom frames. The T-1152/T-1153 test suites (coverage-walk, continuous-zoom seams, compute_tile_grid mosaic) retire with their code in T-1182''s change; this re-scoped harness is their replacement and the eyeball-check substrate for every step canvas. More needed than ever — keep, don''t cancel.
PR #203 fix-round inventory (2026-07-25): the retirement is now TOTAL — AtlasViewer and its whole cluster (atlas_viewer, atlas_marker_overlay, atlas_descend_geometry, atlas_legend_panel, atlas_generation_proxy, atlas_generation_state) are deleted. visual_scenarios.gd (_setup_atlas_golden_shot, 6 of 12 atlas_GJ* z2_0/z4_0 non-fit scenarios) and visual_capture.gd (_run_atlas_matrix) now call StepCanvasViewer methods that do not exist: get_heightmap_texture(), set_view(zoom, offset), get_view_offset() — the fit-zoom shots early-return before hitting them. Ruled in PR #203: NO shim (would resurrect the retired continuous-zoom API D-255 kills); the redesign here keys captures on (body, rung) via StepCanvasViewer''s real surface (enter/_scroll_rung/rung state + the echoed extent). get_overlay_defs()/set_overlay_visible() duck-type fine and survive. Until this lands, those 6 goldens + atlas_matrix are knowingly dead (manual make targets only, gate-invisible — confirmed again).
Harness techniques inventory addition (from the PR #204 / T-1183 restart-persistence eyeball, 2026-07-25) — proven live-rendering eyeball patterns for shared-desktop sessions:
1. InputSwallower pattern: root-level Node (added first, low process_priority), _input()+_unhandled_input() both call get_viewport().set_input_as_handled() unconditionally — neutralizes real desktop input reaching a `-s` SceneTree driver''s real window (X11/XWayland delivers pointer events to unfocused windows under the cursor). Verify via a per-capture-window intercept counter: N>0 intercepted AND zero resulting view drift is proof; 0 intercepted proves nothing.
2. In-process navigation via viewer methods, not OS input: call the same methods the app''s own gdUnit suite exercises (StepCanvasViewer.enter()/_scroll_rung(); RegionalScreen.enter() atlas-matrix precedent in visual_capture.gd) — identical production code path, no OS injection needed (Wayland/KWin blocks synthetic X11 injection for XWayland clients entirely).
3. View-transform logging at every capture: world_center, view_offset, the drawing Node2D''s canvas position, footprint_px alongside every screenshot — turns "pixels don''t match" into a root-causable mechanism (PR #204: drift matched PAN_SPEED_PX_S*frame_time*held-frames to 0.02px, identifying unfocused edge-scroll bleed, now T-1191).
4. Full-run-restart-on-anomaly, never partial retry: any mid-sequence assertion failure discards the whole run (cold-wipe for a cold-role attempt; fresh-process-only for a revisit-role attempt) — partial retries silently corrupt the state the comparison depends on.
5. Fixed-center revisit mode: record a prior run''s actual derived centers and re-request them literally on the comparison run, bypassing cursor-anchored re-derivation — required when run 2 must land on run 1''s exact cache keys, not merely repeat the gesture.
Harness-discipline gap found 2026-07-25 (Jeroen spotted a live orphan): agent-side kill-on-completion does not cover an agent CRASHING mid-capture (API error killed a session between server spawn and its own cleanup step — the server ran on unnoticed for ~25 min on the shared machine). Inventory addition: (6) the LEAD runs an explicit orphan sweep (ps for godot4 + settled-reach-server) at every cycle checkpoint — commit, merge, and after any agent dies abnormally — not only inside agent protocols; pr-process step 1a''s >5-min gdUnit grep does not match live-capture servers, so the sweep needs the server binary name too.', 'done', 'medium', 'hoshe', 'client', NULL, '2026-07-22 09:42:47.197', '2026-07-25 20:38:51.996', NULL, '06065a380c0f7dfe6504ba5a8f7e9ffa', 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 ('06FSM3MQ5GFSPKKFJF6GGZN8CM', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Alpine relief capture — Quarter-vs-District differentiation on a tectonics:high body', 'Araminta''s PR #192 companion note, kept open by her PR #208 ruling (option b): the Quarter-vs-District relief-differentiation check was accepted for the T-1160 merge on a plains/river substitution (GJ380c world center -17723392,-10532864 — Quarter clearly shows finer higher-frequency texture than District), but plains texture and jagged tectonic relief are different visual regimes and the sharper case still needs its own capture. Task: identify the body pool with tectonics:high (or planet_class values mapping to TectonicClass::Volcanic/Active) and RECORD the pool in this ticket first — the PR #208 eyeball proved GJ380c/GJ820Bc are both tectonics:low via exhaustive two-Global color scans, don''t re-run that search blind — then capture quarter-relief vs district-relief at a true Alpine/MountainPass crop (MorphologyZone 13/14) at the same world center, matched crop, and have Araminta judge the differentiation. Reuse the pr208_driver fixed-center --step protocol (T-1157 harness inventory). Related: T-1160, PR #192, PR #208.
POOL RECORDED (2026-07-25, read-only survey per this ticket''s own first step —
do not re-run the search blind).
MAPPING MECHANISM: server/src/atlas/district_profile.rs:296-312
derive_tectonic_class(): BodyParams::tectonic_activity authored override first
("volcanic"/"active"/"tidally_forced", NO column in current systems.db schema and
no authored overrides in the wiki TOMLs — so today the override path is dead),
else planet_class fallback: "volcanic" → Volcanic; "oceanic"/"ocean_world" →
Active; everything else → Stable. Sanity anchors agree with the PR #208 eyeball:
GJ380c (arid) and GJ820Bc (arid) both → Stable.
THE FULL POOL (7 bodies):
- Volcanic: GJ0c (Venus, no heightmap), GJ0f-1 (Io, no heightmap), GJ4056b
(heightmap, airless, relief_energy_q=1.0), GJ660Ad (Seongho Prime, heightmap,
thin atmosphere, relief_energy_q=1.0)
- Active: GJ1002b (Caparica, heightmap, island world, standard atmosphere),
GJ103c (Vlieland, heightmap, island world, standard atmosphere), GJ447c
(Marevna, heightmap, ocean world) — all relief_energy_q=0.70 per D-239 §5.
CAPTURE RECOMMENDATION (lead): for the true Alpine/MountainPass (MorphologyZone
13/14) crop this ticket wants, the Active terrestrial island worlds GJ1002b and
GJ103c are the primary candidates (coastal orogeny, standard climate — closest
analogue to the GJ380c plains/river substitution being replaced). The Volcanic
pair (GJ4056b, GJ660Ad) are alternates with maximum relief energy but
airless/thin atmospheres — shield/lava-field morphology, likely NOT
Alpine-classed; use only if the Active pair fails to produce MorphologyZone
13/14 crops. GJ0c/GJ0f-1 lack terrain_reference and cannot open in the Atlas.', 'in_progress', 'low', NULL, 'client', NULL, '2026-07-25 16:14:35.308', '2026-07-25 20:42:13.772', NULL, 'ba6b11721950646bce2301ad7cfce186', 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 ('06FSM3MQ5GFSPKKFJF6GGZN8CM', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Alpine relief capture — Quarter-vs-District differentiation on a tectonics:high body', 'Araminta''s PR #192 companion note, kept open by her PR #208 ruling (option b): the Quarter-vs-District relief-differentiation check was accepted for the T-1160 merge on a plains/river substitution (GJ380c world center -17723392,-10532864 — Quarter clearly shows finer higher-frequency texture than District), but plains texture and jagged tectonic relief are different visual regimes and the sharper case still needs its own capture. Task: identify the body pool with tectonics:high (or planet_class values mapping to TectonicClass::Volcanic/Active) and RECORD the pool in this ticket first — the PR #208 eyeball proved GJ380c/GJ820Bc are both tectonics:low via exhaustive two-Global color scans, don''t re-run that search blind — then capture quarter-relief vs district-relief at a true Alpine/MountainPass crop (MorphologyZone 13/14) at the same world center, matched crop, and have Araminta judge the differentiation. Reuse the pr208_driver fixed-center --step protocol (T-1157 harness inventory). Related: T-1160, PR #192, PR #208.
POOL RECORDED (2026-07-25, read-only survey per this ticket''s own first step —
do not re-run the search blind).
MAPPING MECHANISM: server/src/atlas/district_profile.rs:296-312
derive_tectonic_class(): BodyParams::tectonic_activity authored override first
("volcanic"/"active"/"tidally_forced", NO column in current systems.db schema and
no authored overrides in the wiki TOMLs — so today the override path is dead),
else planet_class fallback: "volcanic" → Volcanic; "oceanic"/"ocean_world" →
Active; everything else → Stable. Sanity anchors agree with the PR #208 eyeball:
GJ380c (arid) and GJ820Bc (arid) both → Stable.
THE FULL POOL (7 bodies):
- Volcanic: GJ0c (Venus, no heightmap), GJ0f-1 (Io, no heightmap), GJ4056b
(heightmap, airless, relief_energy_q=1.0), GJ660Ad (Seongho Prime, heightmap,
thin atmosphere, relief_energy_q=1.0)
- Active: GJ1002b (Caparica, heightmap, island world, standard atmosphere),
GJ103c (Vlieland, heightmap, island world, standard atmosphere), GJ447c
(Marevna, heightmap, ocean world) — all relief_energy_q=0.70 per D-239 §5.
CAPTURE RECOMMENDATION (lead): for the true Alpine/MountainPass (MorphologyZone
13/14) crop this ticket wants, the Active terrestrial island worlds GJ1002b and
GJ103c are the primary candidates (coastal orogeny, standard climate — closest
analogue to the GJ380c plains/river substitution being replaced). The Volcanic
pair (GJ4056b, GJ660Ad) are alternates with maximum relief energy but
airless/thin atmospheres — shield/lava-field morphology, likely NOT
Alpine-classed; use only if the Active pair fails to produce MorphologyZone
13/14 crops. GJ0c/GJ0f-1 lack terrain_reference and cannot open in the Atlas.', 'in_progress', 'low', 'hoshe', 'client', NULL, '2026-07-25 16:14:35.308', '2026-07-25 20:42:14.404', NULL, '5b98276c5a6ed1e2e771182e54569617', 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 ('06FSM3MQ5GFSPKKFJF6GGZN8CM', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Alpine relief capture — Quarter-vs-District differentiation on a tectonics:high body', 'Araminta''s PR #192 companion note, kept open by her PR #208 ruling (option b): the Quarter-vs-District relief-differentiation check was accepted for the T-1160 merge on a plains/river substitution (GJ380c world center -17723392,-10532864 — Quarter clearly shows finer higher-frequency texture than District), but plains texture and jagged tectonic relief are different visual regimes and the sharper case still needs its own capture. Task: identify the body pool with tectonics:high (or planet_class values mapping to TectonicClass::Volcanic/Active) and RECORD the pool in this ticket first — the PR #208 eyeball proved GJ380c/GJ820Bc are both tectonics:low via exhaustive two-Global color scans, don''t re-run that search blind — then capture quarter-relief vs district-relief at a true Alpine/MountainPass crop (MorphologyZone 13/14) at the same world center, matched crop, and have Araminta judge the differentiation. Reuse the pr208_driver fixed-center --step protocol (T-1157 harness inventory). Related: T-1160, PR #192, PR #208.
POOL RECORDED (2026-07-25, read-only survey per this ticket''s own first step —
do not re-run the search blind).
MAPPING MECHANISM: server/src/atlas/district_profile.rs:296-312
derive_tectonic_class(): BodyParams::tectonic_activity authored override first
("volcanic"/"active"/"tidally_forced", NO column in current systems.db schema and
no authored overrides in the wiki TOMLs — so today the override path is dead),
else planet_class fallback: "volcanic" → Volcanic; "oceanic"/"ocean_world" →
Active; everything else → Stable. Sanity anchors agree with the PR #208 eyeball:
GJ380c (arid) and GJ820Bc (arid) both → Stable.
THE FULL POOL (7 bodies):
- Volcanic: GJ0c (Venus, no heightmap), GJ0f-1 (Io, no heightmap), GJ4056b
(heightmap, airless, relief_energy_q=1.0), GJ660Ad (Seongho Prime, heightmap,
thin atmosphere, relief_energy_q=1.0)
- Active: GJ1002b (Caparica, heightmap, island world, standard atmosphere),
GJ103c (Vlieland, heightmap, island world, standard atmosphere), GJ447c
(Marevna, heightmap, ocean world) — all relief_energy_q=0.70 per D-239 §5.
CAPTURE RECOMMENDATION (lead): for the true Alpine/MountainPass (MorphologyZone
13/14) crop this ticket wants, the Active terrestrial island worlds GJ1002b and
GJ103c are the primary candidates (coastal orogeny, standard climate — closest
analogue to the GJ380c plains/river substitution being replaced). The Volcanic
pair (GJ4056b, GJ660Ad) are alternates with maximum relief energy but
airless/thin atmospheres — shield/lava-field morphology, likely NOT
Alpine-classed; use only if the Active pair fails to produce MorphologyZone
13/14 crops. GJ0c/GJ0f-1 lack terrain_reference and cannot open in the Atlas.
CAPTURE CENTER PROBED (2026-07-25, server-side scan; scratch bins deleted, tree
verified clean). CHOSEN BODY: GJ1002b (Caparica). GJ103c is a NEGATIVE finding —
a full-body stride-8 scan (2,246,140 districts sampled) found only 4 isolated
MorphologyZone-13/14 cells, none clustering; it is not a viable capture body.
No Volcanic-pair fallback needed.
PRIMARY CENTER (feed to StepCanvasViewer.jump_to, world metres):
(385024, -598016) [district grid (188, -292) x DISTRICT_M=2048]
5x5-district neighborhood 25/25 Alpine/MountainPass; +/-40 km box 452/1681
(26.9%) hits; elev_q 50-100 — reads as a range, not speckle.
ALTERNATES on GJ1002b: (6973440, 1361920) 23/25 dense, 18.9% box;
(2488320, 129024) 24/25, 7.9%; (35919872, -6862848) 23/25, 16.2%;
(29184000, -4837376) 19/25, 18.6%.
METHOD (reproducible): derive_district() per district (district_profile.rs:1769)
over cascade_snapshot_for_body(world_seed=42) + run_layer1 TerrainAnalysis —
identical to aliveness_probe''s --render path (aliveness_probe.rs:72-85). Gate
verified at district_profile.rs:594-712: Alpine(14) = slope_q>=40 && elev_q>=75
&& ocean_fraction_q<20; MountainPass(13) same family, 50<=elev_q<75. Coarse
stride-8 full-body scan -> union-find clustering -> dense stride-1 5x5-window
refine. Both bodies confirmed planet_class=''oceanic'' -> TectonicClass::Active.', 'in_progress', 'low', 'hoshe', 'client', NULL, '2026-07-25 16:14:35.308', '2026-07-25 20:47:03.484', NULL, 'fdaff837803112c5b7c454fea3c06cc6', 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 ('06FSM3MQ5GFSPKKFJF6GGZN8CM', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Alpine relief capture — Quarter-vs-District differentiation on a tectonics:high body', 'Araminta''s PR #192 companion note, kept open by her PR #208 ruling (option b): the Quarter-vs-District relief-differentiation check was accepted for the T-1160 merge on a plains/river substitution (GJ380c world center -17723392,-10532864 — Quarter clearly shows finer higher-frequency texture than District), but plains texture and jagged tectonic relief are different visual regimes and the sharper case still needs its own capture. Task: identify the body pool with tectonics:high (or planet_class values mapping to TectonicClass::Volcanic/Active) and RECORD the pool in this ticket first — the PR #208 eyeball proved GJ380c/GJ820Bc are both tectonics:low via exhaustive two-Global color scans, don''t re-run that search blind — then capture quarter-relief vs district-relief at a true Alpine/MountainPass crop (MorphologyZone 13/14) at the same world center, matched crop, and have Araminta judge the differentiation. Reuse the pr208_driver fixed-center --step protocol (T-1157 harness inventory). Related: T-1160, PR #192, PR #208.
POOL RECORDED (2026-07-25, read-only survey per this ticket''s own first step —
do not re-run the search blind).
MAPPING MECHANISM: server/src/atlas/district_profile.rs:296-312
derive_tectonic_class(): BodyParams::tectonic_activity authored override first
("volcanic"/"active"/"tidally_forced", NO column in current systems.db schema and
no authored overrides in the wiki TOMLs — so today the override path is dead),
else planet_class fallback: "volcanic" → Volcanic; "oceanic"/"ocean_world" →
Active; everything else → Stable. Sanity anchors agree with the PR #208 eyeball:
GJ380c (arid) and GJ820Bc (arid) both → Stable.
THE FULL POOL (7 bodies):
- Volcanic: GJ0c (Venus, no heightmap), GJ0f-1 (Io, no heightmap), GJ4056b
(heightmap, airless, relief_energy_q=1.0), GJ660Ad (Seongho Prime, heightmap,
thin atmosphere, relief_energy_q=1.0)
- Active: GJ1002b (Caparica, heightmap, island world, standard atmosphere),
GJ103c (Vlieland, heightmap, island world, standard atmosphere), GJ447c
(Marevna, heightmap, ocean world) — all relief_energy_q=0.70 per D-239 §5.
CAPTURE RECOMMENDATION (lead): for the true Alpine/MountainPass (MorphologyZone
13/14) crop this ticket wants, the Active terrestrial island worlds GJ1002b and
GJ103c are the primary candidates (coastal orogeny, standard climate — closest
analogue to the GJ380c plains/river substitution being replaced). The Volcanic
pair (GJ4056b, GJ660Ad) are alternates with maximum relief energy but
airless/thin atmospheres — shield/lava-field morphology, likely NOT
Alpine-classed; use only if the Active pair fails to produce MorphologyZone
13/14 crops. GJ0c/GJ0f-1 lack terrain_reference and cannot open in the Atlas.
CAPTURE CENTER PROBED (2026-07-25, server-side scan; scratch bins deleted, tree
verified clean). CHOSEN BODY: GJ1002b (Caparica). GJ103c is a NEGATIVE finding —
a full-body stride-8 scan (2,246,140 districts sampled) found only 4 isolated
MorphologyZone-13/14 cells, none clustering; it is not a viable capture body.
No Volcanic-pair fallback needed.
PRIMARY CENTER (feed to StepCanvasViewer.jump_to, world metres):
(385024, -598016) [district grid (188, -292) x DISTRICT_M=2048]
5x5-district neighborhood 25/25 Alpine/MountainPass; +/-40 km box 452/1681
(26.9%) hits; elev_q 50-100 — reads as a range, not speckle.
ALTERNATES on GJ1002b: (6973440, 1361920) 23/25 dense, 18.9% box;
(2488320, 129024) 24/25, 7.9%; (35919872, -6862848) 23/25, 16.2%;
(29184000, -4837376) 19/25, 18.6%.
METHOD (reproducible): derive_district() per district (district_profile.rs:1769)
over cascade_snapshot_for_body(world_seed=42) + run_layer1 TerrainAnalysis —
identical to aliveness_probe''s --render path (aliveness_probe.rs:72-85). Gate
verified at district_profile.rs:594-712: Alpine(14) = slope_q>=40 && elev_q>=75
&& ocean_fraction_q<20; MountainPass(13) same family, 50<=elev_q<75. Coarse
stride-8 full-body scan -> union-find clustering -> dense stride-1 5x5-window
refine. Both bodies confirmed planet_class=''oceanic'' -> TectonicClass::Active.
VERDICT (araminta, 2026-07-25): PASS — closes the PR #192 companion concern. District shows the massif as soft formless high-elevation blobs; Quarter at the identical center resolves an elongated NW-SE ridge spine with perpendicular lobate spurs, slope stipple, and a localized feature District has no resolution to show — organized directional structure absent at District, not a rescale. Judged the harder test than the accepted GJ380c plains case and a cleaner pass. Region frame correctly shows no relief signature (a ~10km massif is near-sub-pixel at 204.8km/gridunit; Region''s job is context, no claim staked there). Evidence: scratchpad t1196/gj1002b_{quarter,district,region}.png, matched world_center (385024,-598016) proven by view-transform logs. Secondary observations ticketed separately: header-ghosting artifact (client) and elevation-white vs ice-white grammar split (visual); the dark-red speckle inside the massif is explicitly NOT actioned off one frame — re-examine only if it recurs at other Alpine sites.', 'in_progress', 'low', 'hoshe', 'client', NULL, '2026-07-25 16:14:35.308', '2026-07-25 20:53:29.300', NULL, '39df53306a87e6f02549bbd0434e97bf', 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 ('06FSP3GEJYTA966BSSR9354YC4', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Atlas header readout ghosted in Quarter capture — rung-transition alpha artifact', 'Araminta''s T-1196 secondary observation (first GJ1002b captures, 2026-07-25): the ''ATLAS - CAPARICA / QUARTER'' header readout above the legend rendered faded/ghosted in the Quarter frame (scratchpad t1196/gj1002b_quarter.png), while the District and Region frames from the same session and capture path rendered it fully legible. Suspect a z-order or alpha transition state caught mid-render on rung change (the capture settles on StepCanvasRequest.is_pending() then waits 2 frames — a header fade animation may outlast the data settle). Reproduce with a T-1157-harness jump_to capture at any Quarter rung and inspect header alpha across post-settle frames; if it is a fade animation racing the capture, decide whether the fix is harness-side (settle until animations idle) or UI-side (no fade on rung change). Likely cosmetic; the only frame of the three with a legibility defect.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-25 20:53:37.559', '2026-07-25 20:53:37.559', NULL, '040a9db109324aa0c768676e51b0a103', 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 ('06FSP3H9BVF26YM6PPSMEHX7Z0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Visual grammar: split high-elevation white from ice-tint white on the Atlas ramp', 'Araminta''s T-1196 secondary observation (first GJ1002b captures, 2026-07-25): at Quarter on a temperate standard-atmosphere island world, the elevation ramp''s peak-white reads nearly identical to the legend''s heavy/ice-cap tint white — a temperate alpine ridge can be misread as glaciated. Not a bug (lightness=elevation is always-on; ice tint is a separate overlay per the D-217/D-239 glaciation bands and GLACIATION_TINT_ALPHA ramp), but once past boxes-and-labels the hue/value split between ''bright because high'' and ''white because icy'' needs a deliberate design so the map never lies about glaciation. Home: palette.md''s §3 register (T-1052) + the Atlas legend treatment; araminta holds the art call. Evidence frame: scratchpad t1196/gj1002b_quarter.png vs the legend''s ICE TINT swatch.', 'backlog', 'low', NULL, 'visual', NULL, '2026-07-25 20:53:44.414', '2026-07-25 20:53:44.414', NULL, 'a98e0686b2266fc5a2d2fdc17ede67f3', 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 ('06FSM3MQ5GFSPKKFJF6GGZN8CM', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Alpine relief capture — Quarter-vs-District differentiation on a tectonics:high body', 'Araminta''s PR #192 companion note, kept open by her PR #208 ruling (option b): the Quarter-vs-District relief-differentiation check was accepted for the T-1160 merge on a plains/river substitution (GJ380c world center -17723392,-10532864 — Quarter clearly shows finer higher-frequency texture than District), but plains texture and jagged tectonic relief are different visual regimes and the sharper case still needs its own capture. Task: identify the body pool with tectonics:high (or planet_class values mapping to TectonicClass::Volcanic/Active) and RECORD the pool in this ticket first — the PR #208 eyeball proved GJ380c/GJ820Bc are both tectonics:low via exhaustive two-Global color scans, don''t re-run that search blind — then capture quarter-relief vs district-relief at a true Alpine/MountainPass crop (MorphologyZone 13/14) at the same world center, matched crop, and have Araminta judge the differentiation. Reuse the pr208_driver fixed-center --step protocol (T-1157 harness inventory). Related: T-1160, PR #192, PR #208.
POOL RECORDED (2026-07-25, read-only survey per this ticket''s own first step —
do not re-run the search blind).
MAPPING MECHANISM: server/src/atlas/district_profile.rs:296-312
derive_tectonic_class(): BodyParams::tectonic_activity authored override first
("volcanic"/"active"/"tidally_forced", NO column in current systems.db schema and
no authored overrides in the wiki TOMLs — so today the override path is dead),
else planet_class fallback: "volcanic" → Volcanic; "oceanic"/"ocean_world" →
Active; everything else → Stable. Sanity anchors agree with the PR #208 eyeball:
GJ380c (arid) and GJ820Bc (arid) both → Stable.
THE FULL POOL (7 bodies):
- Volcanic: GJ0c (Venus, no heightmap), GJ0f-1 (Io, no heightmap), GJ4056b
(heightmap, airless, relief_energy_q=1.0), GJ660Ad (Seongho Prime, heightmap,
thin atmosphere, relief_energy_q=1.0)
- Active: GJ1002b (Caparica, heightmap, island world, standard atmosphere),
GJ103c (Vlieland, heightmap, island world, standard atmosphere), GJ447c
(Marevna, heightmap, ocean world) — all relief_energy_q=0.70 per D-239 §5.
CAPTURE RECOMMENDATION (lead): for the true Alpine/MountainPass (MorphologyZone
13/14) crop this ticket wants, the Active terrestrial island worlds GJ1002b and
GJ103c are the primary candidates (coastal orogeny, standard climate — closest
analogue to the GJ380c plains/river substitution being replaced). The Volcanic
pair (GJ4056b, GJ660Ad) are alternates with maximum relief energy but
airless/thin atmospheres — shield/lava-field morphology, likely NOT
Alpine-classed; use only if the Active pair fails to produce MorphologyZone
13/14 crops. GJ0c/GJ0f-1 lack terrain_reference and cannot open in the Atlas.
CAPTURE CENTER PROBED (2026-07-25, server-side scan; scratch bins deleted, tree
verified clean). CHOSEN BODY: GJ1002b (Caparica). GJ103c is a NEGATIVE finding —
a full-body stride-8 scan (2,246,140 districts sampled) found only 4 isolated
MorphologyZone-13/14 cells, none clustering; it is not a viable capture body.
No Volcanic-pair fallback needed.
PRIMARY CENTER (feed to StepCanvasViewer.jump_to, world metres):
(385024, -598016) [district grid (188, -292) x DISTRICT_M=2048]
5x5-district neighborhood 25/25 Alpine/MountainPass; +/-40 km box 452/1681
(26.9%) hits; elev_q 50-100 — reads as a range, not speckle.
ALTERNATES on GJ1002b: (6973440, 1361920) 23/25 dense, 18.9% box;
(2488320, 129024) 24/25, 7.9%; (35919872, -6862848) 23/25, 16.2%;
(29184000, -4837376) 19/25, 18.6%.
METHOD (reproducible): derive_district() per district (district_profile.rs:1769)
over cascade_snapshot_for_body(world_seed=42) + run_layer1 TerrainAnalysis —
identical to aliveness_probe''s --render path (aliveness_probe.rs:72-85). Gate
verified at district_profile.rs:594-712: Alpine(14) = slope_q>=40 && elev_q>=75
&& ocean_fraction_q<20; MountainPass(13) same family, 50<=elev_q<75. Coarse
stride-8 full-body scan -> union-find clustering -> dense stride-1 5x5-window
refine. Both bodies confirmed planet_class=''oceanic'' -> TectonicClass::Active.
VERDICT (araminta, 2026-07-25): PASS — closes the PR #192 companion concern. District shows the massif as soft formless high-elevation blobs; Quarter at the identical center resolves an elongated NW-SE ridge spine with perpendicular lobate spurs, slope stipple, and a localized feature District has no resolution to show — organized directional structure absent at District, not a rescale. Judged the harder test than the accepted GJ380c plains case and a cleaner pass. Region frame correctly shows no relief signature (a ~10km massif is near-sub-pixel at 204.8km/gridunit; Region''s job is context, no claim staked there). Evidence: scratchpad t1196/gj1002b_{quarter,district,region}.png, matched world_center (385024,-598016) proven by view-transform logs. Secondary observations ticketed separately: header-ghosting artifact (client) and elevation-white vs ice-white grammar split (visual); the dark-red speckle inside the massif is explicitly NOT actioned off one frame — re-examine only if it recurs at other Alpine sites.', 'done', 'low', 'hoshe', 'client', NULL, '2026-07-25 16:14:35.308', '2026-07-25 20:53:47.773', NULL, 'ce4cc02ea489aaa9e0fd882a2713978a', 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 ('06FBPTXPE8FR7TGEM7X6QVPV4M', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Refine T-961 into per-asset-class stories, each gated on a written brief', '(description follows in first append)
Filed 2026-06-12 from the fable-ous.md audit (S-43; citations adversarially verified).
T-961 (two-sentence description, no acceptance criteria, no brief links) is the only asset-production ticket under T-750, while the epic names eight classes; cars, barns, TVs, billboards appear in NO ticket at all (verified against the full corpus). Refine into child stories per class — station walls, rural walls, doors, floors, furniture/props, street furniture (lamp posts), signage/screens (billboards/TVs), rural structures (barns) — each gated on a one-page brief; explicitly record which classes are deferred (cars: see Q-067 audit note). Decide Q-119 (generic-parent placeholder set as first milestone) before sequencing.
Audit per-class status matrix (verified 2026-06-12):
# docs/assets/ survey (13 files + downloads/)
| File | One-line summary |
|---|---|
| README.md | Master index: audio/visual/video pipelines, status vocabulary (planned/in-progress/placeholder/final), structure that promises visual/{sprites,furniture,tilesets,icons,effects}.md |
| _templates/audio.md | Row + detailed-entry template for audio assets |
| _templates/sprite.md | Row + detailed-entry template for sprites (ID prefixes SPR/FUR/TILE/ICO/FX, status, method, directions columns) |
| _templates/video.md | Row + detailed-entry template for video |
| audio/README.md | ACTIVE production-status catalog — per-category counts table (12 assets tracked), generation tools, bus architecture |
| audio/palette.md | Sonic style bible |
| audio/ambient.md / sfx.md / ui.md | Per-category audio asset tables |
| visual/README.md | STUB — links 5 category files that do not exist, all counts 0 |
| visual/palette.md | STUB — 4 bullet decisions (D-019/D-033/D-045, "functional warmth"), defers to art-direction workshop |
| video/README.md / palette.md | Stubs |
| downloads/ | Purchased character sources (Quaternius Universal Base Characters, Modular Character Outfits FBX) — characters only |
# Per-asset-class matrix
| Class | Written brief | Style guide | Naming/res conventions | Gen path | Produced assets | Tickets |
|---|---|---|---|---|---|---|
| Walls (station) | art-direction workshop-outcomes.md §3.3 (v0.1.1 set, pre-cascade) + docs/design/placeholder-art-spec-v01.md §2.1 | visual-grammar-v01.md §1/§4.3, workshop-outcomes §1.8 era palette (partly superseded by D-235), D-235 WallMaterial vocab | renderer/README.md:80-95 (`<model>_<dir>_<res>.png`, 1024→256→64 chain) | working — renderer/models/wall_structural.tscn rendered | wall_structural ×4 dirs in client/assets/sprites/; wall_bar_green model unrendered; 2 textures | T-961 (backlog), T-988 (token vocab, backlog) |
| Walls (rural) | none found | D-235 tokens only (timber/rammed-earth etc.) | renderer/README.md (generic) | same path, unused | none | T-961 text mention only |
| Doors | placeholder-art-spec-v01.md §2.1 (procedural rects, v0.1) + workshop-outcomes §3.3 `door_sliding` | none for generated world | none for open/closed state frames | same path, unused | none | T-979/T-998 (DoorSpec — logical only), T-961 |
| Floors | placeholder-art-spec-v01.md §2.1 + workshop-outcomes §3.3 (4 floor sprites) | D-228 FloorMaterial axis (open vocab) | 64px visual tile (placeholder-art-spec §1) | same path, unused | none — client/assets/tilesets/ is .gitkeep only | T-959 (tile fill, logical), T-961 |
| Styled buildings | D-232 + D-235 + wiki/economics/architecture_trait_catalog.toml (T-1005 done, ~35 visual bundles) | D-235 material/color-register grammar — tokens only, no rendered art direction | token→fallback-parent chain (catalog header :34-44) | no model/texture path defined for building exteriors | none | T-977, T-988, T-994, T-1004 (all backlog), T-1005 (done) |
| Furniture | docs/assets/visual/furniture.md MISSING (linked); workshop-outcomes §3.3 lists 8-10 objects (pre-cascade) | era table §1.8 only | character-asset-organization.md explicitly excludes furniture ("follow Trellis spike conventions") | proven in spike | spikes/3dpipeline/models/furniture/ ×3 GLB (unpromoted); zero in client/assets | T-961, T-995 (ObjectTag vocab, backlog) |
| Items | docs/design/icon-set-v01.md (HUD/inventory icons only) | D-086 icon constraints | SVG icon_ prefix (client/assets/icons/) | n/a for icons | 13 SVGs in client/assets/icons/ | none for in-world item sprites |
| Cars/vehicles | none found | none | none | spike only | spikes/3dpipeline/models/props/vw_beetle.glb (spike) | none — Q-067 open ("driveable vs NPC traffic?") |
| Lamp posts | none found (zero repo hits) | none | none | unused | none | T-961 text mention only |
| Barns | none found (only T-750 epic text) | none | none | unused | none | none |
| TVs | none found (only T-750 epic text) | none | none | unused | none | none |
| Billboards | none found (only T-750 epic text; other hits = LOD impostors) | none | none | unused | none | none |
| Characters (reference class — the working model) | docs/architecture/character-asset-organization.md + docs/design/character-visuals-spec.md | yes | full (manifest.json, seg_ naming, body-type keys) | proven end-to-end | 1002 files in client/assets/characters/ | done |
# Generation path (verified files)
image-gen (.claude/skills/image-gen/SKILL.md + scripts/image_connector.py, Gemini) → glb-gen (SKILL.md + tooling/db/trellis_connector.py + tooling/trellis-batch.sh + Blender postprocess scripts) → sprite-gen (SKILL.md + scripts/render.sh) → renderer/ (project.godot, render_scene.tscn, render_export.gd; D-019 camera, 3-light rig, 1024/256/64 chain) → client/assets/sprites/. renderer/output/ holds only .gdignore (intermediates gitignored).
# Produced assets in client/assets (non-.import files)
audio 16 · characters 1002 · fonts 3 · icons 13 · planets 7 · sprites 9 (npc_generic ×4, wall_structural ×4, .gitkeep) · theme 1 · tilesets 1 (.gitkeep only)
---
Refinement (2026-06-16, /whats-next Si pass) — READY. This is a PLANNING task: break T-961 into per-class child stories.
CLASS LIST (8 stories, parented under T-961, each gated on a one-page brief): station walls, rural walls, doors, floors, furniture/props, street furniture (lamp posts), signage/screens (TVs/billboards), rural structures (barns).
- CARS (9th class named in T-750): DEFERRED — Q-067 needs a phase decision (Phase-4 deliverable vs Phase-5+) before any car story. File a deferral note, not a story.
- Q-119 (generic-parent placeholder set as milestone-0): NOT a blocker — sequencing only. On each relevant story note that the generic parents (generic_wall/roof/facade/street + generic_door/floor/furniture; D-235 / wiki/economics/architecture_trait_catalog.toml:33-41) are milestone-0 IF Q-119 resolves "yes".
BRIEF TEMPLATE: none exists. Create docs/assets/_templates/asset-brief.md (scope · visual reference · naming/resolution conventions · generation path · acceptance), or adopt docs/_templates/design.md. Each story states where its brief + catalog file live under docs/assets/visual/.
READ before filing: docs/assets/README.md, docs/design/{placeholder-art-spec-v01,visual-grammar-v01}.md, docs/workshops/art-direction-mood-board/workshop-outcomes.md, D-235/D-228/D-232, docs/architecture/character-asset-organization.md (the reference model).
NOTE: T-1050 (sibling) owns the catalog + naming conventions these stories reference — sequence T-1049 to consume T-1050''s conventions where they overlap.
---
HELD 2026-06-16 (separated from the cascade batch; needs re-scoping before resuming).
The asset pipeline this ticket assumes is sprite-centric (docs/assets/visual/sprites.md
"Entities", _templates/sprite.md, SPR/FUR/TILE/ICO/FX prefixes, the /sprite-gen
render-3D->2D-sprites step). Direction correction: we are NOT producing sprites — Trellis
produces 3D objects (.glb) displayed in-world; the only flattened 2D content is TEXTURES.
Re-scope this ticket around a 3D-model + texture catalog (not a sprite catalog) before
activating. See the sprite-vs-3D root-cause note (this session).
Refinement (batch-6 Si pass, 2026-07-25) — SUPERSEDES the 2026-06-16 READY block
(sprite-era, invalidated by D-244) and the 2026-06-16 HELD note. T-1050 (catalog +
conventions.md + manifest.json) and T-1052 (palette.md style bible) are both DONE
(PR #211, merged 2026-07-25) — the conventions this ticket needed to consume now
exist and are read, not hypothetical.
SCOPE, 3D REALITY: T-961 breaks into per-asset-class stories, each gated on a
one-page brief. A "written brief" is now a short doc that states, per class:
(1) scope — which manifest category (furniture|props) and which D-235 ObjectTag
axis if any; (2) visual reference — cites palette.md §3''s hue/grain/weathering
register for the relevant token(s), or palette.md §2 toon/PBR default for classes
with no D-235 token; (3) naming — conventions.md §1 model naming
(<category>_<descriptor>.glb), §2 mask sidecar if tintable, §4 footprint_tiles if
multi-tile; (4) generation path — /image-gen concept → /glb-gen (Trellis) →
postprocess → promotion into client/assets/models/{furniture,props}/ +
manifest.json entry (conventions.md, models.md); (5) acceptance — N assets at
"planned" status registered in the manifest with a real manifest.json entry,
status flips as production proceeds (models.md''s shared status vocab).
BRIEF TEMPLATE (create docs/assets/_templates/asset-brief.md): the five headers
above (scope / visual reference / naming / gen path / acceptance), one page,
adopting docs/_templates/design.md frontmatter. Do NOT reuse _templates/sprite.md
(SPR/FUR/TILE/ICO/FX prefixes) — explicitly retired by D-244/T-1050.
CLASS LIST — station walls, rural walls, doors, floors, furniture/props already
have partial prior art (station walls: wall_structural GLB exists unrendered per
the 2026-06-12 audit matrix; doors: DoorSpec logical model exists, T-979/T-998;
floors: D-228 FloorMaterial axis exists) — each gets a real story + brief, citing
its relevant D-235 axis (wall/roof/facade/street) or, for doors/floors/furniture,
the non-ObjectTag conventions (mask sidecars, footprint_tiles).
STREET FURNITURE / SIGNAGE-SCREENS / RURAL STRUCTURES (lamp posts, barns,
TVs, billboards — T-1051''s four classes): RECOMMEND FORMAL DEFERRAL, not real
briefs, this batch. Rationale: Phase 4''s deliverable is "deterministic
walkable-world data + per-layer Atlas maps + asset catalog" (cascade table) —
outside-in generation (D-166) needs building EXTERIOR templates
(D-235 wall/roof/facade/street axes) to make districts/blocks walkable and
Atlas-legible. Lamp posts (street furniture), TVs/billboards (signage/screens
placed as interior or exterior set-dressing), and barns (a rural building
*type*, not a wall/roof/facade axis token) are furniture-tier detail objects
that populate a walkable exterior once it exists — they read as Phase 6
"detail coloring / room-level population" (cascade table) or, at best, late
Phase 4 polish, not blocking asset classes for getting the world walkable.
Zero repo hits exist for any of the four beyond epic text (T-1049''s own 2026-06-12
audit matrix, unchanged) — there is no partial work to protect by deferring.
Treat identically to Q-067''s car deferral: file a deferral note on the story
(or fold into one placeholder story "T-1051 classes — deferred pending Phase 4
exterior-generation completion"), do not write real briefs now. Distinguish from
cars only in degree, not in kind — Q-067 is a still-open decision question
(vehicle simulation is a bigger unresolved design surface); the T-1051 four are
simpler set-dressing whose deferral needs no open question, just a scope note,
because nothing about their eventual brief depends on an undecided mechanic.
Q-119 (generic-parent placeholder set as milestone-0) is STILL OPEN (unresolved
as of this pass) — unchanged from the prior block: not a blocker, sequencing-only.
Each relevant story notes the generic_wall/roof/facade/street (+ generic
door/floor/furniture) parents as milestone-0 IF Q-119 resolves yes.
READ before filing: docs/assets/visual/{conventions,palette,models,textures,
artwork}.md (T-1050/T-1052, merged), D-244, D-257, D-235 (ratified token list),
D-243 (footprint unit), docs/architecture/character-asset-organization.md
(mask/manifest precedent).
VERDICT: READY (activation = batch 6; the T-1051 deferral recommendation above is
ratified at activation, then T-1051 is updated to match the ruling this ticket records).
Lead ratification (batch-6 activation, 2026-07-25): the refinement block''s T-1051 recommendation is RATIFIED — lamp posts, barns, TVs, billboards get a formal Q-067-style deferral, not briefs. Implementation of this ticket authors the deferral note (four classes, rationale, Q-067 precedent, ''revisit when Phase-4 exterior generation completes'') alongside the real briefs; T-1051''s own deliverable (''or record formal deferral'') is satisfied by that note and T-1051 closes when it lands. Child story tickets under T-961: the implementing agent DRAFTS the story list (title + one-line scope + brief path per class) in its report; the lead creates them on main at merge (avoids cross-vault worktree pql writes, FR-4). Surfaced to Jeroen in the batch report for cheap veto.', 'backlog', 'high', NULL, 'visual', NULL, '2026-06-12 10:40:58', '2026-07-25 20:54:28.647', NULL, 'f5d39a7155b5b624547958856e99cd29', 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 ('06FBPTXPE8FR7TGEM7X6QVPV4M', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Refine T-961 into per-asset-class stories, each gated on a written brief', '(description follows in first append)
Filed 2026-06-12 from the fable-ous.md audit (S-43; citations adversarially verified).
T-961 (two-sentence description, no acceptance criteria, no brief links) is the only asset-production ticket under T-750, while the epic names eight classes; cars, barns, TVs, billboards appear in NO ticket at all (verified against the full corpus). Refine into child stories per class — station walls, rural walls, doors, floors, furniture/props, street furniture (lamp posts), signage/screens (billboards/TVs), rural structures (barns) — each gated on a one-page brief; explicitly record which classes are deferred (cars: see Q-067 audit note). Decide Q-119 (generic-parent placeholder set as first milestone) before sequencing.
Audit per-class status matrix (verified 2026-06-12):
# docs/assets/ survey (13 files + downloads/)
| File | One-line summary |
|---|---|
| README.md | Master index: audio/visual/video pipelines, status vocabulary (planned/in-progress/placeholder/final), structure that promises visual/{sprites,furniture,tilesets,icons,effects}.md |
| _templates/audio.md | Row + detailed-entry template for audio assets |
| _templates/sprite.md | Row + detailed-entry template for sprites (ID prefixes SPR/FUR/TILE/ICO/FX, status, method, directions columns) |
| _templates/video.md | Row + detailed-entry template for video |
| audio/README.md | ACTIVE production-status catalog — per-category counts table (12 assets tracked), generation tools, bus architecture |
| audio/palette.md | Sonic style bible |
| audio/ambient.md / sfx.md / ui.md | Per-category audio asset tables |
| visual/README.md | STUB — links 5 category files that do not exist, all counts 0 |
| visual/palette.md | STUB — 4 bullet decisions (D-019/D-033/D-045, "functional warmth"), defers to art-direction workshop |
| video/README.md / palette.md | Stubs |
| downloads/ | Purchased character sources (Quaternius Universal Base Characters, Modular Character Outfits FBX) — characters only |
# Per-asset-class matrix
| Class | Written brief | Style guide | Naming/res conventions | Gen path | Produced assets | Tickets |
|---|---|---|---|---|---|---|
| Walls (station) | art-direction workshop-outcomes.md §3.3 (v0.1.1 set, pre-cascade) + docs/design/placeholder-art-spec-v01.md §2.1 | visual-grammar-v01.md §1/§4.3, workshop-outcomes §1.8 era palette (partly superseded by D-235), D-235 WallMaterial vocab | renderer/README.md:80-95 (`<model>_<dir>_<res>.png`, 1024→256→64 chain) | working — renderer/models/wall_structural.tscn rendered | wall_structural ×4 dirs in client/assets/sprites/; wall_bar_green model unrendered; 2 textures | T-961 (backlog), T-988 (token vocab, backlog) |
| Walls (rural) | none found | D-235 tokens only (timber/rammed-earth etc.) | renderer/README.md (generic) | same path, unused | none | T-961 text mention only |
| Doors | placeholder-art-spec-v01.md §2.1 (procedural rects, v0.1) + workshop-outcomes §3.3 `door_sliding` | none for generated world | none for open/closed state frames | same path, unused | none | T-979/T-998 (DoorSpec — logical only), T-961 |
| Floors | placeholder-art-spec-v01.md §2.1 + workshop-outcomes §3.3 (4 floor sprites) | D-228 FloorMaterial axis (open vocab) | 64px visual tile (placeholder-art-spec §1) | same path, unused | none — client/assets/tilesets/ is .gitkeep only | T-959 (tile fill, logical), T-961 |
| Styled buildings | D-232 + D-235 + wiki/economics/architecture_trait_catalog.toml (T-1005 done, ~35 visual bundles) | D-235 material/color-register grammar — tokens only, no rendered art direction | token→fallback-parent chain (catalog header :34-44) | no model/texture path defined for building exteriors | none | T-977, T-988, T-994, T-1004 (all backlog), T-1005 (done) |
| Furniture | docs/assets/visual/furniture.md MISSING (linked); workshop-outcomes §3.3 lists 8-10 objects (pre-cascade) | era table §1.8 only | character-asset-organization.md explicitly excludes furniture ("follow Trellis spike conventions") | proven in spike | spikes/3dpipeline/models/furniture/ ×3 GLB (unpromoted); zero in client/assets | T-961, T-995 (ObjectTag vocab, backlog) |
| Items | docs/design/icon-set-v01.md (HUD/inventory icons only) | D-086 icon constraints | SVG icon_ prefix (client/assets/icons/) | n/a for icons | 13 SVGs in client/assets/icons/ | none for in-world item sprites |
| Cars/vehicles | none found | none | none | spike only | spikes/3dpipeline/models/props/vw_beetle.glb (spike) | none — Q-067 open ("driveable vs NPC traffic?") |
| Lamp posts | none found (zero repo hits) | none | none | unused | none | T-961 text mention only |
| Barns | none found (only T-750 epic text) | none | none | unused | none | none |
| TVs | none found (only T-750 epic text) | none | none | unused | none | none |
| Billboards | none found (only T-750 epic text; other hits = LOD impostors) | none | none | unused | none | none |
| Characters (reference class — the working model) | docs/architecture/character-asset-organization.md + docs/design/character-visuals-spec.md | yes | full (manifest.json, seg_ naming, body-type keys) | proven end-to-end | 1002 files in client/assets/characters/ | done |
# Generation path (verified files)
image-gen (.claude/skills/image-gen/SKILL.md + scripts/image_connector.py, Gemini) → glb-gen (SKILL.md + tooling/db/trellis_connector.py + tooling/trellis-batch.sh + Blender postprocess scripts) → sprite-gen (SKILL.md + scripts/render.sh) → renderer/ (project.godot, render_scene.tscn, render_export.gd; D-019 camera, 3-light rig, 1024/256/64 chain) → client/assets/sprites/. renderer/output/ holds only .gdignore (intermediates gitignored).
# Produced assets in client/assets (non-.import files)
audio 16 · characters 1002 · fonts 3 · icons 13 · planets 7 · sprites 9 (npc_generic ×4, wall_structural ×4, .gitkeep) · theme 1 · tilesets 1 (.gitkeep only)
---
Refinement (2026-06-16, /whats-next Si pass) — READY. This is a PLANNING task: break T-961 into per-class child stories.
CLASS LIST (8 stories, parented under T-961, each gated on a one-page brief): station walls, rural walls, doors, floors, furniture/props, street furniture (lamp posts), signage/screens (TVs/billboards), rural structures (barns).
- CARS (9th class named in T-750): DEFERRED — Q-067 needs a phase decision (Phase-4 deliverable vs Phase-5+) before any car story. File a deferral note, not a story.
- Q-119 (generic-parent placeholder set as milestone-0): NOT a blocker — sequencing only. On each relevant story note that the generic parents (generic_wall/roof/facade/street + generic_door/floor/furniture; D-235 / wiki/economics/architecture_trait_catalog.toml:33-41) are milestone-0 IF Q-119 resolves "yes".
BRIEF TEMPLATE: none exists. Create docs/assets/_templates/asset-brief.md (scope · visual reference · naming/resolution conventions · generation path · acceptance), or adopt docs/_templates/design.md. Each story states where its brief + catalog file live under docs/assets/visual/.
READ before filing: docs/assets/README.md, docs/design/{placeholder-art-spec-v01,visual-grammar-v01}.md, docs/workshops/art-direction-mood-board/workshop-outcomes.md, D-235/D-228/D-232, docs/architecture/character-asset-organization.md (the reference model).
NOTE: T-1050 (sibling) owns the catalog + naming conventions these stories reference — sequence T-1049 to consume T-1050''s conventions where they overlap.
---
HELD 2026-06-16 (separated from the cascade batch; needs re-scoping before resuming).
The asset pipeline this ticket assumes is sprite-centric (docs/assets/visual/sprites.md
"Entities", _templates/sprite.md, SPR/FUR/TILE/ICO/FX prefixes, the /sprite-gen
render-3D->2D-sprites step). Direction correction: we are NOT producing sprites — Trellis
produces 3D objects (.glb) displayed in-world; the only flattened 2D content is TEXTURES.
Re-scope this ticket around a 3D-model + texture catalog (not a sprite catalog) before
activating. See the sprite-vs-3D root-cause note (this session).
Refinement (batch-6 Si pass, 2026-07-25) — SUPERSEDES the 2026-06-16 READY block
(sprite-era, invalidated by D-244) and the 2026-06-16 HELD note. T-1050 (catalog +
conventions.md + manifest.json) and T-1052 (palette.md style bible) are both DONE
(PR #211, merged 2026-07-25) — the conventions this ticket needed to consume now
exist and are read, not hypothetical.
SCOPE, 3D REALITY: T-961 breaks into per-asset-class stories, each gated on a
one-page brief. A "written brief" is now a short doc that states, per class:
(1) scope — which manifest category (furniture|props) and which D-235 ObjectTag
axis if any; (2) visual reference — cites palette.md §3''s hue/grain/weathering
register for the relevant token(s), or palette.md §2 toon/PBR default for classes
with no D-235 token; (3) naming — conventions.md §1 model naming
(<category>_<descriptor>.glb), §2 mask sidecar if tintable, §4 footprint_tiles if
multi-tile; (4) generation path — /image-gen concept → /glb-gen (Trellis) →
postprocess → promotion into client/assets/models/{furniture,props}/ +
manifest.json entry (conventions.md, models.md); (5) acceptance — N assets at
"planned" status registered in the manifest with a real manifest.json entry,
status flips as production proceeds (models.md''s shared status vocab).
BRIEF TEMPLATE (create docs/assets/_templates/asset-brief.md): the five headers
above (scope / visual reference / naming / gen path / acceptance), one page,
adopting docs/_templates/design.md frontmatter. Do NOT reuse _templates/sprite.md
(SPR/FUR/TILE/ICO/FX prefixes) — explicitly retired by D-244/T-1050.
CLASS LIST — station walls, rural walls, doors, floors, furniture/props already
have partial prior art (station walls: wall_structural GLB exists unrendered per
the 2026-06-12 audit matrix; doors: DoorSpec logical model exists, T-979/T-998;
floors: D-228 FloorMaterial axis exists) — each gets a real story + brief, citing
its relevant D-235 axis (wall/roof/facade/street) or, for doors/floors/furniture,
the non-ObjectTag conventions (mask sidecars, footprint_tiles).
STREET FURNITURE / SIGNAGE-SCREENS / RURAL STRUCTURES (lamp posts, barns,
TVs, billboards — T-1051''s four classes): RECOMMEND FORMAL DEFERRAL, not real
briefs, this batch. Rationale: Phase 4''s deliverable is "deterministic
walkable-world data + per-layer Atlas maps + asset catalog" (cascade table) —
outside-in generation (D-166) needs building EXTERIOR templates
(D-235 wall/roof/facade/street axes) to make districts/blocks walkable and
Atlas-legible. Lamp posts (street furniture), TVs/billboards (signage/screens
placed as interior or exterior set-dressing), and barns (a rural building
*type*, not a wall/roof/facade axis token) are furniture-tier detail objects
that populate a walkable exterior once it exists — they read as Phase 6
"detail coloring / room-level population" (cascade table) or, at best, late
Phase 4 polish, not blocking asset classes for getting the world walkable.
Zero repo hits exist for any of the four beyond epic text (T-1049''s own 2026-06-12
audit matrix, unchanged) — there is no partial work to protect by deferring.
Treat identically to Q-067''s car deferral: file a deferral note on the story
(or fold into one placeholder story "T-1051 classes — deferred pending Phase 4
exterior-generation completion"), do not write real briefs now. Distinguish from
cars only in degree, not in kind — Q-067 is a still-open decision question
(vehicle simulation is a bigger unresolved design surface); the T-1051 four are
simpler set-dressing whose deferral needs no open question, just a scope note,
because nothing about their eventual brief depends on an undecided mechanic.
Q-119 (generic-parent placeholder set as milestone-0) is STILL OPEN (unresolved
as of this pass) — unchanged from the prior block: not a blocker, sequencing-only.
Each relevant story notes the generic_wall/roof/facade/street (+ generic
door/floor/furniture) parents as milestone-0 IF Q-119 resolves yes.
READ before filing: docs/assets/visual/{conventions,palette,models,textures,
artwork}.md (T-1050/T-1052, merged), D-244, D-257, D-235 (ratified token list),
D-243 (footprint unit), docs/architecture/character-asset-organization.md
(mask/manifest precedent).
VERDICT: READY (activation = batch 6; the T-1051 deferral recommendation above is
ratified at activation, then T-1051 is updated to match the ruling this ticket records).
Lead ratification (batch-6 activation, 2026-07-25): the refinement block''s T-1051 recommendation is RATIFIED — lamp posts, barns, TVs, billboards get a formal Q-067-style deferral, not briefs. Implementation of this ticket authors the deferral note (four classes, rationale, Q-067 precedent, ''revisit when Phase-4 exterior generation completes'') alongside the real briefs; T-1051''s own deliverable (''or record formal deferral'') is satisfied by that note and T-1051 closes when it lands. Child story tickets under T-961: the implementing agent DRAFTS the story list (title + one-line scope + brief path per class) in its report; the lead creates them on main at merge (avoids cross-vault worktree pql writes, FR-4). Surfaced to Jeroen in the batch report for cheap veto.', 'in_progress', 'high', NULL, 'visual', NULL, '2026-06-12 10:40:58', '2026-07-25 20:54:32.386', NULL, '90292db4d108f4bfaf6d421942c0df1d', 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 ('06FBPTXPZ0ZTM1QQXD5K972CMM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Rewrite docs/briefings/araminta.md around the Phase-4 asset pipeline', '(description follows in first append)
Refinement (batch-6 Si pass, 2026-07-25) — READY.
WHAT''S WRONG WITH THE CURRENT BRIEFING: frozen at the 2026-03-13 v0.2 pivot.
(1) Frames the role around "v0.2 Pivot — Key Changes" — v0.2 is formally dropped
("No scoping negotiations. Build the base systems fully"), so the framing section
is obsolete, not just outdated. (2) Assumes the sprite-era pipeline throughout
("Phase 1-3 placeholder art pipeline," "sprite replacements" in Phase 4) —
contradicted by D-244 (3D objects in-world, no per-object sprite layer) and D-149
(live 3D characters). (3) Cites D-019 as simply "top-down art style" with no note
that D-148 superseded it for gameplay (palette.md §1 had to correct exactly this
conflation in its own stub — the briefing repeats the same error). (4) Zone visual
grammar / tile palette framing predates D-235''s ObjectTag vocabulary and D-232''s
trait-template catalog entirely. (5) Character customisation section (D-126,
Q-WTF-039/040) predates the shipped D-159D-164 body-type + slot-compositing
system and the wardrobe/garment-token pipeline — describes a design question that
has since been built. (6) No mention of the Atlas at all (D-255 stepped ladder,
D-169/D-170 implant UI) despite araminta being a named co-designer of D-255''s
named-feature encoding/payload schema/lakes work.
WHAT THE REWRITE MUST CONTAIN:
- Role framing: visual art direction for the Phase-4 asset pipeline + Atlas
map-art rendering (not "generator spike prerequisites").
- palette.md (T-1052, merged) as the SINGLE LIVE STYLE AUTHORITY — point to it,
do not restate camera/color/material content (avoids a second drifting copy;
visual-grammar-v01.md / workshop-outcomes.md supersession is palette.md §5''s
to state, the briefing defers).
- conventions.md (T-1050, merged) for naming/mask/manifest mechanics — note the
category-first naming ruling (conventions.md §1) and the D-257 toon/PBR ruling
with glazing carve-out as HER standing rulings she''ll be asked to extend.
- D-235 ObjectTag vocabulary as the logical layer she renders a look on top of —
co-maintained with Miri per D-235''s own text.
- The character wardrobe pipeline (character-asset-organization.md) as the proven
reference model conventions.md imitates (mask sidecars, manifest schema).
- D-255 stepped Atlas as active/ongoing work she co-designed.
- Current open items: T-1049 (per-class asset stories, re-scoped this batch), the
D-257 glazing shader implementation (unassigned per palette.md §2.3), Q-119
(generic-parent milestone question, hers to weigh in on per D-235
co-maintenance).
- Drop: v0.2 pivot section, Q-WTF-039/040, tycoon-bookmark/apartment-wakeup
framing, and the v0.2-era decision list (D-114/D-117/D-119/D-122/D-128/D-131/
D-135) — verify each against current decision status before dropping outright;
a few (D-033 entity color, D-126 customisation dimensions) may still have live
sub-parts even though the surrounding section is obsolete.
ACCEPTANCE: rewritten briefing reflects the current Phase-4 landscape (D-235,
D-244, D-257, D-255), points to palette.md/conventions.md as live authorities
rather than duplicating content, drops v0.2-pivot and superseded-question
framing, lists araminta''s actual current/open tickets.
SEQUENCING: independent of T-1049 (different files, no shared input) — both can
run in the same batch.', 'in_progress', 'medium', NULL, 'visual', NULL, '2026-06-12 10:40:59', '2026-07-25 20:54:32.393', NULL, '6a6439b0cf47677bae891287d4cb9251', 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 ('06FBPTXPE8FR7TGEM7X6QVPV4M', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Refine T-961 into per-asset-class stories, each gated on a written brief', '(description follows in first append)
Filed 2026-06-12 from the fable-ous.md audit (S-43; citations adversarially verified).
T-961 (two-sentence description, no acceptance criteria, no brief links) is the only asset-production ticket under T-750, while the epic names eight classes; cars, barns, TVs, billboards appear in NO ticket at all (verified against the full corpus). Refine into child stories per class — station walls, rural walls, doors, floors, furniture/props, street furniture (lamp posts), signage/screens (billboards/TVs), rural structures (barns) — each gated on a one-page brief; explicitly record which classes are deferred (cars: see Q-067 audit note). Decide Q-119 (generic-parent placeholder set as first milestone) before sequencing.
Audit per-class status matrix (verified 2026-06-12):
# docs/assets/ survey (13 files + downloads/)
| File | One-line summary |
|---|---|
| README.md | Master index: audio/visual/video pipelines, status vocabulary (planned/in-progress/placeholder/final), structure that promises visual/{sprites,furniture,tilesets,icons,effects}.md |
| _templates/audio.md | Row + detailed-entry template for audio assets |
| _templates/sprite.md | Row + detailed-entry template for sprites (ID prefixes SPR/FUR/TILE/ICO/FX, status, method, directions columns) |
| _templates/video.md | Row + detailed-entry template for video |
| audio/README.md | ACTIVE production-status catalog — per-category counts table (12 assets tracked), generation tools, bus architecture |
| audio/palette.md | Sonic style bible |
| audio/ambient.md / sfx.md / ui.md | Per-category audio asset tables |
| visual/README.md | STUB — links 5 category files that do not exist, all counts 0 |
| visual/palette.md | STUB — 4 bullet decisions (D-019/D-033/D-045, "functional warmth"), defers to art-direction workshop |
| video/README.md / palette.md | Stubs |
| downloads/ | Purchased character sources (Quaternius Universal Base Characters, Modular Character Outfits FBX) — characters only |
# Per-asset-class matrix
| Class | Written brief | Style guide | Naming/res conventions | Gen path | Produced assets | Tickets |
|---|---|---|---|---|---|---|
| Walls (station) | art-direction workshop-outcomes.md §3.3 (v0.1.1 set, pre-cascade) + docs/design/placeholder-art-spec-v01.md §2.1 | visual-grammar-v01.md §1/§4.3, workshop-outcomes §1.8 era palette (partly superseded by D-235), D-235 WallMaterial vocab | renderer/README.md:80-95 (`<model>_<dir>_<res>.png`, 1024→256→64 chain) | working — renderer/models/wall_structural.tscn rendered | wall_structural ×4 dirs in client/assets/sprites/; wall_bar_green model unrendered; 2 textures | T-961 (backlog), T-988 (token vocab, backlog) |
| Walls (rural) | none found | D-235 tokens only (timber/rammed-earth etc.) | renderer/README.md (generic) | same path, unused | none | T-961 text mention only |
| Doors | placeholder-art-spec-v01.md §2.1 (procedural rects, v0.1) + workshop-outcomes §3.3 `door_sliding` | none for generated world | none for open/closed state frames | same path, unused | none | T-979/T-998 (DoorSpec — logical only), T-961 |
| Floors | placeholder-art-spec-v01.md §2.1 + workshop-outcomes §3.3 (4 floor sprites) | D-228 FloorMaterial axis (open vocab) | 64px visual tile (placeholder-art-spec §1) | same path, unused | none — client/assets/tilesets/ is .gitkeep only | T-959 (tile fill, logical), T-961 |
| Styled buildings | D-232 + D-235 + wiki/economics/architecture_trait_catalog.toml (T-1005 done, ~35 visual bundles) | D-235 material/color-register grammar — tokens only, no rendered art direction | token→fallback-parent chain (catalog header :34-44) | no model/texture path defined for building exteriors | none | T-977, T-988, T-994, T-1004 (all backlog), T-1005 (done) |
| Furniture | docs/assets/visual/furniture.md MISSING (linked); workshop-outcomes §3.3 lists 8-10 objects (pre-cascade) | era table §1.8 only | character-asset-organization.md explicitly excludes furniture ("follow Trellis spike conventions") | proven in spike | spikes/3dpipeline/models/furniture/ ×3 GLB (unpromoted); zero in client/assets | T-961, T-995 (ObjectTag vocab, backlog) |
| Items | docs/design/icon-set-v01.md (HUD/inventory icons only) | D-086 icon constraints | SVG icon_ prefix (client/assets/icons/) | n/a for icons | 13 SVGs in client/assets/icons/ | none for in-world item sprites |
| Cars/vehicles | none found | none | none | spike only | spikes/3dpipeline/models/props/vw_beetle.glb (spike) | none — Q-067 open ("driveable vs NPC traffic?") |
| Lamp posts | none found (zero repo hits) | none | none | unused | none | T-961 text mention only |
| Barns | none found (only T-750 epic text) | none | none | unused | none | none |
| TVs | none found (only T-750 epic text) | none | none | unused | none | none |
| Billboards | none found (only T-750 epic text; other hits = LOD impostors) | none | none | unused | none | none |
| Characters (reference class — the working model) | docs/architecture/character-asset-organization.md + docs/design/character-visuals-spec.md | yes | full (manifest.json, seg_ naming, body-type keys) | proven end-to-end | 1002 files in client/assets/characters/ | done |
# Generation path (verified files)
image-gen (.claude/skills/image-gen/SKILL.md + scripts/image_connector.py, Gemini) → glb-gen (SKILL.md + tooling/db/trellis_connector.py + tooling/trellis-batch.sh + Blender postprocess scripts) → sprite-gen (SKILL.md + scripts/render.sh) → renderer/ (project.godot, render_scene.tscn, render_export.gd; D-019 camera, 3-light rig, 1024/256/64 chain) → client/assets/sprites/. renderer/output/ holds only .gdignore (intermediates gitignored).
# Produced assets in client/assets (non-.import files)
audio 16 · characters 1002 · fonts 3 · icons 13 · planets 7 · sprites 9 (npc_generic ×4, wall_structural ×4, .gitkeep) · theme 1 · tilesets 1 (.gitkeep only)
---
Refinement (2026-06-16, /whats-next Si pass) — READY. This is a PLANNING task: break T-961 into per-class child stories.
CLASS LIST (8 stories, parented under T-961, each gated on a one-page brief): station walls, rural walls, doors, floors, furniture/props, street furniture (lamp posts), signage/screens (TVs/billboards), rural structures (barns).
- CARS (9th class named in T-750): DEFERRED — Q-067 needs a phase decision (Phase-4 deliverable vs Phase-5+) before any car story. File a deferral note, not a story.
- Q-119 (generic-parent placeholder set as milestone-0): NOT a blocker — sequencing only. On each relevant story note that the generic parents (generic_wall/roof/facade/street + generic_door/floor/furniture; D-235 / wiki/economics/architecture_trait_catalog.toml:33-41) are milestone-0 IF Q-119 resolves "yes".
BRIEF TEMPLATE: none exists. Create docs/assets/_templates/asset-brief.md (scope · visual reference · naming/resolution conventions · generation path · acceptance), or adopt docs/_templates/design.md. Each story states where its brief + catalog file live under docs/assets/visual/.
READ before filing: docs/assets/README.md, docs/design/{placeholder-art-spec-v01,visual-grammar-v01}.md, docs/workshops/art-direction-mood-board/workshop-outcomes.md, D-235/D-228/D-232, docs/architecture/character-asset-organization.md (the reference model).
NOTE: T-1050 (sibling) owns the catalog + naming conventions these stories reference — sequence T-1049 to consume T-1050''s conventions where they overlap.
---
HELD 2026-06-16 (separated from the cascade batch; needs re-scoping before resuming).
The asset pipeline this ticket assumes is sprite-centric (docs/assets/visual/sprites.md
"Entities", _templates/sprite.md, SPR/FUR/TILE/ICO/FX prefixes, the /sprite-gen
render-3D->2D-sprites step). Direction correction: we are NOT producing sprites — Trellis
produces 3D objects (.glb) displayed in-world; the only flattened 2D content is TEXTURES.
Re-scope this ticket around a 3D-model + texture catalog (not a sprite catalog) before
activating. See the sprite-vs-3D root-cause note (this session).
Refinement (batch-6 Si pass, 2026-07-25) — SUPERSEDES the 2026-06-16 READY block
(sprite-era, invalidated by D-244) and the 2026-06-16 HELD note. T-1050 (catalog +
conventions.md + manifest.json) and T-1052 (palette.md style bible) are both DONE
(PR #211, merged 2026-07-25) — the conventions this ticket needed to consume now
exist and are read, not hypothetical.
SCOPE, 3D REALITY: T-961 breaks into per-asset-class stories, each gated on a
one-page brief. A "written brief" is now a short doc that states, per class:
(1) scope — which manifest category (furniture|props) and which D-235 ObjectTag
axis if any; (2) visual reference — cites palette.md §3''s hue/grain/weathering
register for the relevant token(s), or palette.md §2 toon/PBR default for classes
with no D-235 token; (3) naming — conventions.md §1 model naming
(<category>_<descriptor>.glb), §2 mask sidecar if tintable, §4 footprint_tiles if
multi-tile; (4) generation path — /image-gen concept → /glb-gen (Trellis) →
postprocess → promotion into client/assets/models/{furniture,props}/ +
manifest.json entry (conventions.md, models.md); (5) acceptance — N assets at
"planned" status registered in the manifest with a real manifest.json entry,
status flips as production proceeds (models.md''s shared status vocab).
BRIEF TEMPLATE (create docs/assets/_templates/asset-brief.md): the five headers
above (scope / visual reference / naming / gen path / acceptance), one page,
adopting docs/_templates/design.md frontmatter. Do NOT reuse _templates/sprite.md
(SPR/FUR/TILE/ICO/FX prefixes) — explicitly retired by D-244/T-1050.
CLASS LIST — station walls, rural walls, doors, floors, furniture/props already
have partial prior art (station walls: wall_structural GLB exists unrendered per
the 2026-06-12 audit matrix; doors: DoorSpec logical model exists, T-979/T-998;
floors: D-228 FloorMaterial axis exists) — each gets a real story + brief, citing
its relevant D-235 axis (wall/roof/facade/street) or, for doors/floors/furniture,
the non-ObjectTag conventions (mask sidecars, footprint_tiles).
STREET FURNITURE / SIGNAGE-SCREENS / RURAL STRUCTURES (lamp posts, barns,
TVs, billboards — T-1051''s four classes): RECOMMEND FORMAL DEFERRAL, not real
briefs, this batch. Rationale: Phase 4''s deliverable is "deterministic
walkable-world data + per-layer Atlas maps + asset catalog" (cascade table) —
outside-in generation (D-166) needs building EXTERIOR templates
(D-235 wall/roof/facade/street axes) to make districts/blocks walkable and
Atlas-legible. Lamp posts (street furniture), TVs/billboards (signage/screens
placed as interior or exterior set-dressing), and barns (a rural building
*type*, not a wall/roof/facade axis token) are furniture-tier detail objects
that populate a walkable exterior once it exists — they read as Phase 6
"detail coloring / room-level population" (cascade table) or, at best, late
Phase 4 polish, not blocking asset classes for getting the world walkable.
Zero repo hits exist for any of the four beyond epic text (T-1049''s own 2026-06-12
audit matrix, unchanged) — there is no partial work to protect by deferring.
Treat identically to Q-067''s car deferral: file a deferral note on the story
(or fold into one placeholder story "T-1051 classes — deferred pending Phase 4
exterior-generation completion"), do not write real briefs now. Distinguish from
cars only in degree, not in kind — Q-067 is a still-open decision question
(vehicle simulation is a bigger unresolved design surface); the T-1051 four are
simpler set-dressing whose deferral needs no open question, just a scope note,
because nothing about their eventual brief depends on an undecided mechanic.
Q-119 (generic-parent placeholder set as milestone-0) is STILL OPEN (unresolved
as of this pass) — unchanged from the prior block: not a blocker, sequencing-only.
Each relevant story notes the generic_wall/roof/facade/street (+ generic
door/floor/furniture) parents as milestone-0 IF Q-119 resolves yes.
READ before filing: docs/assets/visual/{conventions,palette,models,textures,
artwork}.md (T-1050/T-1052, merged), D-244, D-257, D-235 (ratified token list),
D-243 (footprint unit), docs/architecture/character-asset-organization.md
(mask/manifest precedent).
VERDICT: READY (activation = batch 6; the T-1051 deferral recommendation above is
ratified at activation, then T-1051 is updated to match the ruling this ticket records).
Lead ratification (batch-6 activation, 2026-07-25): the refinement block''s T-1051 recommendation is RATIFIED — lamp posts, barns, TVs, billboards get a formal Q-067-style deferral, not briefs. Implementation of this ticket authors the deferral note (four classes, rationale, Q-067 precedent, ''revisit when Phase-4 exterior generation completes'') alongside the real briefs; T-1051''s own deliverable (''or record formal deferral'') is satisfied by that note and T-1051 closes when it lands. Child story tickets under T-961: the implementing agent DRAFTS the story list (title + one-line scope + brief path per class) in its report; the lead creates them on main at merge (avoids cross-vault worktree pql writes, FR-4). Surfaced to Jeroen in the batch report for cheap veto.', 'in_progress', 'high', 'araminta', 'visual', NULL, '2026-06-12 10:40:58', '2026-07-25 20:54:32.546', NULL, 'c4fa92cd096b4a796ed72b61468e418d', 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 ('06FBPTXPZ0ZTM1QQXD5K972CMM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Rewrite docs/briefings/araminta.md around the Phase-4 asset pipeline', '(description follows in first append)
Refinement (batch-6 Si pass, 2026-07-25) — READY.
WHAT''S WRONG WITH THE CURRENT BRIEFING: frozen at the 2026-03-13 v0.2 pivot.
(1) Frames the role around "v0.2 Pivot — Key Changes" — v0.2 is formally dropped
("No scoping negotiations. Build the base systems fully"), so the framing section
is obsolete, not just outdated. (2) Assumes the sprite-era pipeline throughout
("Phase 1-3 placeholder art pipeline," "sprite replacements" in Phase 4) —
contradicted by D-244 (3D objects in-world, no per-object sprite layer) and D-149
(live 3D characters). (3) Cites D-019 as simply "top-down art style" with no note
that D-148 superseded it for gameplay (palette.md §1 had to correct exactly this
conflation in its own stub — the briefing repeats the same error). (4) Zone visual
grammar / tile palette framing predates D-235''s ObjectTag vocabulary and D-232''s
trait-template catalog entirely. (5) Character customisation section (D-126,
Q-WTF-039/040) predates the shipped D-159D-164 body-type + slot-compositing
system and the wardrobe/garment-token pipeline — describes a design question that
has since been built. (6) No mention of the Atlas at all (D-255 stepped ladder,
D-169/D-170 implant UI) despite araminta being a named co-designer of D-255''s
named-feature encoding/payload schema/lakes work.
WHAT THE REWRITE MUST CONTAIN:
- Role framing: visual art direction for the Phase-4 asset pipeline + Atlas
map-art rendering (not "generator spike prerequisites").
- palette.md (T-1052, merged) as the SINGLE LIVE STYLE AUTHORITY — point to it,
do not restate camera/color/material content (avoids a second drifting copy;
visual-grammar-v01.md / workshop-outcomes.md supersession is palette.md §5''s
to state, the briefing defers).
- conventions.md (T-1050, merged) for naming/mask/manifest mechanics — note the
category-first naming ruling (conventions.md §1) and the D-257 toon/PBR ruling
with glazing carve-out as HER standing rulings she''ll be asked to extend.
- D-235 ObjectTag vocabulary as the logical layer she renders a look on top of —
co-maintained with Miri per D-235''s own text.
- The character wardrobe pipeline (character-asset-organization.md) as the proven
reference model conventions.md imitates (mask sidecars, manifest schema).
- D-255 stepped Atlas as active/ongoing work she co-designed.
- Current open items: T-1049 (per-class asset stories, re-scoped this batch), the
D-257 glazing shader implementation (unassigned per palette.md §2.3), Q-119
(generic-parent milestone question, hers to weigh in on per D-235
co-maintenance).
- Drop: v0.2 pivot section, Q-WTF-039/040, tycoon-bookmark/apartment-wakeup
framing, and the v0.2-era decision list (D-114/D-117/D-119/D-122/D-128/D-131/
D-135) — verify each against current decision status before dropping outright;
a few (D-033 entity color, D-126 customisation dimensions) may still have live
sub-parts even though the surrounding section is obsolete.
ACCEPTANCE: rewritten briefing reflects the current Phase-4 landscape (D-235,
D-244, D-257, D-255), points to palette.md/conventions.md as live authorities
rather than duplicating content, drops v0.2-pivot and superseded-question
framing, lists araminta''s actual current/open tickets.
SEQUENCING: independent of T-1049 (different files, no shared input) — both can
run in the same batch.', 'in_progress', 'medium', 'qatux', 'visual', NULL, '2026-06-12 10:40:59', '2026-07-25 20:54:36.535', NULL, '23d0250d37a3a109902cb6e96adf5ca2', 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 ('06FSP4MX7C66N76V31EB88YC78', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Sweep stale D-126/D-131 citations — decision-ID drift audit', 'Found during the T-1053 briefing rewrite (2026-07-25): D-126 and D-131 have been repurposed to content unrelated to what v0.2-era documents cite them for. The old araminta briefing cited ''D-126 = character customisation dimensions'' — current D-126 reads ''Groundhog Day alarm clock homage — first game day only'' (the customisation work actually shipped as D-159D-164). The old briefing cited ''D-131 = apartment/insert first-moment priority'' — current D-131 is ''broad economic verb vocabulary (life verbs, not tycoon-specific)''. This is ID-drift, not staleness: any document still citing the old meanings cites the WRONG decision entirely. Task: (1) first establish the MECHANISM — were these IDs renumbered during the DQR-tree migration, or were the era documents mis-cited at write time? If renumbering, derive the full affected-ID set from the migration mapping, not just these two. (2) Repo-wide grep for D-126/D-131 (and any other affected IDs) across docs/, wiki/, governance/ prose, and .claude/, classifying each hit as current-meaning (fine) vs era-meaning (fix or annotate). (3) Fix or annotate the stale citations; note the outcome here. The rewritten araminta.md (T-1053) is already clean. Clerk-profile work — D-record consistency audit.', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-25 20:58:36.219', '2026-07-25 20:58:36.219', NULL, '3d21e7b98f04d2b2cb9c63d0f6c5f470', 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 ('06FBPTXPE8FR7TGEM7X6QVPV4M', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Refine T-961 into per-asset-class stories, each gated on a written brief', '(description follows in first append)
Filed 2026-06-12 from the fable-ous.md audit (S-43; citations adversarially verified).
T-961 (two-sentence description, no acceptance criteria, no brief links) is the only asset-production ticket under T-750, while the epic names eight classes; cars, barns, TVs, billboards appear in NO ticket at all (verified against the full corpus). Refine into child stories per class — station walls, rural walls, doors, floors, furniture/props, street furniture (lamp posts), signage/screens (billboards/TVs), rural structures (barns) — each gated on a one-page brief; explicitly record which classes are deferred (cars: see Q-067 audit note). Decide Q-119 (generic-parent placeholder set as first milestone) before sequencing.
Audit per-class status matrix (verified 2026-06-12):
# docs/assets/ survey (13 files + downloads/)
| File | One-line summary |
|---|---|
| README.md | Master index: audio/visual/video pipelines, status vocabulary (planned/in-progress/placeholder/final), structure that promises visual/{sprites,furniture,tilesets,icons,effects}.md |
| _templates/audio.md | Row + detailed-entry template for audio assets |
| _templates/sprite.md | Row + detailed-entry template for sprites (ID prefixes SPR/FUR/TILE/ICO/FX, status, method, directions columns) |
| _templates/video.md | Row + detailed-entry template for video |
| audio/README.md | ACTIVE production-status catalog — per-category counts table (12 assets tracked), generation tools, bus architecture |
| audio/palette.md | Sonic style bible |
| audio/ambient.md / sfx.md / ui.md | Per-category audio asset tables |
| visual/README.md | STUB — links 5 category files that do not exist, all counts 0 |
| visual/palette.md | STUB — 4 bullet decisions (D-019/D-033/D-045, "functional warmth"), defers to art-direction workshop |
| video/README.md / palette.md | Stubs |
| downloads/ | Purchased character sources (Quaternius Universal Base Characters, Modular Character Outfits FBX) — characters only |
# Per-asset-class matrix
| Class | Written brief | Style guide | Naming/res conventions | Gen path | Produced assets | Tickets |
|---|---|---|---|---|---|---|
| Walls (station) | art-direction workshop-outcomes.md §3.3 (v0.1.1 set, pre-cascade) + docs/design/placeholder-art-spec-v01.md §2.1 | visual-grammar-v01.md §1/§4.3, workshop-outcomes §1.8 era palette (partly superseded by D-235), D-235 WallMaterial vocab | renderer/README.md:80-95 (`<model>_<dir>_<res>.png`, 1024→256→64 chain) | working — renderer/models/wall_structural.tscn rendered | wall_structural ×4 dirs in client/assets/sprites/; wall_bar_green model unrendered; 2 textures | T-961 (backlog), T-988 (token vocab, backlog) |
| Walls (rural) | none found | D-235 tokens only (timber/rammed-earth etc.) | renderer/README.md (generic) | same path, unused | none | T-961 text mention only |
| Doors | placeholder-art-spec-v01.md §2.1 (procedural rects, v0.1) + workshop-outcomes §3.3 `door_sliding` | none for generated world | none for open/closed state frames | same path, unused | none | T-979/T-998 (DoorSpec — logical only), T-961 |
| Floors | placeholder-art-spec-v01.md §2.1 + workshop-outcomes §3.3 (4 floor sprites) | D-228 FloorMaterial axis (open vocab) | 64px visual tile (placeholder-art-spec §1) | same path, unused | none — client/assets/tilesets/ is .gitkeep only | T-959 (tile fill, logical), T-961 |
| Styled buildings | D-232 + D-235 + wiki/economics/architecture_trait_catalog.toml (T-1005 done, ~35 visual bundles) | D-235 material/color-register grammar — tokens only, no rendered art direction | token→fallback-parent chain (catalog header :34-44) | no model/texture path defined for building exteriors | none | T-977, T-988, T-994, T-1004 (all backlog), T-1005 (done) |
| Furniture | docs/assets/visual/furniture.md MISSING (linked); workshop-outcomes §3.3 lists 8-10 objects (pre-cascade) | era table §1.8 only | character-asset-organization.md explicitly excludes furniture ("follow Trellis spike conventions") | proven in spike | spikes/3dpipeline/models/furniture/ ×3 GLB (unpromoted); zero in client/assets | T-961, T-995 (ObjectTag vocab, backlog) |
| Items | docs/design/icon-set-v01.md (HUD/inventory icons only) | D-086 icon constraints | SVG icon_ prefix (client/assets/icons/) | n/a for icons | 13 SVGs in client/assets/icons/ | none for in-world item sprites |
| Cars/vehicles | none found | none | none | spike only | spikes/3dpipeline/models/props/vw_beetle.glb (spike) | none — Q-067 open ("driveable vs NPC traffic?") |
| Lamp posts | none found (zero repo hits) | none | none | unused | none | T-961 text mention only |
| Barns | none found (only T-750 epic text) | none | none | unused | none | none |
| TVs | none found (only T-750 epic text) | none | none | unused | none | none |
| Billboards | none found (only T-750 epic text; other hits = LOD impostors) | none | none | unused | none | none |
| Characters (reference class — the working model) | docs/architecture/character-asset-organization.md + docs/design/character-visuals-spec.md | yes | full (manifest.json, seg_ naming, body-type keys) | proven end-to-end | 1002 files in client/assets/characters/ | done |
# Generation path (verified files)
image-gen (.claude/skills/image-gen/SKILL.md + scripts/image_connector.py, Gemini) → glb-gen (SKILL.md + tooling/db/trellis_connector.py + tooling/trellis-batch.sh + Blender postprocess scripts) → sprite-gen (SKILL.md + scripts/render.sh) → renderer/ (project.godot, render_scene.tscn, render_export.gd; D-019 camera, 3-light rig, 1024/256/64 chain) → client/assets/sprites/. renderer/output/ holds only .gdignore (intermediates gitignored).
# Produced assets in client/assets (non-.import files)
audio 16 · characters 1002 · fonts 3 · icons 13 · planets 7 · sprites 9 (npc_generic ×4, wall_structural ×4, .gitkeep) · theme 1 · tilesets 1 (.gitkeep only)
---
Refinement (2026-06-16, /whats-next Si pass) — READY. This is a PLANNING task: break T-961 into per-class child stories.
CLASS LIST (8 stories, parented under T-961, each gated on a one-page brief): station walls, rural walls, doors, floors, furniture/props, street furniture (lamp posts), signage/screens (TVs/billboards), rural structures (barns).
- CARS (9th class named in T-750): DEFERRED — Q-067 needs a phase decision (Phase-4 deliverable vs Phase-5+) before any car story. File a deferral note, not a story.
- Q-119 (generic-parent placeholder set as milestone-0): NOT a blocker — sequencing only. On each relevant story note that the generic parents (generic_wall/roof/facade/street + generic_door/floor/furniture; D-235 / wiki/economics/architecture_trait_catalog.toml:33-41) are milestone-0 IF Q-119 resolves "yes".
BRIEF TEMPLATE: none exists. Create docs/assets/_templates/asset-brief.md (scope · visual reference · naming/resolution conventions · generation path · acceptance), or adopt docs/_templates/design.md. Each story states where its brief + catalog file live under docs/assets/visual/.
READ before filing: docs/assets/README.md, docs/design/{placeholder-art-spec-v01,visual-grammar-v01}.md, docs/workshops/art-direction-mood-board/workshop-outcomes.md, D-235/D-228/D-232, docs/architecture/character-asset-organization.md (the reference model).
NOTE: T-1050 (sibling) owns the catalog + naming conventions these stories reference — sequence T-1049 to consume T-1050''s conventions where they overlap.
---
HELD 2026-06-16 (separated from the cascade batch; needs re-scoping before resuming).
The asset pipeline this ticket assumes is sprite-centric (docs/assets/visual/sprites.md
"Entities", _templates/sprite.md, SPR/FUR/TILE/ICO/FX prefixes, the /sprite-gen
render-3D->2D-sprites step). Direction correction: we are NOT producing sprites — Trellis
produces 3D objects (.glb) displayed in-world; the only flattened 2D content is TEXTURES.
Re-scope this ticket around a 3D-model + texture catalog (not a sprite catalog) before
activating. See the sprite-vs-3D root-cause note (this session).
Refinement (batch-6 Si pass, 2026-07-25) — SUPERSEDES the 2026-06-16 READY block
(sprite-era, invalidated by D-244) and the 2026-06-16 HELD note. T-1050 (catalog +
conventions.md + manifest.json) and T-1052 (palette.md style bible) are both DONE
(PR #211, merged 2026-07-25) — the conventions this ticket needed to consume now
exist and are read, not hypothetical.
SCOPE, 3D REALITY: T-961 breaks into per-asset-class stories, each gated on a
one-page brief. A "written brief" is now a short doc that states, per class:
(1) scope — which manifest category (furniture|props) and which D-235 ObjectTag
axis if any; (2) visual reference — cites palette.md §3''s hue/grain/weathering
register for the relevant token(s), or palette.md §2 toon/PBR default for classes
with no D-235 token; (3) naming — conventions.md §1 model naming
(<category>_<descriptor>.glb), §2 mask sidecar if tintable, §4 footprint_tiles if
multi-tile; (4) generation path — /image-gen concept → /glb-gen (Trellis) →
postprocess → promotion into client/assets/models/{furniture,props}/ +
manifest.json entry (conventions.md, models.md); (5) acceptance — N assets at
"planned" status registered in the manifest with a real manifest.json entry,
status flips as production proceeds (models.md''s shared status vocab).
BRIEF TEMPLATE (create docs/assets/_templates/asset-brief.md): the five headers
above (scope / visual reference / naming / gen path / acceptance), one page,
adopting docs/_templates/design.md frontmatter. Do NOT reuse _templates/sprite.md
(SPR/FUR/TILE/ICO/FX prefixes) — explicitly retired by D-244/T-1050.
CLASS LIST — station walls, rural walls, doors, floors, furniture/props already
have partial prior art (station walls: wall_structural GLB exists unrendered per
the 2026-06-12 audit matrix; doors: DoorSpec logical model exists, T-979/T-998;
floors: D-228 FloorMaterial axis exists) — each gets a real story + brief, citing
its relevant D-235 axis (wall/roof/facade/street) or, for doors/floors/furniture,
the non-ObjectTag conventions (mask sidecars, footprint_tiles).
STREET FURNITURE / SIGNAGE-SCREENS / RURAL STRUCTURES (lamp posts, barns,
TVs, billboards — T-1051''s four classes): RECOMMEND FORMAL DEFERRAL, not real
briefs, this batch. Rationale: Phase 4''s deliverable is "deterministic
walkable-world data + per-layer Atlas maps + asset catalog" (cascade table) —
outside-in generation (D-166) needs building EXTERIOR templates
(D-235 wall/roof/facade/street axes) to make districts/blocks walkable and
Atlas-legible. Lamp posts (street furniture), TVs/billboards (signage/screens
placed as interior or exterior set-dressing), and barns (a rural building
*type*, not a wall/roof/facade axis token) are furniture-tier detail objects
that populate a walkable exterior once it exists — they read as Phase 6
"detail coloring / room-level population" (cascade table) or, at best, late
Phase 4 polish, not blocking asset classes for getting the world walkable.
Zero repo hits exist for any of the four beyond epic text (T-1049''s own 2026-06-12
audit matrix, unchanged) — there is no partial work to protect by deferring.
Treat identically to Q-067''s car deferral: file a deferral note on the story
(or fold into one placeholder story "T-1051 classes — deferred pending Phase 4
exterior-generation completion"), do not write real briefs now. Distinguish from
cars only in degree, not in kind — Q-067 is a still-open decision question
(vehicle simulation is a bigger unresolved design surface); the T-1051 four are
simpler set-dressing whose deferral needs no open question, just a scope note,
because nothing about their eventual brief depends on an undecided mechanic.
Q-119 (generic-parent placeholder set as milestone-0) is STILL OPEN (unresolved
as of this pass) — unchanged from the prior block: not a blocker, sequencing-only.
Each relevant story notes the generic_wall/roof/facade/street (+ generic
door/floor/furniture) parents as milestone-0 IF Q-119 resolves yes.
READ before filing: docs/assets/visual/{conventions,palette,models,textures,
artwork}.md (T-1050/T-1052, merged), D-244, D-257, D-235 (ratified token list),
D-243 (footprint unit), docs/architecture/character-asset-organization.md
(mask/manifest precedent).
VERDICT: READY (activation = batch 6; the T-1051 deferral recommendation above is
ratified at activation, then T-1051 is updated to match the ruling this ticket records).
Lead ratification (batch-6 activation, 2026-07-25): the refinement block''s T-1051 recommendation is RATIFIED — lamp posts, barns, TVs, billboards get a formal Q-067-style deferral, not briefs. Implementation of this ticket authors the deferral note (four classes, rationale, Q-067 precedent, ''revisit when Phase-4 exterior generation completes'') alongside the real briefs; T-1051''s own deliverable (''or record formal deferral'') is satisfied by that note and T-1051 closes when it lands. Child story tickets under T-961: the implementing agent DRAFTS the story list (title + one-line scope + brief path per class) in its report; the lead creates them on main at merge (avoids cross-vault worktree pql writes, FR-4). Surfaced to Jeroen in the batch report for cheap veto.', 'review', 'high', 'araminta', 'visual', NULL, '2026-06-12 10:40:58', '2026-07-25 21:01:13.522', NULL, '98f91a60b15bd167558f11f9ff807852', 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 ('06FBPTXPZ0ZTM1QQXD5K972CMM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Rewrite docs/briefings/araminta.md around the Phase-4 asset pipeline', '(description follows in first append)
Refinement (batch-6 Si pass, 2026-07-25) — READY.
WHAT''S WRONG WITH THE CURRENT BRIEFING: frozen at the 2026-03-13 v0.2 pivot.
(1) Frames the role around "v0.2 Pivot — Key Changes" — v0.2 is formally dropped
("No scoping negotiations. Build the base systems fully"), so the framing section
is obsolete, not just outdated. (2) Assumes the sprite-era pipeline throughout
("Phase 1-3 placeholder art pipeline," "sprite replacements" in Phase 4) —
contradicted by D-244 (3D objects in-world, no per-object sprite layer) and D-149
(live 3D characters). (3) Cites D-019 as simply "top-down art style" with no note
that D-148 superseded it for gameplay (palette.md §1 had to correct exactly this
conflation in its own stub — the briefing repeats the same error). (4) Zone visual
grammar / tile palette framing predates D-235''s ObjectTag vocabulary and D-232''s
trait-template catalog entirely. (5) Character customisation section (D-126,
Q-WTF-039/040) predates the shipped D-159D-164 body-type + slot-compositing
system and the wardrobe/garment-token pipeline — describes a design question that
has since been built. (6) No mention of the Atlas at all (D-255 stepped ladder,
D-169/D-170 implant UI) despite araminta being a named co-designer of D-255''s
named-feature encoding/payload schema/lakes work.
WHAT THE REWRITE MUST CONTAIN:
- Role framing: visual art direction for the Phase-4 asset pipeline + Atlas
map-art rendering (not "generator spike prerequisites").
- palette.md (T-1052, merged) as the SINGLE LIVE STYLE AUTHORITY — point to it,
do not restate camera/color/material content (avoids a second drifting copy;
visual-grammar-v01.md / workshop-outcomes.md supersession is palette.md §5''s
to state, the briefing defers).
- conventions.md (T-1050, merged) for naming/mask/manifest mechanics — note the
category-first naming ruling (conventions.md §1) and the D-257 toon/PBR ruling
with glazing carve-out as HER standing rulings she''ll be asked to extend.
- D-235 ObjectTag vocabulary as the logical layer she renders a look on top of —
co-maintained with Miri per D-235''s own text.
- The character wardrobe pipeline (character-asset-organization.md) as the proven
reference model conventions.md imitates (mask sidecars, manifest schema).
- D-255 stepped Atlas as active/ongoing work she co-designed.
- Current open items: T-1049 (per-class asset stories, re-scoped this batch), the
D-257 glazing shader implementation (unassigned per palette.md §2.3), Q-119
(generic-parent milestone question, hers to weigh in on per D-235
co-maintenance).
- Drop: v0.2 pivot section, Q-WTF-039/040, tycoon-bookmark/apartment-wakeup
framing, and the v0.2-era decision list (D-114/D-117/D-119/D-122/D-128/D-131/
D-135) — verify each against current decision status before dropping outright;
a few (D-033 entity color, D-126 customisation dimensions) may still have live
sub-parts even though the surrounding section is obsolete.
ACCEPTANCE: rewritten briefing reflects the current Phase-4 landscape (D-235,
D-244, D-257, D-255), points to palette.md/conventions.md as live authorities
rather than duplicating content, drops v0.2-pivot and superseded-question
framing, lists araminta''s actual current/open tickets.
SEQUENCING: independent of T-1049 (different files, no shared input) — both can
run in the same batch.', 'review', 'medium', 'qatux', 'visual', NULL, '2026-06-12 10:40:59', '2026-07-25 21:01:13.529', NULL, '39fd0f0834893092949d4ccbb46647cf', 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 ('06FSP7EJPBDP1JB4FFMG0BV11M', 'story', '06FB0TNSRXDCJS4P9Z5JQ1DAAM', 'Station walls asset production', 'Per-class production story (T-1049 breakdown, batch 6). Brief: docs/assets/briefs/station-walls.md — the one-page contract: D-235 wall-axis tokens for station/urban/industrial types, palette.md §3 register rows, conventions.md naming/mask/texture rules, image-gen -> glb-gen -> promotion into client/assets/models/ + manifest.json. Prior art: renderer/models/wall_structural.tscn (unrendered GLB) — re-promote through the current 3D pipeline. Acceptance per the brief (starter assets enter manifest.json at ''planned''). Roof/facade/street textures ride this class family''s texture convention as a follow-on (deferred.md axis-completeness note). Q-119 generic-parent milestone-0 conditionality noted in the brief, unresolved.', 'backlog', 'medium', NULL, 'visual', NULL, '2026-07-25 21:10:50.802', '2026-07-25 21:10:50.802', NULL, 'de3474e555baaa5182138e98d70b2b42', 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 ('06FSP7F5THEPJWGV04B4542SFR', 'story', '06FB0TNSRXDCJS4P9Z5JQ1DAAM', 'Rural walls asset production', 'Per-class production story (T-1049 breakdown, batch 6). Brief: docs/assets/briefs/rural-walls.md — D-235 wall-axis tokens for rural/vernacular types (stone/timber/rammed-earth/stucco/brick), palette.md §3 register rows, conventions.md rules, image-gen -> glb-gen -> promotion path. Clean slate, no prior art. Acceptance per the brief. Q-119 conditionality noted, unresolved.', 'backlog', 'medium', NULL, 'visual', NULL, '2026-07-25 21:10:55.700', '2026-07-25 21:10:55.700', NULL, 'a55a60ef67f9f81cd46804f68704dc07', 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 ('06FSP7FZ4F20AMWCXSP23Q5H54', 'story', '06FB0TNSRXDCJS4P9Z5JQ1DAAM', 'Doors asset production', 'Per-class production story (T-1049 breakdown, batch 6). Brief: docs/assets/briefs/doors.md — door models (no D-235 axis; D-257 toon default), conventions.md rules incl. the BINDING negative constraint: no baked state-file variants (door_open/door_closed) — D-231''s initial_state (Open/Closed/Locked/Sealed) is runtime; the credential field (None/TemporalWindow/Corporate/Resident/Authority/Social) is independent metadata. The Phase-5 rendering mechanism attaches to initial_state and is explicitly out of this story''s scope. Visual asset only — does not touch T-979/T-998 DoorSpec derivation logic. Acceptance per the brief.', 'backlog', 'medium', NULL, 'visual', NULL, '2026-07-25 21:11:02.179', '2026-07-25 21:11:02.179', NULL, '9c04e928128ecaca82310e838055ca28', 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 ('06FSP7GX9J2SSHE7ZJB3PH0FEW', 'story', '06FB0TNSRXDCJS4P9Z5JQ1DAAM', 'Floors asset production', 'Per-class production story (T-1049 breakdown, batch 6). Brief: docs/assets/briefs/floors.md — floor surface textures on D-228''s FloorMaterial axis (distinct from D-235 StreetSurface), texture-only pipeline (no Trellis step), conventions.md D-235-token-style texture naming, palette.md treatment. Acceptance per the brief. Q-119 conditionality noted, unresolved.', 'backlog', 'medium', NULL, 'visual', NULL, '2026-07-25 21:11:09.900', '2026-07-25 21:11:09.900', NULL, '9cc31cc39392291e6ee4ae075428bea0', 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 ('06FSP7HT5ES8D83VZA6ETACRAW', 'story', '06FB0TNSRXDCJS4P9Z5JQ1DAAM', 'Furniture/props asset production', 'Per-class production story (T-1049 breakdown, batch 6). Brief: docs/assets/briefs/furniture-props.md — interior furniture + small props (manifest categories furniture|props), D-257 toon treatment, conventions.md naming/mask/footprint_tiles rules. FIRST acceptance: promote + rename the 4 promotable spike GLBs (baroque_table, modernist_chair, scifi_desk + props/lion_statue) into client/assets/models/ + manifest.json per D-241 — no new generation needed; vw_beetle stays excluded (Q-067 car deferral, see briefs/deferred.md). Note: spike GLB binaries are gitignored — regenerate or restore locally at promotion time per the spike README.', 'backlog', 'medium', NULL, 'visual', NULL, '2026-07-25 21:11:17.291', '2026-07-25 21:11:17.291', NULL, '6157e2bfc58eb3ce34ba64396e4bd162', 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 ('06FBPTXPPHC9FZ4T0GNKR615PR', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Author one-page briefs (or record formal deferral) for lamp posts, barns, TVs, billboards', '(description follows in first append)
Refinement (batch-5 Si pass, 2026-07-25) — GAPS. Formally deferred rather than refined into an
actionable brief-writing task now.
WHY: T-1051 was filed as a child of T-1049''s per-class breakdown (the fable-ous.md audit T-1049 cites,
2026-06-12). T-1049 itself is HELD pending the same sprite-to-3D re-scope as T-1050 (its "Refinement
2026-06-16 — READY" block still frames the per-class stories, briefs, and catalog files in sprite-era
language, and its own text says "sequence T-1049 to consume T-1050''s conventions where they overlap" —
so T-1049 cannot be un-held before T-1050 lands). T-1051 is one layer further down that same
dependency: it cannot be meaningfully scoped (what does a "one-page brief" even template against?)
until (1) T-1050 lands conventions.md + the catalog structure a brief would reference, AND (2) T-1049
is re-scoped and un-held (it owns the per-class story breakdown T-1051 is part of).
Zero repo hits exist for lamp posts, barns, TVs, or billboards beyond the T-750 epic description text
(confirmed by T-1049''s own audit matrix) — there is no existing partial work this ticket would be
resuming, so there is no cost to deferring it cleanly.
DISPOSITION: blocker edge wired (T-1051 blocked-by T-1049). When T-1049 is re-scoped (a future Si
pass, after T-1050 lands), that re-scope explicitly decides whether lamp posts/barns/TVs/billboards
get real briefs or a formal deferral note (T-1049''s existing block already leaned toward "explicitly
record which classes are deferred" for cars via Q-067 — the same treatment likely applies to these
four, but that is T-1049''s call when re-scoped, not this ticket''s in isolation).
DELIVERED BY DEFERRAL (2026-07-25, PR #213): docs/assets/briefs/deferred.md is the formal deferral record this ticket''s title asked for — lamp posts, barns, TVs, billboards each recorded with rationale (Phase-4 walkability needs exteriors; these are set-dressing for a walkable world — Phase 6 or late-4 polish), the Q-067 precedent, and the revisit trigger (Phase-4 exterior generation complete). Ratified on T-1049 (batch-6 activation), reviewed by Tyre (PR #213 — including his ruling that no D-record is needed) with the axis-completeness note ensuring the register accounts for all four D-235 axes. The ''or record formal deferral'' arm of this ticket''s deliverable is satisfied; closing done.', 'backlog', 'medium', NULL, 'visual', NULL, '2026-06-12 10:40:59', '2026-07-25 21:11:23.912', NULL, '8d2a1c6f1e65787cd02d4f2daf01db8a', 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 ('06FBPTXPPHC9FZ4T0GNKR615PR', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Author one-page briefs (or record formal deferral) for lamp posts, barns, TVs, billboards', '(description follows in first append)
Refinement (batch-5 Si pass, 2026-07-25) — GAPS. Formally deferred rather than refined into an
actionable brief-writing task now.
WHY: T-1051 was filed as a child of T-1049''s per-class breakdown (the fable-ous.md audit T-1049 cites,
2026-06-12). T-1049 itself is HELD pending the same sprite-to-3D re-scope as T-1050 (its "Refinement
2026-06-16 — READY" block still frames the per-class stories, briefs, and catalog files in sprite-era
language, and its own text says "sequence T-1049 to consume T-1050''s conventions where they overlap" —
so T-1049 cannot be un-held before T-1050 lands). T-1051 is one layer further down that same
dependency: it cannot be meaningfully scoped (what does a "one-page brief" even template against?)
until (1) T-1050 lands conventions.md + the catalog structure a brief would reference, AND (2) T-1049
is re-scoped and un-held (it owns the per-class story breakdown T-1051 is part of).
Zero repo hits exist for lamp posts, barns, TVs, or billboards beyond the T-750 epic description text
(confirmed by T-1049''s own audit matrix) — there is no existing partial work this ticket would be
resuming, so there is no cost to deferring it cleanly.
DISPOSITION: blocker edge wired (T-1051 blocked-by T-1049). When T-1049 is re-scoped (a future Si
pass, after T-1050 lands), that re-scope explicitly decides whether lamp posts/barns/TVs/billboards
get real briefs or a formal deferral note (T-1049''s existing block already leaned toward "explicitly
record which classes are deferred" for cars via Q-067 — the same treatment likely applies to these
four, but that is T-1049''s call when re-scoped, not this ticket''s in isolation).
DELIVERED BY DEFERRAL (2026-07-25, PR #213): docs/assets/briefs/deferred.md is the formal deferral record this ticket''s title asked for — lamp posts, barns, TVs, billboards each recorded with rationale (Phase-4 walkability needs exteriors; these are set-dressing for a walkable world — Phase 6 or late-4 polish), the Q-067 precedent, and the revisit trigger (Phase-4 exterior generation complete). Ratified on T-1049 (batch-6 activation), reviewed by Tyre (PR #213 — including his ruling that no D-record is needed) with the axis-completeness note ensuring the register accounts for all four D-235 axes. The ''or record formal deferral'' arm of this ticket''s deliverable is satisfied; closing done.', 'done', 'medium', NULL, 'visual', NULL, '2026-06-12 10:40:59', '2026-07-25 21:11:27.653', NULL, 'e37d290f426f3bf7c1ab4d979f29088d', 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 ('06FBPTXPE8FR7TGEM7X6QVPV4M', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Refine T-961 into per-asset-class stories, each gated on a written brief', '(description follows in first append)
Filed 2026-06-12 from the fable-ous.md audit (S-43; citations adversarially verified).
T-961 (two-sentence description, no acceptance criteria, no brief links) is the only asset-production ticket under T-750, while the epic names eight classes; cars, barns, TVs, billboards appear in NO ticket at all (verified against the full corpus). Refine into child stories per class — station walls, rural walls, doors, floors, furniture/props, street furniture (lamp posts), signage/screens (billboards/TVs), rural structures (barns) — each gated on a one-page brief; explicitly record which classes are deferred (cars: see Q-067 audit note). Decide Q-119 (generic-parent placeholder set as first milestone) before sequencing.
Audit per-class status matrix (verified 2026-06-12):
# docs/assets/ survey (13 files + downloads/)
| File | One-line summary |
|---|---|
| README.md | Master index: audio/visual/video pipelines, status vocabulary (planned/in-progress/placeholder/final), structure that promises visual/{sprites,furniture,tilesets,icons,effects}.md |
| _templates/audio.md | Row + detailed-entry template for audio assets |
| _templates/sprite.md | Row + detailed-entry template for sprites (ID prefixes SPR/FUR/TILE/ICO/FX, status, method, directions columns) |
| _templates/video.md | Row + detailed-entry template for video |
| audio/README.md | ACTIVE production-status catalog — per-category counts table (12 assets tracked), generation tools, bus architecture |
| audio/palette.md | Sonic style bible |
| audio/ambient.md / sfx.md / ui.md | Per-category audio asset tables |
| visual/README.md | STUB — links 5 category files that do not exist, all counts 0 |
| visual/palette.md | STUB — 4 bullet decisions (D-019/D-033/D-045, "functional warmth"), defers to art-direction workshop |
| video/README.md / palette.md | Stubs |
| downloads/ | Purchased character sources (Quaternius Universal Base Characters, Modular Character Outfits FBX) — characters only |
# Per-asset-class matrix
| Class | Written brief | Style guide | Naming/res conventions | Gen path | Produced assets | Tickets |
|---|---|---|---|---|---|---|
| Walls (station) | art-direction workshop-outcomes.md §3.3 (v0.1.1 set, pre-cascade) + docs/design/placeholder-art-spec-v01.md §2.1 | visual-grammar-v01.md §1/§4.3, workshop-outcomes §1.8 era palette (partly superseded by D-235), D-235 WallMaterial vocab | renderer/README.md:80-95 (`<model>_<dir>_<res>.png`, 1024→256→64 chain) | working — renderer/models/wall_structural.tscn rendered | wall_structural ×4 dirs in client/assets/sprites/; wall_bar_green model unrendered; 2 textures | T-961 (backlog), T-988 (token vocab, backlog) |
| Walls (rural) | none found | D-235 tokens only (timber/rammed-earth etc.) | renderer/README.md (generic) | same path, unused | none | T-961 text mention only |
| Doors | placeholder-art-spec-v01.md §2.1 (procedural rects, v0.1) + workshop-outcomes §3.3 `door_sliding` | none for generated world | none for open/closed state frames | same path, unused | none | T-979/T-998 (DoorSpec — logical only), T-961 |
| Floors | placeholder-art-spec-v01.md §2.1 + workshop-outcomes §3.3 (4 floor sprites) | D-228 FloorMaterial axis (open vocab) | 64px visual tile (placeholder-art-spec §1) | same path, unused | none — client/assets/tilesets/ is .gitkeep only | T-959 (tile fill, logical), T-961 |
| Styled buildings | D-232 + D-235 + wiki/economics/architecture_trait_catalog.toml (T-1005 done, ~35 visual bundles) | D-235 material/color-register grammar — tokens only, no rendered art direction | token→fallback-parent chain (catalog header :34-44) | no model/texture path defined for building exteriors | none | T-977, T-988, T-994, T-1004 (all backlog), T-1005 (done) |
| Furniture | docs/assets/visual/furniture.md MISSING (linked); workshop-outcomes §3.3 lists 8-10 objects (pre-cascade) | era table §1.8 only | character-asset-organization.md explicitly excludes furniture ("follow Trellis spike conventions") | proven in spike | spikes/3dpipeline/models/furniture/ ×3 GLB (unpromoted); zero in client/assets | T-961, T-995 (ObjectTag vocab, backlog) |
| Items | docs/design/icon-set-v01.md (HUD/inventory icons only) | D-086 icon constraints | SVG icon_ prefix (client/assets/icons/) | n/a for icons | 13 SVGs in client/assets/icons/ | none for in-world item sprites |
| Cars/vehicles | none found | none | none | spike only | spikes/3dpipeline/models/props/vw_beetle.glb (spike) | none — Q-067 open ("driveable vs NPC traffic?") |
| Lamp posts | none found (zero repo hits) | none | none | unused | none | T-961 text mention only |
| Barns | none found (only T-750 epic text) | none | none | unused | none | none |
| TVs | none found (only T-750 epic text) | none | none | unused | none | none |
| Billboards | none found (only T-750 epic text; other hits = LOD impostors) | none | none | unused | none | none |
| Characters (reference class — the working model) | docs/architecture/character-asset-organization.md + docs/design/character-visuals-spec.md | yes | full (manifest.json, seg_ naming, body-type keys) | proven end-to-end | 1002 files in client/assets/characters/ | done |
# Generation path (verified files)
image-gen (.claude/skills/image-gen/SKILL.md + scripts/image_connector.py, Gemini) → glb-gen (SKILL.md + tooling/db/trellis_connector.py + tooling/trellis-batch.sh + Blender postprocess scripts) → sprite-gen (SKILL.md + scripts/render.sh) → renderer/ (project.godot, render_scene.tscn, render_export.gd; D-019 camera, 3-light rig, 1024/256/64 chain) → client/assets/sprites/. renderer/output/ holds only .gdignore (intermediates gitignored).
# Produced assets in client/assets (non-.import files)
audio 16 · characters 1002 · fonts 3 · icons 13 · planets 7 · sprites 9 (npc_generic ×4, wall_structural ×4, .gitkeep) · theme 1 · tilesets 1 (.gitkeep only)
---
Refinement (2026-06-16, /whats-next Si pass) — READY. This is a PLANNING task: break T-961 into per-class child stories.
CLASS LIST (8 stories, parented under T-961, each gated on a one-page brief): station walls, rural walls, doors, floors, furniture/props, street furniture (lamp posts), signage/screens (TVs/billboards), rural structures (barns).
- CARS (9th class named in T-750): DEFERRED — Q-067 needs a phase decision (Phase-4 deliverable vs Phase-5+) before any car story. File a deferral note, not a story.
- Q-119 (generic-parent placeholder set as milestone-0): NOT a blocker — sequencing only. On each relevant story note that the generic parents (generic_wall/roof/facade/street + generic_door/floor/furniture; D-235 / wiki/economics/architecture_trait_catalog.toml:33-41) are milestone-0 IF Q-119 resolves "yes".
BRIEF TEMPLATE: none exists. Create docs/assets/_templates/asset-brief.md (scope · visual reference · naming/resolution conventions · generation path · acceptance), or adopt docs/_templates/design.md. Each story states where its brief + catalog file live under docs/assets/visual/.
READ before filing: docs/assets/README.md, docs/design/{placeholder-art-spec-v01,visual-grammar-v01}.md, docs/workshops/art-direction-mood-board/workshop-outcomes.md, D-235/D-228/D-232, docs/architecture/character-asset-organization.md (the reference model).
NOTE: T-1050 (sibling) owns the catalog + naming conventions these stories reference — sequence T-1049 to consume T-1050''s conventions where they overlap.
---
HELD 2026-06-16 (separated from the cascade batch; needs re-scoping before resuming).
The asset pipeline this ticket assumes is sprite-centric (docs/assets/visual/sprites.md
"Entities", _templates/sprite.md, SPR/FUR/TILE/ICO/FX prefixes, the /sprite-gen
render-3D->2D-sprites step). Direction correction: we are NOT producing sprites — Trellis
produces 3D objects (.glb) displayed in-world; the only flattened 2D content is TEXTURES.
Re-scope this ticket around a 3D-model + texture catalog (not a sprite catalog) before
activating. See the sprite-vs-3D root-cause note (this session).
Refinement (batch-6 Si pass, 2026-07-25) — SUPERSEDES the 2026-06-16 READY block
(sprite-era, invalidated by D-244) and the 2026-06-16 HELD note. T-1050 (catalog +
conventions.md + manifest.json) and T-1052 (palette.md style bible) are both DONE
(PR #211, merged 2026-07-25) — the conventions this ticket needed to consume now
exist and are read, not hypothetical.
SCOPE, 3D REALITY: T-961 breaks into per-asset-class stories, each gated on a
one-page brief. A "written brief" is now a short doc that states, per class:
(1) scope — which manifest category (furniture|props) and which D-235 ObjectTag
axis if any; (2) visual reference — cites palette.md §3''s hue/grain/weathering
register for the relevant token(s), or palette.md §2 toon/PBR default for classes
with no D-235 token; (3) naming — conventions.md §1 model naming
(<category>_<descriptor>.glb), §2 mask sidecar if tintable, §4 footprint_tiles if
multi-tile; (4) generation path — /image-gen concept → /glb-gen (Trellis) →
postprocess → promotion into client/assets/models/{furniture,props}/ +
manifest.json entry (conventions.md, models.md); (5) acceptance — N assets at
"planned" status registered in the manifest with a real manifest.json entry,
status flips as production proceeds (models.md''s shared status vocab).
BRIEF TEMPLATE (create docs/assets/_templates/asset-brief.md): the five headers
above (scope / visual reference / naming / gen path / acceptance), one page,
adopting docs/_templates/design.md frontmatter. Do NOT reuse _templates/sprite.md
(SPR/FUR/TILE/ICO/FX prefixes) — explicitly retired by D-244/T-1050.
CLASS LIST — station walls, rural walls, doors, floors, furniture/props already
have partial prior art (station walls: wall_structural GLB exists unrendered per
the 2026-06-12 audit matrix; doors: DoorSpec logical model exists, T-979/T-998;
floors: D-228 FloorMaterial axis exists) — each gets a real story + brief, citing
its relevant D-235 axis (wall/roof/facade/street) or, for doors/floors/furniture,
the non-ObjectTag conventions (mask sidecars, footprint_tiles).
STREET FURNITURE / SIGNAGE-SCREENS / RURAL STRUCTURES (lamp posts, barns,
TVs, billboards — T-1051''s four classes): RECOMMEND FORMAL DEFERRAL, not real
briefs, this batch. Rationale: Phase 4''s deliverable is "deterministic
walkable-world data + per-layer Atlas maps + asset catalog" (cascade table) —
outside-in generation (D-166) needs building EXTERIOR templates
(D-235 wall/roof/facade/street axes) to make districts/blocks walkable and
Atlas-legible. Lamp posts (street furniture), TVs/billboards (signage/screens
placed as interior or exterior set-dressing), and barns (a rural building
*type*, not a wall/roof/facade axis token) are furniture-tier detail objects
that populate a walkable exterior once it exists — they read as Phase 6
"detail coloring / room-level population" (cascade table) or, at best, late
Phase 4 polish, not blocking asset classes for getting the world walkable.
Zero repo hits exist for any of the four beyond epic text (T-1049''s own 2026-06-12
audit matrix, unchanged) — there is no partial work to protect by deferring.
Treat identically to Q-067''s car deferral: file a deferral note on the story
(or fold into one placeholder story "T-1051 classes — deferred pending Phase 4
exterior-generation completion"), do not write real briefs now. Distinguish from
cars only in degree, not in kind — Q-067 is a still-open decision question
(vehicle simulation is a bigger unresolved design surface); the T-1051 four are
simpler set-dressing whose deferral needs no open question, just a scope note,
because nothing about their eventual brief depends on an undecided mechanic.
Q-119 (generic-parent placeholder set as milestone-0) is STILL OPEN (unresolved
as of this pass) — unchanged from the prior block: not a blocker, sequencing-only.
Each relevant story notes the generic_wall/roof/facade/street (+ generic
door/floor/furniture) parents as milestone-0 IF Q-119 resolves yes.
READ before filing: docs/assets/visual/{conventions,palette,models,textures,
artwork}.md (T-1050/T-1052, merged), D-244, D-257, D-235 (ratified token list),
D-243 (footprint unit), docs/architecture/character-asset-organization.md
(mask/manifest precedent).
VERDICT: READY (activation = batch 6; the T-1051 deferral recommendation above is
ratified at activation, then T-1051 is updated to match the ruling this ticket records).
Lead ratification (batch-6 activation, 2026-07-25): the refinement block''s T-1051 recommendation is RATIFIED — lamp posts, barns, TVs, billboards get a formal Q-067-style deferral, not briefs. Implementation of this ticket authors the deferral note (four classes, rationale, Q-067 precedent, ''revisit when Phase-4 exterior generation completes'') alongside the real briefs; T-1051''s own deliverable (''or record formal deferral'') is satisfied by that note and T-1051 closes when it lands. Child story tickets under T-961: the implementing agent DRAFTS the story list (title + one-line scope + brief path per class) in its report; the lead creates them on main at merge (avoids cross-vault worktree pql writes, FR-4). Surfaced to Jeroen in the batch report for cheap veto.', 'done', 'high', 'araminta', 'visual', NULL, '2026-06-12 10:40:58', '2026-07-25 21:11:27.660', NULL, 'feddf82eb748e9b8106743886529d69f', 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 ('06FBPTXPZ0ZTM1QQXD5K972CMM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Rewrite docs/briefings/araminta.md around the Phase-4 asset pipeline', '(description follows in first append)
Refinement (batch-6 Si pass, 2026-07-25) — READY.
WHAT''S WRONG WITH THE CURRENT BRIEFING: frozen at the 2026-03-13 v0.2 pivot.
(1) Frames the role around "v0.2 Pivot — Key Changes" — v0.2 is formally dropped
("No scoping negotiations. Build the base systems fully"), so the framing section
is obsolete, not just outdated. (2) Assumes the sprite-era pipeline throughout
("Phase 1-3 placeholder art pipeline," "sprite replacements" in Phase 4) —
contradicted by D-244 (3D objects in-world, no per-object sprite layer) and D-149
(live 3D characters). (3) Cites D-019 as simply "top-down art style" with no note
that D-148 superseded it for gameplay (palette.md §1 had to correct exactly this
conflation in its own stub — the briefing repeats the same error). (4) Zone visual
grammar / tile palette framing predates D-235''s ObjectTag vocabulary and D-232''s
trait-template catalog entirely. (5) Character customisation section (D-126,
Q-WTF-039/040) predates the shipped D-159D-164 body-type + slot-compositing
system and the wardrobe/garment-token pipeline — describes a design question that
has since been built. (6) No mention of the Atlas at all (D-255 stepped ladder,
D-169/D-170 implant UI) despite araminta being a named co-designer of D-255''s
named-feature encoding/payload schema/lakes work.
WHAT THE REWRITE MUST CONTAIN:
- Role framing: visual art direction for the Phase-4 asset pipeline + Atlas
map-art rendering (not "generator spike prerequisites").
- palette.md (T-1052, merged) as the SINGLE LIVE STYLE AUTHORITY — point to it,
do not restate camera/color/material content (avoids a second drifting copy;
visual-grammar-v01.md / workshop-outcomes.md supersession is palette.md §5''s
to state, the briefing defers).
- conventions.md (T-1050, merged) for naming/mask/manifest mechanics — note the
category-first naming ruling (conventions.md §1) and the D-257 toon/PBR ruling
with glazing carve-out as HER standing rulings she''ll be asked to extend.
- D-235 ObjectTag vocabulary as the logical layer she renders a look on top of —
co-maintained with Miri per D-235''s own text.
- The character wardrobe pipeline (character-asset-organization.md) as the proven
reference model conventions.md imitates (mask sidecars, manifest schema).
- D-255 stepped Atlas as active/ongoing work she co-designed.
- Current open items: T-1049 (per-class asset stories, re-scoped this batch), the
D-257 glazing shader implementation (unassigned per palette.md §2.3), Q-119
(generic-parent milestone question, hers to weigh in on per D-235
co-maintenance).
- Drop: v0.2 pivot section, Q-WTF-039/040, tycoon-bookmark/apartment-wakeup
framing, and the v0.2-era decision list (D-114/D-117/D-119/D-122/D-128/D-131/
D-135) — verify each against current decision status before dropping outright;
a few (D-033 entity color, D-126 customisation dimensions) may still have live
sub-parts even though the surrounding section is obsolete.
ACCEPTANCE: rewritten briefing reflects the current Phase-4 landscape (D-235,
D-244, D-257, D-255), points to palette.md/conventions.md as live authorities
rather than duplicating content, drops v0.2-pivot and superseded-question
framing, lists araminta''s actual current/open tickets.
SEQUENCING: independent of T-1049 (different files, no shared input) — both can
run in the same batch.', 'done', 'medium', 'qatux', 'visual', NULL, '2026-06-12 10:40:59', '2026-07-25 21:11:27.661', NULL, 'df35c3bfd15e38822bc24019e04cadd1', 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 ('06FSP4MX7C66N76V31EB88YC78', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Sweep stale D-126/D-131 citations — decision-ID drift audit', 'Found during the T-1053 briefing rewrite (2026-07-25): D-126 and D-131 have been repurposed to content unrelated to what v0.2-era documents cite them for. The old araminta briefing cited ''D-126 = character customisation dimensions'' — current D-126 reads ''Groundhog Day alarm clock homage — first game day only'' (the customisation work actually shipped as D-159D-164). The old briefing cited ''D-131 = apartment/insert first-moment priority'' — current D-131 is ''broad economic verb vocabulary (life verbs, not tycoon-specific)''. This is ID-drift, not staleness: any document still citing the old meanings cites the WRONG decision entirely. Task: (1) first establish the MECHANISM — were these IDs renumbered during the DQR-tree migration, or were the era documents mis-cited at write time? If renumbering, derive the full affected-ID set from the migration mapping, not just these two. (2) Repo-wide grep for D-126/D-131 (and any other affected IDs) across docs/, wiki/, governance/ prose, and .claude/, classifying each hit as current-meaning (fine) vs era-meaning (fix or annotate). (3) Fix or annotate the stale citations; note the outcome here. The rewritten araminta.md (T-1053) is already clean. Clerk-profile work — D-record consistency audit.
Batch-7 Si pre-answer (2026-07-25): the ticket''s question (1) is RESOLVED — the pql migration did NOT renumber decision ids. tooling/pql-migrate/restructure_decisions.py''s own docstring states the migration is token-preserving (''the [D-NNN] bracket text and #d-nnn-... anchor stay byte-identical'' — only file locations and link paths changed), and seed_tickets.py preserves decision_ref verbatim. Therefore the D-126/D-131 drift is write-time mis-citation in the era documents (or later reuse/repurposing through ordinary record editing), and the sweep scope is exactly D-126/D-131 (plus any other ids the sweep itself surfaces as suspicious), not a migration-derived affected set. Start at step (2).', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-25 20:58:36.219', '2026-07-25 21:15:56.268', NULL, '7a49fda7edb41f75cb7b566ff6b26f4d', 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 ('06FB0TNSRXDCJS4P9Z5JQ1DAAM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Asset pipeline + catalog (walls, doors, props; rural & station variants)', 'Parallel asset pipeline producing the walls/doors/props/lamp-posts/etc. the tile fill places, with rural and station variants, tracked in a production-status catalog. Runs alongside generation.
Q-119 sequencing ruling (lead, batch-7 activation, 2026-07-25): the child stories T-1200..T-1204 follow the BRIEFS'' narrow reading — Q-119 gates only whether the generic-parent placeholder line-item counts as milestone-0; it never gates specific-token production. T-1049''s older ''Decide Q-119 before sequencing'' line is superseded as imprecise (the reviewed, merged briefs are the work contracts). Specific-token work on any story may start immediately; the generic-parent line items stay provisional until Q-119 resolves. Surfaced to Jeroen for cheap veto in the batch-7 report.', 'backlog', 'medium', NULL, 'visual', 'D-166', '2026-05-22 17:42:34', '2026-07-25 21:16:03.830', NULL, '08365e80c93106caa1a8e1068a7dcd7c', 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 ('06FPPM0KP850FN6WQ5PDEX9ZS4', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Road graph: 0 routable edges on water-heavy bodies — placements land in water-majority routing cells, every A* pair drops', 'Found during T-1076 (road-hub refinement) verification: several real bodies (GJ251c, GJ380c) produce 0 routable road edges because settlement placements land in water-majority routing cells, so every A* pair is dropped. PRE-EXISTING behavior (T-1038''s core had the same drop path) but now more visible with hub/spur semantics and on the Atlas road overlay. Candidate fixes per Dudley: coastal-cell routing relaxation (allow routing through coast-adjacent cells at a cost penalty) or a placement nudge (settlements snap out of water-majority cells at L3). Affects the Atlas planet-browsing experience directly: water-heavy bodies show settlements but no roads.', 'in_progress', 'medium', NULL, 'server', NULL, '2026-07-16 14:23:57.874', '2026-07-25 21:16:09.467', NULL, '359b2d1d0c0e27b724af7a73908238a3', 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 ('06FSP4MX7C66N76V31EB88YC78', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Sweep stale D-126/D-131 citations — decision-ID drift audit', 'Found during the T-1053 briefing rewrite (2026-07-25): D-126 and D-131 have been repurposed to content unrelated to what v0.2-era documents cite them for. The old araminta briefing cited ''D-126 = character customisation dimensions'' — current D-126 reads ''Groundhog Day alarm clock homage — first game day only'' (the customisation work actually shipped as D-159D-164). The old briefing cited ''D-131 = apartment/insert first-moment priority'' — current D-131 is ''broad economic verb vocabulary (life verbs, not tycoon-specific)''. This is ID-drift, not staleness: any document still citing the old meanings cites the WRONG decision entirely. Task: (1) first establish the MECHANISM — were these IDs renumbered during the DQR-tree migration, or were the era documents mis-cited at write time? If renumbering, derive the full affected-ID set from the migration mapping, not just these two. (2) Repo-wide grep for D-126/D-131 (and any other affected IDs) across docs/, wiki/, governance/ prose, and .claude/, classifying each hit as current-meaning (fine) vs era-meaning (fix or annotate). (3) Fix or annotate the stale citations; note the outcome here. The rewritten araminta.md (T-1053) is already clean. Clerk-profile work — D-record consistency audit.
Batch-7 Si pre-answer (2026-07-25): the ticket''s question (1) is RESOLVED — the pql migration did NOT renumber decision ids. tooling/pql-migrate/restructure_decisions.py''s own docstring states the migration is token-preserving (''the [D-NNN] bracket text and #d-nnn-... anchor stay byte-identical'' — only file locations and link paths changed), and seed_tickets.py preserves decision_ref verbatim. Therefore the D-126/D-131 drift is write-time mis-citation in the era documents (or later reuse/repurposing through ordinary record editing), and the sweep scope is exactly D-126/D-131 (plus any other ids the sweep itself surfaces as suspicious), not a migration-derived affected set. Start at step (2).', 'in_progress', 'medium', NULL, 'server', NULL, '2026-07-25 20:58:36.219', '2026-07-25 21:16:09.474', NULL, '1de606d715fe694573edb517b86f6172', 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 ('06FSP7HT5ES8D83VZA6ETACRAW', 'story', '06FB0TNSRXDCJS4P9Z5JQ1DAAM', 'Furniture/props asset production', 'Per-class production story (T-1049 breakdown, batch 6). Brief: docs/assets/briefs/furniture-props.md — interior furniture + small props (manifest categories furniture|props), D-257 toon treatment, conventions.md naming/mask/footprint_tiles rules. FIRST acceptance: promote + rename the 4 promotable spike GLBs (baroque_table, modernist_chair, scifi_desk + props/lion_statue) into client/assets/models/ + manifest.json per D-241 — no new generation needed; vw_beetle stays excluded (Q-067 car deferral, see briefs/deferred.md). Note: spike GLB binaries are gitignored — regenerate or restore locally at promotion time per the spike README.', 'in_progress', 'medium', NULL, 'visual', NULL, '2026-07-25 21:11:17.291', '2026-07-25 21:16:09.474', NULL, 'f5187b399a214c444d8314e6c37f70b6', 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 ('06FPPM0KP850FN6WQ5PDEX9ZS4', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Road graph: 0 routable edges on water-heavy bodies — placements land in water-majority routing cells, every A* pair drops', 'Found during T-1076 (road-hub refinement) verification: several real bodies (GJ251c, GJ380c) produce 0 routable road edges because settlement placements land in water-majority routing cells, so every A* pair is dropped. PRE-EXISTING behavior (T-1038''s core had the same drop path) but now more visible with hub/spur semantics and on the Atlas road overlay. Candidate fixes per Dudley: coastal-cell routing relaxation (allow routing through coast-adjacent cells at a cost penalty) or a placement nudge (settlements snap out of water-majority cells at L3). Affects the Atlas planet-browsing experience directly: water-heavy bodies show settlements but no roads.', 'in_progress', 'medium', 'dudley', 'server', NULL, '2026-07-16 14:23:57.874', '2026-07-25 21:16:12.993', NULL, '2576e01eff3fd0ea9c8cc5477c082be2', 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 ('06FSP7HT5ES8D83VZA6ETACRAW', 'story', '06FB0TNSRXDCJS4P9Z5JQ1DAAM', 'Furniture/props asset production', 'Per-class production story (T-1049 breakdown, batch 6). Brief: docs/assets/briefs/furniture-props.md — interior furniture + small props (manifest categories furniture|props), D-257 toon treatment, conventions.md naming/mask/footprint_tiles rules. FIRST acceptance: promote + rename the 4 promotable spike GLBs (baroque_table, modernist_chair, scifi_desk + props/lion_statue) into client/assets/models/ + manifest.json per D-241 — no new generation needed; vw_beetle stays excluded (Q-067 car deferral, see briefs/deferred.md). Note: spike GLB binaries are gitignored — regenerate or restore locally at promotion time per the spike README.', 'in_progress', 'medium', 'justine', 'visual', NULL, '2026-07-25 21:11:17.291', '2026-07-25 21:16:13.465', NULL, 'ae6be73a0e4fe79e1883fee45871402b', 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 ('06FSP4MX7C66N76V31EB88YC78', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Sweep stale D-126/D-131 citations — decision-ID drift audit', 'Found during the T-1053 briefing rewrite (2026-07-25): D-126 and D-131 have been repurposed to content unrelated to what v0.2-era documents cite them for. The old araminta briefing cited ''D-126 = character customisation dimensions'' — current D-126 reads ''Groundhog Day alarm clock homage — first game day only'' (the customisation work actually shipped as D-159D-164). The old briefing cited ''D-131 = apartment/insert first-moment priority'' — current D-131 is ''broad economic verb vocabulary (life verbs, not tycoon-specific)''. This is ID-drift, not staleness: any document still citing the old meanings cites the WRONG decision entirely. Task: (1) first establish the MECHANISM — were these IDs renumbered during the DQR-tree migration, or were the era documents mis-cited at write time? If renumbering, derive the full affected-ID set from the migration mapping, not just these two. (2) Repo-wide grep for D-126/D-131 (and any other affected IDs) across docs/, wiki/, governance/ prose, and .claude/, classifying each hit as current-meaning (fine) vs era-meaning (fix or annotate). (3) Fix or annotate the stale citations; note the outcome here. The rewritten araminta.md (T-1053) is already clean. Clerk-profile work — D-record consistency audit.
Batch-7 Si pre-answer (2026-07-25): the ticket''s question (1) is RESOLVED — the pql migration did NOT renumber decision ids. tooling/pql-migrate/restructure_decisions.py''s own docstring states the migration is token-preserving (''the [D-NNN] bracket text and #d-nnn-... anchor stay byte-identical'' — only file locations and link paths changed), and seed_tickets.py preserves decision_ref verbatim. Therefore the D-126/D-131 drift is write-time mis-citation in the era documents (or later reuse/repurposing through ordinary record editing), and the sweep scope is exactly D-126/D-131 (plus any other ids the sweep itself surfaces as suspicious), not a migration-derived affected set. Start at step (2).', 'in_progress', 'medium', 'clerk', 'server', NULL, '2026-07-25 20:58:36.219', '2026-07-25 21:16:14.050', NULL, '6af3687d11dbacd7098b1b479876423e', 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);