diff --git a/.pql/changelog/ticket_history/2026-06.sql b/.pql/changelog/ticket_history/2026-06.sql index 647ef2a64..e0837abe8 100644 --- a/.pql/changelog/ticket_history/2026-06.sql +++ b/.pql/changelog/ticket_history/2026-06.sql @@ -2313,3 +2313,150 @@ INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, chang INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPQ02KN9147H4Z12KJW7H1M', 'status', 'backlog', 'in_progress', NULL, '2026-06-16 09:42:26', '2026-06-16 09:42:26', '2026-06-16 09:42:26', NULL, 'd8f01ba3260dce078727d36de414b255', 2) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPQ02KN9147H4Z12KJW7H1M', 'status', 'in_progress', 'done', NULL, '2026-06-16 11:04:51', '2026-06-16 11:04:51', '2026-06-16 11:04:51', NULL, '44f484ff205bb503a5574ae47949f83f', 2) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPQ034DKN4ENSGX5HTYTBJG', 'status', 'in_progress', 'done', NULL, '2026-06-16 11:04:51', '2026-06-16 11:04:51', '2026-06-16 11:04:51', NULL, 'e0fc9bd58973f8b3bc3445f786ff6597', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPQ0381QWDAQABZAY7MRD8M', 'description', 'Workshopped-but-unticketed cascade work captured 2026-06-12 (cascade-refocus grounding pass). Full grounded scope follows.', 'Workshopped-but-unticketed cascade work captured 2026-06-12 (cascade-refocus grounding pass). Full grounded scope follows. + +--- +Refinement (2026-06-16, /whats-next Si pass) — READY. Current-tree brief (line refs were ~15 lines off pre-T-1077/T-1078; the PERF/TODO(T-1044) marker pins it). + +REDUNDANCY: run_layer1 (layer1.rs:44-74) computes drainage::analyze + TerrainAnalysis::analyze, but Layer1Output (layer1.rs:28-41) carries only river_network/drainage_basins/attractors. cascade.rs (~230-255, PERF/TODO(T-1044) at :235) RE-RUNS both from scratch to serve derive_all_districts (district_profile.rs:1356, takes &TerrainAnalysis) and build_road_graph (road_graph.rs:191, takes &TerrainAnalysis). ~45ms/body wasted (D-239 ref). + +FIX: carry TerrainAnalysis from the run_layer1 pass and reuse it in cascade.rs instead of recomputing. +SERIALIZE decision: TerrainAnalysis derives Debug+Clone only (features.rs:65); Layer1Output derives Serialize/Deserialize. Per D-203/T-1048 the LRU per-entry size is already a concern (TerrainAnalysis is ~2MB of full-grid Vecs) — so PREFER holding it transiently on CascadeSnapshot (drop after the cascade consumes it) rather than persisting it on the LRU-cached Layer1Output. If it must live on Layer1Output, use #[serde(skip)] + re-derive on deserialize. Do NOT bloat the cached state. + +COMPOSE WITH T-1047 (same batch): T-1047 aggregates a per-district dominant-D8-direction in the SAME run_layer1 pass — do both together to avoid double-touching layer1.rs/cascade.rs. + +Acceptance: T-1031 harness cost drops toward ~45ms/body; T-952 golden-seed byte-identity UNCHANGED. Files: layer1.rs, cascade.rs (+ CascadeSnapshot if transient).', NULL, '2026-06-16 13:21:25', '2026-06-16 13:21:25', '2026-06-16 13:21:25', NULL, 'c4951d402078f39f1914c362f8b424a0', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPTXP5N42SSGW300FEDTS3M', 'description', '(description follows in first append) + +Filed 2026-06-12 from the fable-ous.md audit (S-36; citations adversarially verified). +derive_basin_direction (chunk_context.rs:211-231) branches only on ocean_fraction_q then picks a direction from seed bits in both branches; the doc comment (:201-210) falsely claims an elev_q-gradient proxy; the module contract (:12-13, :40-42) claims D8-thalweg derivation. BodyWorldState.river_network exists (body_world_state.rs:79) and is never consulted — chunk-scale water has no guarantee of flowing downhill or connecting across the 16-chunk region-seed boundary. D-239 §8 binding law: respect the D8 thalweg. +Fix: compute the covering region''s dominant D8 direction during the RegionProfile pass (DrainageResult is in scope there) and thread it into RegionProfile for derive_chunk_context to read; fix the misleading comments either way. Composes with T-1040 (channel anchor) and T-1042 (seam blend).', '(description follows in first append) + +Filed 2026-06-12 from the fable-ous.md audit (S-36; citations adversarially verified). +derive_basin_direction (chunk_context.rs:211-231) branches only on ocean_fraction_q then picks a direction from seed bits in both branches; the doc comment (:201-210) falsely claims an elev_q-gradient proxy; the module contract (:12-13, :40-42) claims D8-thalweg derivation. BodyWorldState.river_network exists (body_world_state.rs:79) and is never consulted — chunk-scale water has no guarantee of flowing downhill or connecting across the 16-chunk region-seed boundary. D-239 §8 binding law: respect the D8 thalweg. +Fix: compute the covering region''s dominant D8 direction during the RegionProfile pass (DrainageResult is in scope there) and thread it into RegionProfile for derive_chunk_context to read; fix the misleading comments either way. Composes with T-1040 (channel anchor) and T-1042 (seam blend). + +--- +Refinement (2026-06-16, /whats-next Si pass) — staleness corrected + resolution. Original scope pre-T-1077. + +STALENESS (critical): the ticket says thread basin_direction into "RegionProfile" — WRONG post-T-1077. The per-chunk carrier derive_chunk_context receives is **DistrictProfile** (2km). The new RegionProfile (region_profile.rs) is the 205km climate tier (T-1078) and is NOT passed to derive_chunk_context. Thread basin_direction into **DistrictProfile**. + +CURRENT LOCATIONS: derive_basin_direction = chunk_context.rs:453-483 (false comment :453-462 claims an elev_q/slope_q D8-gradient proxy but the body branches only on ocean_fraction_q>=15 then picks from seed bits; module header :12-13 claims D8-thalweg). BodyWorldState.river_network = body_world_state.rs:80 (stub, never consulted). fdir (D8 flow-dir grid) is computed in drainage.rs:81 but DROPPED (not on DrainageResult :44-56). + +RESOLUTION (D-239 §8 BINDS the D8 thalweg → flow_accumulation alone is insufficient; use the true fdir): in run_layer1 (where fdir is still alive, before it''s discarded), aggregate the dominant D8 direction PER DISTRICT and carry that small per-district direction map on Layer1Output — which composes directly with T-1044''s Layer1Output caching. Thread it through derive_all_districts → derive_district_profile → DistrictProfile.basin_direction; derive_chunk_context reads district.basin_direction. Fix the false doc comment + module contract either way. Do NOT expose the full 131KB fdir grid on DrainageResult — aggregate to per-district direction in the pass. + +COMPOSE WITH T-1044 (same batch, same layer1.rs/cascade.rs flow). Acceptance: chunk-scale channels respect the covering district''s D8 thalweg (flow downhill, connect across the chunk/region-seed boundary); false comment corrected; golden-seed determinism preserved.', NULL, '2026-06-16 13:21:25', '2026-06-16 13:21:25', '2026-06-16 13:21:25', NULL, '1272b06590477a4154084d36deee4784', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPTXPE8FR7TGEM7X6QVPV4M', 'description', '(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 (`__.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)', '(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 (`__.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.', NULL, '2026-06-16 13:21:48', '2026-06-16 13:21:48', '2026-06-16 13:21:48', NULL, '870087ce715ac7ca15a3a4cab698f4f6', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPTXPJ910NNR5T2R9F9NWQR', 'description', '(description follows in first append)', '(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. _open / _closed), Godot TileSet resource organization (/.tres), multi-tile footprint metadata, and renderer/textures/ naming keyed to D-235 tokens (current renderer/README.md:80-95 covers sprites only: __.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.', NULL, '2026-06-16 13:21:48', '2026-06-16 13:21:48', '2026-06-16 13:21:48', NULL, 'f150b9375ca3d032ce8520a6be4dce5c', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPTXPJ910NNR5T2R9F9NWQR', 'status', 'backlog', 'in_progress', NULL, '2026-06-16 13:42:26', '2026-06-16 13:42:26', '2026-06-16 13:42:26', NULL, 'a5b560bb1bc0034aa83984cc8c7b9190', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPTXP5N42SSGW300FEDTS3M', 'status', 'backlog', 'in_progress', NULL, '2026-06-16 13:42:26', '2026-06-16 13:42:26', '2026-06-16 13:42:26', NULL, 'c31076efc012fb05c5f0976f00aa91b5', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPTXPE8FR7TGEM7X6QVPV4M', 'status', 'backlog', 'in_progress', NULL, '2026-06-16 13:42:26', '2026-06-16 13:42:26', '2026-06-16 13:42:26', NULL, 'e9eec22ee21e54104fb9ce92ea89c65a', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPQ0381QWDAQABZAY7MRD8M', 'status', 'backlog', 'in_progress', NULL, '2026-06-16 13:42:26', '2026-06-16 13:42:26', '2026-06-16 13:42:26', NULL, 'f92fd6c43fc29463faf961bc33407957', 2) ON CONFLICT(hash) DO NOTHING; diff --git a/.pql/changelog/tickets/2026-06.sql b/.pql/changelog/tickets/2026-06.sql index de42b998a..ae603478a 100644 --- a/.pql/changelog/tickets/2026-06.sql +++ b/.pql/changelog/tickets/2026-06.sql @@ -3113,3 +3113,195 @@ DECISION — arrangement_pattern path: **OPTION (b), re-derive. LOCKED.** COORDINATION: T-1039 and T-1043 both edit build_skeleton_work_item (plugin.rs:180-213). Do them as ONE combined branch/PR (single implementer), not two parallel edits to the same function. Acceptance (unchanged): dispatch a FjordWall-region coastal placement in a CorpTerritory province → the generated quarter uses Ribbon topology and CompanyTown-class layout, not the AlluvialPlain mesh + Commission stub path.', 'done', 'high', NULL, 'server', 'D-234', '2026-06-12 10:23:49', '2026-06-16 11:04:51', NULL, 'faf93cbd0ceefe3a8b1052659931de23', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPQ0381QWDAQABZAY7MRD8M', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Cache TerrainAnalysis on Layer1Output — drop the redundant per-body drainage re-run', 'Workshopped-but-unticketed cascade work captured 2026-06-12 (cascade-refocus grounding pass). Full grounded scope follows. + +--- +Refinement (2026-06-16, /whats-next Si pass) — READY. Current-tree brief (line refs were ~15 lines off pre-T-1077/T-1078; the PERF/TODO(T-1044) marker pins it). + +REDUNDANCY: run_layer1 (layer1.rs:44-74) computes drainage::analyze + TerrainAnalysis::analyze, but Layer1Output (layer1.rs:28-41) carries only river_network/drainage_basins/attractors. cascade.rs (~230-255, PERF/TODO(T-1044) at :235) RE-RUNS both from scratch to serve derive_all_districts (district_profile.rs:1356, takes &TerrainAnalysis) and build_road_graph (road_graph.rs:191, takes &TerrainAnalysis). ~45ms/body wasted (D-239 ref). + +FIX: carry TerrainAnalysis from the run_layer1 pass and reuse it in cascade.rs instead of recomputing. +SERIALIZE decision: TerrainAnalysis derives Debug+Clone only (features.rs:65); Layer1Output derives Serialize/Deserialize. Per D-203/T-1048 the LRU per-entry size is already a concern (TerrainAnalysis is ~2MB of full-grid Vecs) — so PREFER holding it transiently on CascadeSnapshot (drop after the cascade consumes it) rather than persisting it on the LRU-cached Layer1Output. If it must live on Layer1Output, use #[serde(skip)] + re-derive on deserialize. Do NOT bloat the cached state. + +COMPOSE WITH T-1047 (same batch): T-1047 aggregates a per-district dominant-D8-direction in the SAME run_layer1 pass — do both together to avoid double-touching layer1.rs/cascade.rs. + +Acceptance: T-1031 harness cost drops toward ~45ms/body; T-952 golden-seed byte-identity UNCHANGED. Files: layer1.rs, cascade.rs (+ CascadeSnapshot if transient).', 'backlog', 'medium', NULL, 'server', 'D-203', '2026-06-12 10:23:50', '2026-06-16 13:21:25', NULL, '42b619776712ed47b206bb7ed3694ead', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPTXP5N42SSGW300FEDTS3M', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Derive ChunkContext basin_direction from the L1 D8 thalweg; fix the false doc comment', '(description follows in first append) + +Filed 2026-06-12 from the fable-ous.md audit (S-36; citations adversarially verified). +derive_basin_direction (chunk_context.rs:211-231) branches only on ocean_fraction_q then picks a direction from seed bits in both branches; the doc comment (:201-210) falsely claims an elev_q-gradient proxy; the module contract (:12-13, :40-42) claims D8-thalweg derivation. BodyWorldState.river_network exists (body_world_state.rs:79) and is never consulted — chunk-scale water has no guarantee of flowing downhill or connecting across the 16-chunk region-seed boundary. D-239 §8 binding law: respect the D8 thalweg. +Fix: compute the covering region''s dominant D8 direction during the RegionProfile pass (DrainageResult is in scope there) and thread it into RegionProfile for derive_chunk_context to read; fix the misleading comments either way. Composes with T-1040 (channel anchor) and T-1042 (seam blend). + +--- +Refinement (2026-06-16, /whats-next Si pass) — staleness corrected + resolution. Original scope pre-T-1077. + +STALENESS (critical): the ticket says thread basin_direction into "RegionProfile" — WRONG post-T-1077. The per-chunk carrier derive_chunk_context receives is **DistrictProfile** (2km). The new RegionProfile (region_profile.rs) is the 205km climate tier (T-1078) and is NOT passed to derive_chunk_context. Thread basin_direction into **DistrictProfile**. + +CURRENT LOCATIONS: derive_basin_direction = chunk_context.rs:453-483 (false comment :453-462 claims an elev_q/slope_q D8-gradient proxy but the body branches only on ocean_fraction_q>=15 then picks from seed bits; module header :12-13 claims D8-thalweg). BodyWorldState.river_network = body_world_state.rs:80 (stub, never consulted). fdir (D8 flow-dir grid) is computed in drainage.rs:81 but DROPPED (not on DrainageResult :44-56). + +RESOLUTION (D-239 §8 BINDS the D8 thalweg → flow_accumulation alone is insufficient; use the true fdir): in run_layer1 (where fdir is still alive, before it''s discarded), aggregate the dominant D8 direction PER DISTRICT and carry that small per-district direction map on Layer1Output — which composes directly with T-1044''s Layer1Output caching. Thread it through derive_all_districts → derive_district_profile → DistrictProfile.basin_direction; derive_chunk_context reads district.basin_direction. Fix the false doc comment + module contract either way. Do NOT expose the full 131KB fdir grid on DrainageResult — aggregate to per-district direction in the pass. + +COMPOSE WITH T-1044 (same batch, same layer1.rs/cascade.rs flow). Acceptance: chunk-scale channels respect the covering district''s D8 thalweg (flow downhill, connect across the chunk/region-seed boundary); false comment corrected; golden-seed determinism preserved.', 'backlog', 'medium', NULL, 'server', 'D-239', '2026-06-12 10:40:58', '2026-06-16 13:21:25', NULL, '67324ec668b83efda3cce9e0975ad2c7', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_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 (`__.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.', 'backlog', 'high', NULL, 'visual', NULL, '2026-06-12 10:40:58', '2026-06-16 13:21:48', NULL, '24db28c07edc3ea2d5daa36db28ff74f', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_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. _open / _closed), Godot TileSet resource organization (/.tres), multi-tile footprint metadata, and renderer/textures/ naming keyed to D-235 tokens (current renderer/README.md:80-95 covers sprites only: __.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.', 'backlog', 'high', NULL, 'visual', NULL, '2026-06-12 10:40:59', '2026-06-16 13:21:48', NULL, '546f75f2fa3361ebd0e88026ae90b3e9', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_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 (`__.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.', 'in_progress', 'high', NULL, 'visual', NULL, '2026-06-12 10:40:58', '2026-06-16 13:42:26', NULL, '4ab36440ca481dfd0b8ec757119219ad', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_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. _open / _closed), Godot TileSet resource organization (/.tres), multi-tile footprint metadata, and renderer/textures/ naming keyed to D-235 tokens (current renderer/README.md:80-95 covers sprites only: __.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.', 'in_progress', 'high', NULL, 'visual', NULL, '2026-06-12 10:40:59', '2026-06-16 13:42:26', NULL, '54f99e24429bfd5b5ef93327759619d5', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPTXP5N42SSGW300FEDTS3M', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Derive ChunkContext basin_direction from the L1 D8 thalweg; fix the false doc comment', '(description follows in first append) + +Filed 2026-06-12 from the fable-ous.md audit (S-36; citations adversarially verified). +derive_basin_direction (chunk_context.rs:211-231) branches only on ocean_fraction_q then picks a direction from seed bits in both branches; the doc comment (:201-210) falsely claims an elev_q-gradient proxy; the module contract (:12-13, :40-42) claims D8-thalweg derivation. BodyWorldState.river_network exists (body_world_state.rs:79) and is never consulted — chunk-scale water has no guarantee of flowing downhill or connecting across the 16-chunk region-seed boundary. D-239 §8 binding law: respect the D8 thalweg. +Fix: compute the covering region''s dominant D8 direction during the RegionProfile pass (DrainageResult is in scope there) and thread it into RegionProfile for derive_chunk_context to read; fix the misleading comments either way. Composes with T-1040 (channel anchor) and T-1042 (seam blend). + +--- +Refinement (2026-06-16, /whats-next Si pass) — staleness corrected + resolution. Original scope pre-T-1077. + +STALENESS (critical): the ticket says thread basin_direction into "RegionProfile" — WRONG post-T-1077. The per-chunk carrier derive_chunk_context receives is **DistrictProfile** (2km). The new RegionProfile (region_profile.rs) is the 205km climate tier (T-1078) and is NOT passed to derive_chunk_context. Thread basin_direction into **DistrictProfile**. + +CURRENT LOCATIONS: derive_basin_direction = chunk_context.rs:453-483 (false comment :453-462 claims an elev_q/slope_q D8-gradient proxy but the body branches only on ocean_fraction_q>=15 then picks from seed bits; module header :12-13 claims D8-thalweg). BodyWorldState.river_network = body_world_state.rs:80 (stub, never consulted). fdir (D8 flow-dir grid) is computed in drainage.rs:81 but DROPPED (not on DrainageResult :44-56). + +RESOLUTION (D-239 §8 BINDS the D8 thalweg → flow_accumulation alone is insufficient; use the true fdir): in run_layer1 (where fdir is still alive, before it''s discarded), aggregate the dominant D8 direction PER DISTRICT and carry that small per-district direction map on Layer1Output — which composes directly with T-1044''s Layer1Output caching. Thread it through derive_all_districts → derive_district_profile → DistrictProfile.basin_direction; derive_chunk_context reads district.basin_direction. Fix the false doc comment + module contract either way. Do NOT expose the full 131KB fdir grid on DrainageResult — aggregate to per-district direction in the pass. + +COMPOSE WITH T-1044 (same batch, same layer1.rs/cascade.rs flow). Acceptance: chunk-scale channels respect the covering district''s D8 thalweg (flow downhill, connect across the chunk/region-seed boundary); false comment corrected; golden-seed determinism preserved.', 'in_progress', 'medium', NULL, 'server', 'D-239', '2026-06-12 10:40:58', '2026-06-16 13:42:26', NULL, 'a1c329e777e2f9b0465ecdb645693a5a', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPQ0381QWDAQABZAY7MRD8M', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Cache TerrainAnalysis on Layer1Output — drop the redundant per-body drainage re-run', 'Workshopped-but-unticketed cascade work captured 2026-06-12 (cascade-refocus grounding pass). Full grounded scope follows. + +--- +Refinement (2026-06-16, /whats-next Si pass) — READY. Current-tree brief (line refs were ~15 lines off pre-T-1077/T-1078; the PERF/TODO(T-1044) marker pins it). + +REDUNDANCY: run_layer1 (layer1.rs:44-74) computes drainage::analyze + TerrainAnalysis::analyze, but Layer1Output (layer1.rs:28-41) carries only river_network/drainage_basins/attractors. cascade.rs (~230-255, PERF/TODO(T-1044) at :235) RE-RUNS both from scratch to serve derive_all_districts (district_profile.rs:1356, takes &TerrainAnalysis) and build_road_graph (road_graph.rs:191, takes &TerrainAnalysis). ~45ms/body wasted (D-239 ref). + +FIX: carry TerrainAnalysis from the run_layer1 pass and reuse it in cascade.rs instead of recomputing. +SERIALIZE decision: TerrainAnalysis derives Debug+Clone only (features.rs:65); Layer1Output derives Serialize/Deserialize. Per D-203/T-1048 the LRU per-entry size is already a concern (TerrainAnalysis is ~2MB of full-grid Vecs) — so PREFER holding it transiently on CascadeSnapshot (drop after the cascade consumes it) rather than persisting it on the LRU-cached Layer1Output. If it must live on Layer1Output, use #[serde(skip)] + re-derive on deserialize. Do NOT bloat the cached state. + +COMPOSE WITH T-1047 (same batch): T-1047 aggregates a per-district dominant-D8-direction in the SAME run_layer1 pass — do both together to avoid double-touching layer1.rs/cascade.rs. + +Acceptance: T-1031 harness cost drops toward ~45ms/body; T-952 golden-seed byte-identity UNCHANGED. Files: layer1.rs, cascade.rs (+ CascadeSnapshot if transient).', 'in_progress', 'medium', NULL, 'server', 'D-203', '2026-06-12 10:23:50', '2026-06-16 13:42:26', NULL, 'acb7bf02efc3a7d7f4418f7d6c151204', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);