diff --git a/.claude/skills/glb-gen/SKILL.md b/.claude/skills/glb-gen/SKILL.md index 5d6c46930..27a1c4e75 100644 --- a/.claude/skills/glb-gen/SKILL.md +++ b/.claude/skills/glb-gen/SKILL.md @@ -104,6 +104,18 @@ Promotion = repo-canonical (D-241): an asset entering the manifest AND in `spikes/`. Naming, mask-sidecar, and texture conventions are in `docs/assets/visual/conventions.md`. +**Promotion commits three files per tintable model, two for non-tintable.** +Copying the `.glb` (+ `_mask.png`, if the asset is tintable) into +`client/assets/models/{furniture,props}/` is not the last step — run a +headless import (`godot4 --headless --path client --import`) after copying, +which generates a third required file, `_Image_0.png`, next to the +`.glb`. Godot's scene importer externalizes the GLB's embedded texture to +this loose file and the compiled scene references it by UID even with +`gltf/embedded_image_handling=3` set (confirmed Godot 4.6, T-1204). It must be +committed with the rest — see `conventions.md` §2 for the full failure mode +(deleting it breaks `ResourceLoader` loading with a binary UID dependency +error that no text search of the compiled `.scn` will surface). + ## Post-process in Blender Normalize scale, center, generate recolor mask, adjust materials: diff --git a/.pql/changelog/ticket_history/2026-07.sql b/.pql/changelog/ticket_history/2026-07.sql index 43a6bb232..b0f451db2 100644 --- a/.pql/changelog/ticket_history/2026-07.sql +++ b/.pql/changelog/ticket_history/2026-07.sql @@ -3123,3 +3123,13 @@ 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 ('06FPPM0KP850FN6WQ5PDEX9ZS4', 'assigned_to', NULL, 'dudley', NULL, '2026-07-25 21:16:12', '2026-07-25 21:16:12.993', '2026-07-25 21:16:12.993', NULL, '5a1279c95e7f72db48cd78cbc492cc35', 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 ('06FSP7HT5ES8D83VZA6ETACRAW', 'assigned_to', NULL, 'justine', NULL, '2026-07-25 21:16:13', '2026-07-25 21:16:13.465', '2026-07-25 21:16:13.465', NULL, 'c00c4231a9e967524b556aadf3255cb8', 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 ('06FSP4MX7C66N76V31EB88YC78', 'assigned_to', NULL, 'clerk', NULL, '2026-07-25 21:16:14', '2026-07-25 21:16:14.050', '2026-07-25 21:16:14.050', NULL, 'a1bb4b5b3f1fba52fc7fa1a6be8e5e02', 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 ('06FSP4MX7C66N76V31EB88YC78', 'description', '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-159–D-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).', '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-159–D-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). + +SWEEP COMPLETE (2026-07-25/26). Root cause pinned to ONE commit: f4c72e148 (2026-03-13, ''update all 18 briefings for v0.2 pivot'') — adjacent-id transcription slips from the Where''s the Fun? workshop batch (eea3f3cf2), copied forward by every later briefing-maintenance pass, never a renumbering. Eleven mis-citations fixed on main across five briefings: gore.md (D-131->D-130 emergent-moral-arc — lead-verified against D-130''s near-verbatim body; D-131->D-136 first-moment), mellanie.md (D-131->D-136, D-134->D-126 x2), ozzie.md (D-126->D-134, D-131->D-136, D-134->D-126), stig.md (D-126->D-134 x2, D-131->D-136), nigel.md (D-136->D-137 x3, plus a knock-on the sweep''s grep could not see: D-137->D-120 x2 ''no skill ceiling'' — found when the D-136->D-137 fix would have created contradictory D-137 citations in one file; D-120 verified as the real record, scope.md, itself SUPERSEDED). The slip family: D-126<->D-134 swapped; D-136 as D-131; D-137 as D-136; D-130 as D-131; D-120 as D-137. Sprint/discussion archives verified already-correct (all postdate the workshop and cite current meanings) — no annotations needed. Governance records themselves correct, untouched. Remaining 13 briefings from the same commit: T-1205.', NULL, '2026-07-26 10:50:32', '2026-07-26 10:50:32.969', '2026-07-26 10:50:32.969', NULL, '735a5bcd8dbce0897f4a77a76ff3ff3b', 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 ('06FSP4MX7C66N76V31EB88YC78', 'status', 'in_progress', 'done', NULL, '2026-07-26 10:50:38', '2026-07-26 10:50:38.896', '2026-07-26 10:50:38.896', NULL, 'a73745109833b6478ce0d7ff2ed673b5', 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 ('06FSP7HT5ES8D83VZA6ETACRAW', 'status', 'in_progress', 'review', NULL, '2026-07-26 10:55:32', '2026-07-26 10:55:32.378', '2026-07-26 10:55:32.378', NULL, '77078d2928d2421bb00ca4e4dcc49faa', 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 ('06FSP7HT5ES8D83VZA6ETACRAW', 'status', 'review', 'done', NULL, '2026-07-26 11:04:45', '2026-07-26 11:04:45.800', '2026-07-26 11:04:45.800', NULL, '2b689e1630b210f11ed9686a9e71a913', 2) ON CONFLICT(hash) DO NOTHING; diff --git a/.pql/changelog/tickets/2026-07.sql b/.pql/changelog/tickets/2026-07.sql index 2750fcc36..6002607eb 100644 --- a/.pql/changelog/tickets/2026-07.sql +++ b/.pql/changelog/tickets/2026-07.sql @@ -5297,3 +5297,16 @@ INSERT INTO tickets (record_id, type, parent_record_id, title, description, stat INSERT INTO tickets (record_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-159–D-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); +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FSW30GPFFEB05M6EXD1WFANW', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Audit remaining 13 briefings for f4c72e148 citation slips', 'Follow-up to T-1199 (done): the 2026-03-13 commit f4c72e148 (''update all 18 briefings for v0.2 pivot'') is a CONFIRMED source of decision-id transcription errors — five briefings (gore, mellanie, ozzie, stig, nigel) carried eleven mis-citations from a consistent adjacent-id slip family: D-126<->D-134 swapped, D-136 written as D-131, D-137 written as D-136, D-130 written as D-131, D-120 written as D-137. All eleven fixed on main (T-1199). This ticket: audit the OTHER 13 briefings touched by that commit (git show f4c72e148 --stat lists them; araminta.md already rewritten clean by T-1053) for the same slip pattern — check every D-1xx citation in each against the record''s actual title/content, not just the ids from the known family (the family grew twice during the T-1199 fix pass: D-130 and D-120 were found only by verifying claims against record bodies). Method that worked: grep the briefing''s D-ids, read each record''s header, flag any citation whose prose describes different content. Note that several of these briefings are wholesale-stale (pre-cascade framing, like araminta.md was before T-1053) — this ticket fixes CITATIONS only; wholesale rewrites are separate per-briefing work (T-1053 is the model).', 'backlog', 'medium', NULL, 'server', NULL, '2026-07-26 10:50:18.675', '2026-07-26 10:50:18.675', NULL, '7b9349dfa0a61bcf3715328c79de4fa5', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_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-159–D-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). + +SWEEP COMPLETE (2026-07-25/26). Root cause pinned to ONE commit: f4c72e148 (2026-03-13, ''update all 18 briefings for v0.2 pivot'') — adjacent-id transcription slips from the Where''s the Fun? workshop batch (eea3f3cf2), copied forward by every later briefing-maintenance pass, never a renumbering. Eleven mis-citations fixed on main across five briefings: gore.md (D-131->D-130 emergent-moral-arc — lead-verified against D-130''s near-verbatim body; D-131->D-136 first-moment), mellanie.md (D-131->D-136, D-134->D-126 x2), ozzie.md (D-126->D-134, D-131->D-136, D-134->D-126), stig.md (D-126->D-134 x2, D-131->D-136), nigel.md (D-136->D-137 x3, plus a knock-on the sweep''s grep could not see: D-137->D-120 x2 ''no skill ceiling'' — found when the D-136->D-137 fix would have created contradictory D-137 citations in one file; D-120 verified as the real record, scope.md, itself SUPERSEDED). The slip family: D-126<->D-134 swapped; D-136 as D-131; D-137 as D-136; D-130 as D-131; D-120 as D-137. Sprint/discussion archives verified already-correct (all postdate the workshop and cite current meanings) — no annotations needed. Governance records themselves correct, untouched. Remaining 13 briefings from the same commit: T-1205.', 'in_progress', 'medium', 'clerk', 'server', NULL, '2026-07-25 20:58:36.219', '2026-07-26 10:50:32.968', NULL, '6426a6b8ea7e8b620ba39ceb0965c5fc', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_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-159–D-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). + +SWEEP COMPLETE (2026-07-25/26). Root cause pinned to ONE commit: f4c72e148 (2026-03-13, ''update all 18 briefings for v0.2 pivot'') — adjacent-id transcription slips from the Where''s the Fun? workshop batch (eea3f3cf2), copied forward by every later briefing-maintenance pass, never a renumbering. Eleven mis-citations fixed on main across five briefings: gore.md (D-131->D-130 emergent-moral-arc — lead-verified against D-130''s near-verbatim body; D-131->D-136 first-moment), mellanie.md (D-131->D-136, D-134->D-126 x2), ozzie.md (D-126->D-134, D-131->D-136, D-134->D-126), stig.md (D-126->D-134 x2, D-131->D-136), nigel.md (D-136->D-137 x3, plus a knock-on the sweep''s grep could not see: D-137->D-120 x2 ''no skill ceiling'' — found when the D-136->D-137 fix would have created contradictory D-137 citations in one file; D-120 verified as the real record, scope.md, itself SUPERSEDED). The slip family: D-126<->D-134 swapped; D-136 as D-131; D-137 as D-136; D-130 as D-131; D-120 as D-137. Sprint/discussion archives verified already-correct (all postdate the workshop and cite current meanings) — no annotations needed. Governance records themselves correct, untouched. Remaining 13 briefings from the same commit: T-1205.', 'done', 'medium', 'clerk', 'server', NULL, '2026-07-25 20:58:36.219', '2026-07-26 10:50:38.896', NULL, 'eb63bfa9711770fedfcecd1773145fad', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_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.', 'review', 'medium', 'justine', 'visual', NULL, '2026-07-25 21:11:17.291', '2026-07-26 10:55:32.377', NULL, '474e9e9adcb6e92a49c2e579afc16c0f', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_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.', 'done', 'medium', 'justine', 'visual', NULL, '2026-07-25 21:11:17.291', '2026-07-26 11:04:45.800', NULL, '1c43a93489231cdc6de78c332629c085', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); diff --git a/CHANGELOG.md b/CHANGELOG.md index b331a1a77..e4204a5d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Format based on [Keep a Changelog](https://keepachangelog.com/). ## [Unreleased] ### Added +- **The first real environment assets are in the game** (T-1204) — a baroque table, modernist chair, sci-fi desk, and lion statue graduated from the 3D-pipeline spike into the production asset library, registered in the manifest with verified in-engine loading. Two rules were written down in the process: Godot's importer creates a mandatory loose-texture sidecar next to every model (undocumented until it broke loading), and an asset's tile footprint is an authored value — never inferred from normalized geometry, which erases real-world scale - **Every asset class now has a production contract** (T-1049, T-1051, T-1053) — one-page briefs for the first five environment asset classes (station walls, rural walls, doors, floors, furniture/props), each pinning its materials, look, naming, generation path, and concrete acceptance against the merged style bible and conventions; a formal deferral register for the set-dressing classes (lamp posts, barns, TVs, billboards, cars) with recorded revisit triggers; and the art director's briefing rewritten from its year-old shape to the current landscape. Five production stories are now queued — the first real environment assets of the game have a defined path from concept to the world - **The visual asset pipeline has a production home** (D-244, T-1050) — the 3D-model/texture/artwork/icon/effect catalogs are instantiated with live status tables (the icon set's 13 as-built SVGs inventoried with their real wiring), `client/assets/models/` exists as the promotion target for glb-gen output with a schema-documented manifest, and a single conventions document fixes model naming (category-first), mask sidecars, and D-235-token texture naming — with door-state and TileSet conventions recorded as explicit Phase-5 deferrals rather than silent gaps - **The Phase-4 visual style bible** (D-257, T-1052) — palette.md is no longer a stub: gameplay vs offline-render cameras untangled, a rendered-look register for every ratified building material/roof/facade/street tag (hue range, grain, weathering), and the newly-recorded D-257 rendering treatment — environment props share the character toon look, with a minimal-PBR carve-out for glazing so glass reads truthfully about sightlines. Exactly one live style authority remains; the superseded v0.1 documents now say so themselves diff --git a/client/assets/models/furniture/chair_modernist.glb b/client/assets/models/furniture/chair_modernist.glb new file mode 100644 index 000000000..13a93f5b8 Binary files /dev/null and b/client/assets/models/furniture/chair_modernist.glb differ diff --git a/client/assets/models/furniture/chair_modernist_Image_0.png b/client/assets/models/furniture/chair_modernist_Image_0.png new file mode 100644 index 000000000..65a631c62 Binary files /dev/null and b/client/assets/models/furniture/chair_modernist_Image_0.png differ diff --git a/client/assets/models/furniture/chair_modernist_mask.png b/client/assets/models/furniture/chair_modernist_mask.png new file mode 100644 index 000000000..e83c64d66 Binary files /dev/null and b/client/assets/models/furniture/chair_modernist_mask.png differ diff --git a/client/assets/models/furniture/desk_scifi.glb b/client/assets/models/furniture/desk_scifi.glb new file mode 100644 index 000000000..b43f45724 Binary files /dev/null and b/client/assets/models/furniture/desk_scifi.glb differ diff --git a/client/assets/models/furniture/desk_scifi_Image_0.png b/client/assets/models/furniture/desk_scifi_Image_0.png new file mode 100644 index 000000000..67fcde0d2 Binary files /dev/null and b/client/assets/models/furniture/desk_scifi_Image_0.png differ diff --git a/client/assets/models/furniture/desk_scifi_mask.png b/client/assets/models/furniture/desk_scifi_mask.png new file mode 100644 index 000000000..02cc956f5 Binary files /dev/null and b/client/assets/models/furniture/desk_scifi_mask.png differ diff --git a/client/assets/models/furniture/table_baroque.glb b/client/assets/models/furniture/table_baroque.glb new file mode 100644 index 000000000..eb93d966e Binary files /dev/null and b/client/assets/models/furniture/table_baroque.glb differ diff --git a/client/assets/models/furniture/table_baroque_Image_0.png b/client/assets/models/furniture/table_baroque_Image_0.png new file mode 100644 index 000000000..58d589692 Binary files /dev/null and b/client/assets/models/furniture/table_baroque_Image_0.png differ diff --git a/client/assets/models/furniture/table_baroque_mask.png b/client/assets/models/furniture/table_baroque_mask.png new file mode 100644 index 000000000..953a16546 Binary files /dev/null and b/client/assets/models/furniture/table_baroque_mask.png differ diff --git a/client/assets/models/manifest.json b/client/assets/models/manifest.json index c88b88f5a..bb98c3f0e 100644 --- a/client/assets/models/manifest.json +++ b/client/assets/models/manifest.json @@ -19,6 +19,39 @@ "mask": "furniture/table_baroque_mask.png" } }, - "furniture": {}, - "props": {} + "furniture": { + "table_baroque": { + "category": "furniture", + "source_model": "furniture/table_baroque.glb", + "subdirectory": "furniture", + "status": "planned", + "footprint_tiles": [1, 1], + "mask": "furniture/table_baroque_mask.png" + }, + "chair_modernist": { + "category": "furniture", + "source_model": "furniture/chair_modernist.glb", + "subdirectory": "furniture", + "status": "planned", + "footprint_tiles": [1, 1], + "mask": "furniture/chair_modernist_mask.png" + }, + "desk_scifi": { + "category": "furniture", + "source_model": "furniture/desk_scifi.glb", + "subdirectory": "furniture", + "status": "planned", + "footprint_tiles": [1, 1], + "mask": "furniture/desk_scifi_mask.png" + } + }, + "props": { + "lion_statue": { + "category": "props", + "source_model": "props/lion_statue.glb", + "subdirectory": "props", + "status": "planned", + "footprint_tiles": [1, 1] + } + } } diff --git a/client/assets/models/props/lion_statue.glb b/client/assets/models/props/lion_statue.glb new file mode 100644 index 000000000..ce4530802 Binary files /dev/null and b/client/assets/models/props/lion_statue.glb differ diff --git a/client/assets/models/props/lion_statue_Image_0.png b/client/assets/models/props/lion_statue_Image_0.png new file mode 100644 index 000000000..a0c52f9e3 Binary files /dev/null and b/client/assets/models/props/lion_statue_Image_0.png differ diff --git a/docs/assets/briefs/furniture-props.md b/docs/assets/briefs/furniture-props.md index 34f81d4cd..2d13a2788 100644 --- a/docs/assets/briefs/furniture-props.md +++ b/docs/assets/briefs/furniture-props.md @@ -76,6 +76,17 @@ manifest for any furniture with a real multi-tile footprint (a table spanning `[2, 1]`); most `props`-category items, including `lion_statue`, omit the field or set `[1, 1]`. +**Footprint for normalized assets** (araminta ruling, PR #214). +Blender-normalized GLBs (`target-width=1.0` postprocess, the spike default) +lose real-world scale — footprint cannot be derived from geometry after +normalization. `footprint_tiles` is therefore an **authored** value, not a +measured one: default `[1, 1]` only holds when the object's real-world class +is plausibly single-tile (chairs, stools, small props, compact desks). Any +item whose class is ordinarily multi-tile (dining tables seating 4+, couches, +beds, wardrobes) needs the promoting author to state an intended real-world +footprint explicitly (in the promotion commit/PR, or a per-item scale note in +the brief) — never left to infer from the normalized mesh. + ## 4. Generation Path Two paths, depending on whether the spike asset is being promoted or a new diff --git a/docs/assets/visual/README.md b/docs/assets/visual/README.md index 9fd1d3542..000e86972 100644 --- a/docs/assets/visual/README.md +++ b/docs/assets/visual/README.md @@ -7,8 +7,8 @@ status: active # Visual Asset Pipeline -Status: **Active** — catalog structure instantiated (T-1050); awaiting first -model/texture/artwork/effects production sprint. +Status: **Active** — catalog structure instantiated (T-1050); first models +promoted (T-1204); textures/artwork/effects production still pending. Per **[D-244](../../../governance/decisions/architecture.md#d-244)**: the in-world view renders **3D objects directly**. The only flattened 2D content is **textures** (PBR maps on 3D surfaces) and **flat 2D artwork** (paintings, flags, billboards, signage) applied as textures/decals on flat surfaces *within* the 3D world. **There is no per-object sprite layer** — game objects are not pre-rendered to sprites. @@ -16,7 +16,7 @@ Per **[D-244](../../../governance/decisions/architecture.md#d-244)**: the in-wor | Category | File | Count | Description | |----------|------|-------|-------------| -| Models | [models.md](models.md) | 0 | 3D objects/props (`.glb`) via Trellis (`/glb-gen`), promoted per [D-241] | +| Models | [models.md](models.md) | 4 | 3D objects/props (`.glb`) via Trellis (`/glb-gen`), promoted per [D-241] | | Characters | — | — | 3D meshes composited at runtime via `CharacterVisualDescriptor` — see [character-asset-organization.md](../../architecture/character-asset-organization.md) | | Textures | [textures.md](textures.md) | 0 | PBR surface maps (BaseColor / Normal / Roughness) for 3D models | | Artwork | [artwork.md](artwork.md) | 0 | Flat 2D art — paintings, flags, billboards, signage, screen content — applied as textures/decals (via the `/sprite-gen` 2D-artwork generator) | diff --git a/docs/assets/visual/conventions.md b/docs/assets/visual/conventions.md index 0e30b17ed..2d2233b22 100644 --- a/docs/assets/visual/conventions.md +++ b/docs/assets/visual/conventions.md @@ -65,6 +65,27 @@ correction). This is the same mechanism, not a parallel one: environment props reuse the clothing pipeline's multi-region shader family rather than inventing a props-specific one. +**Extracted-texture sidecar (`_Image_0.png`) — generated, must be committed.** +On first Godot import of a promoted `.glb`, the scene importer externalizes +the model's embedded texture to a loose file next to it, +`_Image_0.png` (e.g. `table_baroque_Image_0.png`), and the compiled +`.godot/imported/*.scn` references that file by **UID**, as an external +dependency — this happens even with `gltf/embedded_image_handling=3` (embed +uncompressed) set in `project.godot`, confirmed on Godot 4.6 (T-1204 +promotion). This file is **machine-generated, never hand-edited** — if it is +lost or deleted, a clean re-import (delete the stale `.glb.import` + +`.godot/imported/*.scn` cache entry, then re-run `godot4 --headless --import`) +regenerates it deterministically. But it **must be committed alongside the +`.glb`** it belongs to: deleting it after import breaks loading with +`ERROR: Can't load dependency` / `Resource file not found` at `ResourceLoader` +time, and critically, **a text grep of the compiled `.scn` will not reveal +this dependency** — the reference is a binary-encoded UID (`uid://...`), not +a literal path string, so searching the `.scn` for the filename finds +nothing. Losing this file costs a full debug cycle (re-copy the source `.glb`, +clear the stale `.import`/`.scn` cache, re-import, re-verify) to rediscover +what the UID reference actually pointed to — treat it as a required sidecar +from the moment of promotion, not disposable import cache. + --- ## 3. Texture naming (D-235 tokens) @@ -110,8 +131,12 @@ sprite-era assumption); it is a `manifest.json` field: "footprint_tiles": [2, 1] ``` -`[width, height]` in whole tiles (1 tile = 1m, D-243's voxel unit). Single -Sunday-tile props omit the field or set it to `[1, 1]`. See +`[width, height]` in whole tiles (1 tile = 1m, D-243's voxel unit). Single-tile +props omit the field or set it to `[1, 1]`. For Blender-normalized assets, +this value is **authored at promotion time, not derived from mesh geometry** — +normalization erases real-world scale (araminta ruling, PR #214; the full rule +with the single-tile-class default lives in +`docs/assets/briefs/furniture-props.md` §3). See `client/assets/models/manifest.json`'s `_fields` block for the authoritative field definition alongside the rest of the per-asset schema. diff --git a/docs/assets/visual/models.md b/docs/assets/visual/models.md index 03ef17a5e..bf8a2fda3 100644 --- a/docs/assets/visual/models.md +++ b/docs/assets/visual/models.md @@ -33,12 +33,25 @@ Naming, mask-sidecar, and multi-tile footprint conventions: | ID | Filename | Status | Category | Footprint | Notes | Sprint/Ticket | |----|----------|--------|----------|-----------|-------|---------------| +| table_baroque | `furniture/table_baroque.glb` | planned | furniture | [1, 1] | Promoted from spike (`baroque_table.glb`, renamed category-first); mask sidecar present; spike README flags the mask as "too aggressive — tints the whole model bright white", not yet re-tuned | T-1204 | +| chair_modernist | `furniture/chair_modernist.glb` | planned | furniture | [1, 1] | Promoted from spike (`modernist_chair.glb`, renamed category-first); mask sidecar present | T-1204 | +| desk_scifi | `furniture/desk_scifi.glb` | planned | furniture | [1, 1] | Promoted from spike (`scifi_desk.glb`, renamed category-first); mask sidecar present | T-1204 | +| lion_statue | `props/lion_statue.glb` | planned | props | [1, 1] | Promoted from spike as-is (no rename); "fixed" mode, non-tintable — no mask sidecar per brief §3 | T-1204 | -No models have entered production yet — this table is intentionally empty at -initial creation (T-1050). Rows are added as assets are promoted into -`client/assets/models/manifest.json`; keep this table's `Status` column in -sync with the manifest's `status` field (shared vocab: planned / -in-progress / placeholder / final, `docs/assets/README.md`). +These four rows are the first promoted assets (T-1204, `furniture-props.md` +brief's first acceptance) — the 3 in-scope spike furniture GLBs plus +`lion_statue`, already Blender-postprocessed (`mat_primary`, +roughness=1.0/specular=0.0 confirmed via Blender headless inspection) in the +spike, so no re-run of `postprocess_glb.py` was needed at promotion time. +`vw_beetle.glb` stays excluded (Q-067 car deferral). Footprint is `[1, 1]` +for all four — the spike GLBs are Blender-normalized to `target-width=1.0` +(max dimension ≤ 1m on every axis after normalization), so none of the actual +promoted meshes has real-world geometry spanning more than one tile; the +brief's `[2, 1]` table example is illustrative of the convention, not a +derived value for these specific assets. Further rows are added as new +assets are promoted/generated; keep this table's `Status` column in sync with +the manifest's `status` field (shared vocab: planned / in-progress / +placeholder / final, `docs/assets/README.md`). ## Cross-References diff --git a/docs/briefings/gore.md b/docs/briefings/gore.md index f1d2d62b7..955fcebc5 100644 --- a/docs/briefings/gore.md +++ b/docs/briefings/gore.md @@ -20,7 +20,7 @@ The workshop confirmed and sharpened the thematic frame. "Is this enough?" survi **What changes:** - The thematic question is no longer framed through smuggler-vs-detective as "two answers." The tycoon IS the question: a small business owner in a mid-Reach system. Has this person settled? Is settling valid? Does the player make it more or settle deeper? - Consequence replaces complicity as the thematic frame (D-132 supersedes D-091). The player's arc is not "did you help or hinder the conspiracy?" but "what did your choices produce and do you own them?" -- Fully emergent moral arc for v0.2 (D-131). No authored arc structure. The generator must prove relationships are readable before narrative depth is layered on. +- Fully emergent moral arc for v0.2 (D-130). No authored arc structure. The generator must prove relationships are readable before narrative depth is layered on. **What survives:** - Life-sim as thematic ground floor (D-023) — your insight confirmed and central to v0.2 @@ -41,7 +41,7 @@ The workshop confirmed and sharpened the thematic frame. "Is this enough?" survi - D-117: Tycoon bookmark, zero investigation content - D-122: All NPCs generated, no named hand-authored characters - D-127: Player choices are the content — Rimworld model -- D-131: First Settled Reach moment = apartment + insert activation +- D-136: First Settled Reach moment = apartment + insert activation - D-132: Consequence replaces complicity as thematic frame; fully emergent moral arc ## Open Questions Assigned to You diff --git a/docs/briefings/mellanie.md b/docs/briefings/mellanie.md index b1cba5124..65b2d4081 100644 --- a/docs/briefings/mellanie.md +++ b/docs/briefings/mellanie.md @@ -49,8 +49,8 @@ The copy pipeline fundamentally shifts from hand-authoring specific NPC profiles - D-122: All NPCs generated — NPC content via AI templating, not hand-authoring - D-123: Generative AI for NPC content templating via culture vectors - D-128: Culture implicit in starting location — Van Maanen's Star = Van Maanen's Star culture -- D-131: First Settled Reach moment = apartment + insert activation -- D-134: Groundhog Day alarm clock homage (first day only) — Mellanie writes the monologue +- D-136: First Settled Reach moment = apartment + insert activation +- D-126: Groundhog Day alarm clock homage (first day only) — Mellanie writes the monologue ## Current Priorities Consult `docs/sprints/` for active sprint briefing. @@ -59,7 +59,7 @@ Consult `docs/sprints/` for active sprint briefing. 1. **Culture-primary voice cards for Van Maanen's Star** — the primary pipeline deliverable. Blocked by Miri's culture profile. Once Miri delivers the Van Maanen's Star culture profile (D-128), Mellanie formalises it as a voice card: tone register, formality norms, emotional expression, verbal tics, what Van Maanen's Star people sound like under stress vs at ease. This feeds the AI content pipeline (D-123). 2. **Job-as-modifier voice spec** — how does being a small business owner modify the Van Maanen's Star base voice? What does a tycoon sound like vs a logistics worker vs a bar owner? Design the modifier layer (D-121). 3. **Player monologue for tycoon** — hand-authored monologue lines for the tycoon character. Voice, tone, what they notice, what they care about. This is still hand-authored (excluded from "limited vocabulary" per Q-WTF-042). -4. **Day 1 content** — alarm clock homage (D-134), insert activation moment (D-131). These are authored set-pieces. +4. **Day 1 content** — alarm clock homage (D-126), insert activation moment (D-136). These are authored set-pieces. 5. **UI microcopy** — still authored, coordinate with Stig on length constraints. ## Open Questions Assigned to You diff --git a/docs/briefings/nigel.md b/docs/briefings/nigel.md index 3bb35014b..134c10c1d 100644 --- a/docs/briefings/nigel.md +++ b/docs/briefings/nigel.md @@ -24,8 +24,8 @@ The replayability frame shifts from "same conspiracy, different character lens" **What survives:** - Variable entanglement rate per seed (D-029) — the principle is unchanged; the triangles are economic and social rather than investigation-intrigue -- Generator variety design (D-136): both structural and cosmetic variety at different scales. Same design problem, generator context. -- "No skill ceiling in v0.2" (D-137) — skill progression open-ended, not ladder-gated +- Generator variety design (D-137): both structural and cosmetic variety at different scales. Same design problem, generator context. +- "No skill ceiling in v0.2" (D-120) — skill progression open-ended, not ladder-gated - Emergent narrative as primary replayability mechanism (D-127) ## Decisions Relevant to Your Role @@ -42,8 +42,8 @@ The replayability frame shifts from "same conspiracy, different character lens" - D-119: Generator spike — Phase 5 per D-166 cascade (not imminent) - D-122: All NPCs generated - D-127: Player choices are the content — Rimworld model -- D-136: Both structural and cosmetic generator variety at different scales -- D-137: No skill ceiling in v0.2 +- D-137: Both structural and cosmetic generator variety at different scales +- D-120: No skill ceiling in v0.2 ## Open Questions Assigned to You - Q-010: Storyteller AI architecture (co-lead with Gestalt) @@ -52,7 +52,7 @@ The replayability frame shifts from "same conspiracy, different character lens" Consult `docs/sprints/` for active sprint briefing. ### v0.2 replayability design: -1. **Generator variety spec** — structural and cosmetic variety at different scales (D-136). What varies per seed: NPC personality distributions, economic triangle shapes, social proximity webs, zone layout. Design the variety dimensions the generator must support. +1. **Generator variety spec** — structural and cosmetic variety at different scales (D-137). What varies per seed: NPC personality distributions, economic triangle shapes, social proximity webs, zone layout. Design the variety dimensions the generator must support. 2. **Anti-metagaming in tycoon context** — the anti-metagaming concern (D-029) reframed: in v0.2, what would "solving the game" look like? Knowing which NPC personality types to befriend? Optimal economic strategies? Design variety to prevent calibration. 3. **Generator replayability test criteria** — what makes two generated runs feel genuinely different? Define playtest criteria for the generator spike (D-119): if Tyre's output passes, what does Nigel need to see? 4. **Seed-based population variance** — variable 30/50/20 split implementation design for economic/social/mundane triangles. diff --git a/docs/briefings/ozzie.md b/docs/briefings/ozzie.md index f6630dbe9..17c5920ca 100644 --- a/docs/briefings/ozzie.md +++ b/docs/briefings/ozzie.md @@ -47,12 +47,12 @@ The workshop revealed a core framing error: the game was built as a detective pu - D-117: Tycoon bookmark, zero investigation content - D-122: All NPCs generated, no named hand-authored characters - D-125: Quietly responsive world — gradient of caring by social proximity -- D-126: Full character customisation (hair, clothing, colours) +- D-134: Full character customisation (hair, clothing, colours) - D-128: Culture implicit in starting location (Van Maanen's Star = Van Maanen's Star culture) - D-129: Traits + observable behaviour as NPC personality surface -- D-131: First Settled Reach moment = apartment + insert activation +- D-136: First Settled Reach moment = apartment + insert activation - D-132: Consequence model dual-scale (sharp events + slow accumulation) -- D-134: Groundhog Day alarm clock homage (first day only) +- D-126: Groundhog Day alarm clock homage (first day only) ## Current Priorities Consult `docs/sprints/` for active sprint briefing. diff --git a/docs/briefings/stig.md b/docs/briefings/stig.md index c8295a5a7..45084c784 100644 --- a/docs/briefings/stig.md +++ b/docs/briefings/stig.md @@ -27,8 +27,8 @@ Read `decisions/perception.md` (primary) and `decisions/scope.md` (D-013). Key d - **D-028:** Dialogue UI — four relational layers (access tiers, history, trust-gated, unprompted) - **D-033:** Entity color = relationship to player — palette: unknown teal, friendly green, POI amber, hostile red, static grey. 0.5s transitions. Unchanged in v0.2. - **D-117:** Tycoon bookmark — single character type in v0.2 (no smuggler/detective variants) -- **D-126:** Full character customisation (hair, clothing, colours) — new UI requirement -- **D-131:** First Settled Reach moment = apartment + insert activation — UI must support this moment +- **D-134:** Full character customisation (hair, clothing, colours) — new UI requirement +- **D-136:** First Settled Reach moment = apartment + insert activation — UI must support this moment ## What's Been Delivered (Sprints 1-4) ### Sprint 1 (Run): IPC bridge, player movement @@ -40,7 +40,7 @@ Read `decisions/perception.md` (primary) and `decisions/scope.md` (D-013). Key d Consult `docs/sprints/` for active sprint briefing. ### v0.2 client UI: -1. **Character customisation screen** — hair, clothing, colours (D-126). Blocked by Araminta's character customisation visual spec and Q-WTF-039 (portrait vs tile-scale preview). Once Araminta delivers the spec, implement. +1. **Character customisation screen** — hair, clothing, colours (D-134). Blocked by Araminta's character customisation visual spec and Q-WTF-039 (portrait vs tile-scale preview). Once Araminta delivers the spec, implement. 2. **Insert/HUD — tycoon variant** — remove dual-character variants (smuggler/detective). Single `lattice_profile` parameter. Coordinate with Araminta on tycoon insert wireframe. 3. **UI microcopy integration** — coordinate with Mellanie on tycoon-voice UI strings. YAML format, length constraints. - File location: `content/campaigns/main/_meta/ui-strings.yaml` or `client/scripts/constants/ui_strings.gd`