feat(@projects/@magic-civilization): update sprite asset objectives

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-04-17 15:04:21 -07:00
parent b303b47ade
commit 7f109098ff
12 changed files with 197 additions and 113 deletions

View file

@ -106,17 +106,17 @@
| [p2-10](p2-10-regression-ci-gate.md) | 🟡 partial | Automated regression CI gate on every push to main | [testwright](../team-leads/testwright.md) | 2026-04-17 |
| [p2-11](p2-11-version-about-screen.md) | ✅ done | Version string + About screen | [shipwright](../team-leads/shipwright.md) | 2026-04-17 |
| [p2-16](p2-16-audio-assets.md) | ❌ missing | Audio assets — SFX + music .ogg files shipped | [asset-audio](../team-leads/asset-audio.md) | 2026-04-17 |
| [p2-17](p2-17-sprite-assets.md) | ❌ missing | Sprite assets — full unit / building / race / tier coverage | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-17 |
| [p2-18](p2-18-guide-public-deployment.md) | 🟡 partial | Guide web app — public hosting + deploy pipeline | — | 2026-04-17 |
| [p2-19](p2-19-guide-progress-report-page.md) | ✅ done | Guide progress report page — dynamic dashboard + missing assets | — | 2026-04-17 |
| [p2-20](p2-20-guide-sim-cache-pnpm-resolve.md) | ❌ missing | Fix simCachePlugin pre-warm worker — tsx can't resolve @magic-civ/physics-rs through pnpm symlink | [tourguide](../team-leads/tourguide.md) | 2026-04-17 |
| [p2-21](p2-21-guide-simcache-static-bake.md) | ❌ missing | Bake pre-computed sim-cache frames into the static build | [tourguide](../team-leads/tourguide.md) | 2026-04-17 |
| [p2-21](p2-21-sprite-generation-pipeline.md) | ❌ missing | Sprite generation pipeline — runnable end-to-end | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-17 |
| [p2-22](p2-22-unit-sprites-dwarf-roster.md) | ❌ missing | Unit sprites — Dwarf-racial roster (m/f variants) | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-17 |
| [p2-23](p2-23-unit-sprites-wild-creatures.md) | ❌ missing | Unit sprites — wild creatures & fauna (generic, no race/sex) | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-17 |
| [p2-24](p2-24-building-sprites-base-coverage.md) | ❌ missing | Building sprites — base game coverage (non-wonder) | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-17 |
| [p2-25](p2-25-mundane-wonder-sprites.md) | ❌ missing | Mundane-wonder sprites — 24 distinct, higher-fidelity art | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-17 |
| [p2-26](p2-26-city-population-tier-sprites.md) | ❌ missing | City population-tier sprites — city_q1 through city_q5 | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-17 |
| [p2-22](p2-22-sprite-generation-pipeline.md) | ❌ missing | Sprite generation pipeline — runnable end-to-end | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-17 |
| [p2-23](p2-23-unit-sprites-dwarf-roster.md) | ❌ missing | Unit sprites — Dwarf-racial roster (m/f variants) | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-17 |
| [p2-24](p2-24-unit-sprites-wild-creatures.md) | ❌ missing | Unit sprites — wild creatures & fauna (generic, no race/sex) | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-17 |
| [p2-25](p2-25-building-sprites-base-coverage.md) | ❌ missing | Building sprites — base game coverage (non-wonder) | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-17 |
| [p2-26](p2-26-mundane-wonder-sprites.md) | ❌ missing | Mundane-wonder sprites — 24 distinct, higher-fidelity art | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-17 |
| [p2-27](p2-27-city-population-tier-sprites.md) | ❌ missing | City population-tier sprites — city_q1 through city_q5 | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-17 |
| [p2-28](p2-28-sprite-provenance-ledger.md) | ❌ missing | Sprite provenance ledger — LICENSES.md per-file attribution | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-17 |
## Out of Scope (Game 2 / Game 3)
@ -134,3 +134,11 @@
| [g3-05](g3-05-aether-school-oos.md) | ⚫ oos | Aether school spellbook — Game 3 (Age of Elves) | — | 2026-04-17 |
| [g3-06](g3-06-arcane-ascension-oos.md) | ⚫ oos | Arcane Ascension victory — Game 3 (Age of Elves) | — | 2026-04-17 |
## Superseded
> These objectives were split into narrower children. Files are retained as index stubs so external references don't 404. The `superseded_by:` frontmatter field names the replacement IDs.
| ID | Status | Title | Owner | Updated |
|---|---|---|---|---|
| [p2-17](p2-17-sprite-assets.md) | ♻️ superseded | Sprite assets — superseded index (split into p2-22 … p2-28) | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-17 |

View file

@ -1,27 +1,27 @@
---
id: p2-17
title: Sprite assets — superseded index (split into p2-21 … p2-27)
title: Sprite assets — superseded index (split into p2-22 … p2-28)
priority: p2
status: superseded
scope: game1
owner: asset-sprite
updated_at: 2026-04-17
superseded_by:
- p2-21
- p2-22
- p2-23
- p2-24
- p2-25
- p2-26
- p2-27
- p2-28
evidence:
- .project/objectives/p2-21-sprite-generation-pipeline.md
- .project/objectives/p2-22-unit-sprites-dwarf-roster.md
- .project/objectives/p2-23-unit-sprites-wild-creatures.md
- .project/objectives/p2-24-building-sprites-base-coverage.md
- .project/objectives/p2-25-mundane-wonder-sprites.md
- .project/objectives/p2-26-city-population-tier-sprites.md
- .project/objectives/p2-27-sprite-provenance-ledger.md
- .project/objectives/p2-22-sprite-generation-pipeline.md
- .project/objectives/p2-23-unit-sprites-dwarf-roster.md
- .project/objectives/p2-24-unit-sprites-wild-creatures.md
- .project/objectives/p2-25-building-sprites-base-coverage.md
- .project/objectives/p2-26-mundane-wonder-sprites.md
- .project/objectives/p2-27-city-population-tier-sprites.md
- .project/objectives/p2-28-sprite-provenance-ledger.md
---
## Summary
@ -30,12 +30,12 @@ Split on 2026-04-17 into seven narrower objectives. The original single-file for
Replaced by:
- **[p2-21](p2-21-sprite-generation-pipeline.md)** — `tools/sprite-generation/` runnable end-to-end (gates every child below).
- **[p2-22](p2-22-unit-sprites-dwarf-roster.md)** — Dwarf-racial unit sprites with m/f variants.
- **[p2-23](p2-23-unit-sprites-wild-creatures.md)** — wild-creature unit sprites (generic, no race/sex).
- **[p2-24](p2-24-building-sprites-base-coverage.md)** — non-wonder building sprites.
- **[p2-25](p2-25-mundane-wonder-sprites.md)** — 24 mundane wonders at higher fidelity.
- **[p2-26](p2-26-city-population-tier-sprites.md)** — `city_q1.png``city_q5.png`.
- **[p2-27](p2-27-sprite-provenance-ledger.md)** — `LICENSES.md` per-file attribution (runs continuously alongside p2-22…p2-26).
- **[p2-22](p2-22-sprite-generation-pipeline.md)** — `tools/sprite-generation/` runnable end-to-end (gates every child below).
- **[p2-23](p2-23-unit-sprites-dwarf-roster.md)** — Dwarf-racial unit sprites with m/f variants.
- **[p2-24](p2-24-unit-sprites-wild-creatures.md)** — wild-creature unit sprites (generic, no race/sex).
- **[p2-25](p2-25-building-sprites-base-coverage.md)** — non-wonder building sprites.
- **[p2-26](p2-26-mundane-wonder-sprites.md)** — 24 mundane wonders at higher fidelity.
- **[p2-27](p2-27-city-population-tier-sprites.md)** — `city_q1.png``city_q5.png`.
- **[p2-28](p2-28-sprite-provenance-ledger.md)** — `LICENSES.md` per-file attribution (runs continuously alongside p2-23…p2-27).
This file is retained as an index stub so external references (git history, prior conversations, the team-lead charter before the split) resolve to a pointer rather than a 404. It carries no acceptance bullets of its own — progress is tracked in the children.

View file

@ -1,5 +1,5 @@
---
id: p2-21
id: p2-22
title: Sprite generation pipeline — runnable end-to-end
priority: p2
status: missing
@ -16,14 +16,14 @@ evidence:
## Summary
Gate-one objective for every other `asset-sprite` child (`p2-22` … `p2-26`). Before any sprite can legitimately land in `public/games/age-of-dwarves/assets/sprites/`, the `tools/sprite-generation/` pipeline has to run cleanly end-to-end: scan game data → generate variants via the configured model → auto-rank via Sonnet vision → surface in the Theater GUI for human approval → chroma-key + resize + install with LICENSES.md row written.
Gate-one objective for every other `asset-sprite` child (`p2-23` … `p2-27`). Before any sprite can legitimately land in `public/games/age-of-dwarves/assets/sprites/`, the `tools/sprite-generation/` pipeline has to run cleanly end-to-end: scan game data → generate variants via the configured model → auto-rank via Sonnet vision → surface in the Theater GUI for human approval → chroma-key + resize + install with LICENSES.md row written.
Slate is clean (user deleted 7 pre-existing sprites on 2026-04-17 for quality-bar failure; the prompt library and ranker had drifted). This objective closes out the "pipeline works" half of the split; actual sprite shipping lives in the downstream children.
## Acceptance
- ✗ `tools/sprite-generation/cli.py run --category units --variants 1` completes one full loop on a clean DB: sprite picked → 1 variant generated → ranker writes notes + rating → `status` flips from `needed` to `review` or back to `needed` (never crashes mid-loop).
- ✗ `tools/sprite-generation/cli.py approve <sprite_id>` on a reviewed sprite chroma-keys, resizes to 256×256 (or 512×512 — the choice is documented in `sprite-config.json`), drops the PNG into the install path, and appends a row to `assets/sprites/LICENSES.md` (contract surface for `p2-27`).
- ✗ `tools/sprite-generation/cli.py approve <sprite_id>` on a reviewed sprite chroma-keys, resizes to 256×256 (or 512×512 — the choice is documented in `sprite-config.json`), drops the PNG into the install path, and appends a row to `assets/sprites/LICENSES.md` (contract surface for `p2-28`).
- ✗ `engine/scanner.py` introspects every file in `public/games/age-of-dwarves/data/units/*.json` and `data/buildings/*.json` (excluding `stub.json` / `manifest.json`) and materialises one row per sprite target in `spritegen.db`.sprites.
- ✗ `engine/prompts.py` composes a prompt for every sprite target using the YAML library under `engine/prompts/` (`races.yaml` with `id: dwarf`, `genders.yaml`, `unit_classes.yaml`, `combat_types.yaml`, `quality_tiers.yaml`, `composition.yaml`, `negatives.yaml`, `keywords.yaml`, `styles.yaml`) — no hardcoded strings in `prompts.py` itself.
- ✗ `sprite-config.json` points at a model from the CLAUDE.md approved list (`juggernaut-xl-v9`, `epicrealism-xl`, `illustrious-xl-v2`, or current equivalent). No anime-model variants.
@ -37,6 +37,6 @@ Slate is clean (user deleted 7 pre-existing sprites on 2026-04-17 for quality-ba
## Non-goals
- Actually shipping any sprite PNG into `assets/sprites/` (that's `p2-22` … `p2-26`).
- Actually shipping any sprite PNG into `assets/sprites/` (that's `p2-23` … `p2-27`).
- GPU autoscaling, distributed batch runs, or cross-host orchestration (post-EA if ever).
- Replacement of `tools/gen-fallback-sprites.py` — the SVG fallback tool stays for procedural dev-baseline; it's not part of the ship-art path and is not owned by this objective.

View file

@ -1,5 +1,5 @@
---
id: p2-22
id: p2-23
title: Unit sprites — Dwarf-racial roster (m/f variants)
priority: p2
status: missing
@ -18,28 +18,28 @@ Every Dwarf-controlled combat + support unit declared in `public/games/age-of-dw
The current roster of gender-bearing units in `data/units/` (2026-04-17 snapshot): `archer`, `berserker`, `cavalry`, `pikeman`, `runesmith`, `spearmen`, `warrior`, `worker` — 8 unit types × 2 genders = 16 PNG files. Race id is `dwarf` (singular — confirmed in `public/games/age-of-dwarves/data/races.json`); the `Player.race_id` fallback in `unit_renderer.gd:_resolve_race_id()` resolves to that value at runtime.
Slate is clean — the 7 previously-authored sprites were deleted 2026-04-17 for quality-bar failure. Do not restore; regenerate via `p2-21` once the pipeline is green.
Slate is clean — the 7 previously-authored sprites were deleted 2026-04-17 for quality-bar failure. Do not restore; regenerate via `p2-22` once the pipeline is green.
## Acceptance
- ✗ For each unit `<u>` in `data/units/*.json` with a `gender` block, `<u>_dwarf_m.png` and `<u>_dwarf_f.png` exist in `public/games/age-of-dwarves/assets/sprites/units/`. Filename contract: `SPRITE_LOOKUP_RACE_SEX_FORMAT = "sprites/units/%s_%s_%s.png"` (`unit_renderer.gd:52`).
- ✗ For each such unit, a generic `<u>.png` also exists (renderer's fallback chain in `unit_renderer.gd:_get_unit_sprite()`; present so raceless/genderless resolution paths also render art).
- ✗ Each PNG is 256×256 or 512×512 (resolution choice consistent across the roster, documented once in `p2-27` LICENSES.md header), PNG with alpha channel, transparent background (no baked backdrop).
- ✗ Each PNG is 256×256 or 512×512 (resolution choice consistent across the roster, documented once in `p2-28` LICENSES.md header), PNG with alpha channel, transparent background (no baked backdrop).
- ✗ Style coherence: all 16 sprites came from the same `tools/sprite-generation/` run or from a commission brief using the same prompt library — no mixed provenance within the roster. Verified by running `python3 tools/sprite-generation/cli.py verify --category units --race dwarf` (or equivalent; introduce if not present) returning zero mismatches.
- ✗ Each sprite passed the ranker threshold configured in `p2-21`, OR was approved via the Theater GUI with a rating ≥ 4 in `spritegen.db`.
- ✗ Each sprite has a row in `public/games/age-of-dwarves/assets/sprites/LICENSES.md` (contract satisfied via `p2-27`).
- ✗ Each sprite passed the ranker threshold configured in `p2-22`, OR was approved via the Theater GUI with a rating ≥ 4 in `spritegen.db`.
- ✗ Each sprite has a row in `public/games/age-of-dwarves/assets/sprites/LICENSES.md` (contract satisfied via `p2-28`).
- ✗ Proof screenshot: one game turn rendered on plum Godot showing Dwarf units on the map with sprite overlays (not just procedural circles), captured via `tools/screenshot.sh` per `.claude/instructions/phase-gate-protocol.md`, SCP'd to `$SCREENSHOT_HOST`, reviewed in-conversation.
## Depends on
- `p2-21` (pipeline runnable end-to-end).
- `p2-22` (pipeline runnable end-to-end).
- `p0-23` (sprite rendering capability — done).
- `p2-27` (LICENSES.md ledger) — each sprite shipped here adds one row there.
- `p2-28` (LICENSES.md ledger) — each sprite shipped here adds one row there.
## Non-goals
- Wild-creature sprites (`p2-23`).
- Building sprites (`p2-24`).
- Wild-creature sprites (`p2-24`).
- Building sprites (`p2-25`).
- Animation frames / sprite sheets — post-EA.
- Sprites for the aspirational `dwarf_*`-prefixed unit ids in `data/units/manifest.json` (e.g. `dwarf_founder`, `dwarf_steam_cannon`). Those units are not currently materialised in `data/units/*.json` and are tracked separately when the data is authored.
- Units with `race_required` naming a non-Dwarf race — Game 1 is single-race; non-Dwarf playable races are Game 2 (`g2-02-additional-races-oos.md`) / Game 3.

View file

@ -1,5 +1,5 @@
---
id: p2-23
id: p2-24
title: Unit sprites — wild creatures & fauna (generic, no race/sex)
priority: p2
status: missing
@ -23,21 +23,21 @@ Each is a recognisable silhouette at the renderer's 56×56 hex render size (veri
## Acceptance
- ✗ For each wild unit `<w>` in `data/units/*.json` with `unit_type: "wild"`, `<w>.png` exists in `public/games/age-of-dwarves/assets/sprites/units/`. No race or sex suffix.
- ✗ Each PNG is 256×256 or 512×512 (matching the resolution chosen for `p2-22`), PNG with alpha channel, transparent background.
- ✗ Each PNG is 256×256 or 512×512 (matching the resolution chosen for `p2-23`), PNG with alpha channel, transparent background.
- ✗ Silhouette legibility: each sprite remains identifiable when downsampled to 56×56 (rendered hex size). Verified by eyeballing the `sprite_proof.tscn` rendering pass with each creature keyed into the cache in turn.
- ✗ Each sprite passed the ranker threshold configured in `p2-21` or was approved via Theater GUI with rating ≥ 4 in `spritegen.db`.
- ✗ Each sprite has a row in `LICENSES.md` (contract satisfied via `p2-27`).
- ✗ Each sprite passed the ranker threshold configured in `p2-22` or was approved via Theater GUI with rating ≥ 4 in `spritegen.db`.
- ✗ Each sprite has a row in `LICENSES.md` (contract satisfied via `p2-28`).
- ✗ Proof screenshot: a map snapshot on plum Godot showing a lair tile + at least three wild creature types rendering with sprite overlays, captured via `tools/screenshot.sh` per `phase-gate-protocol.md`.
## Depends on
- `p2-21` (pipeline runnable end-to-end).
- `p2-22` (pipeline runnable end-to-end).
- `p0-23` (sprite rendering capability — done).
- `p2-27` (LICENSES.md ledger).
- `p2-28` (LICENSES.md ledger).
## Non-goals
- Dwarf-racial unit sprites (`p2-22`).
- Dwarf-racial unit sprites (`p2-23`).
- Race/sex variants for wild creatures — deliberately single sprite per id.
- Animated or multi-pose creature sheets — post-EA.
- Hostile-faction or barbarian-unique art — Game 2 concern.

View file

@ -1,5 +1,5 @@
---
id: p2-24
id: p2-25
title: Building sprites — base game coverage (non-wonder)
priority: p2
status: missing
@ -20,22 +20,22 @@ Current base roster (2026-04-17 snapshot, 10 buildings): `ale_hall`, `barracks`,
## Acceptance
- ✗ For each building id `<b>` parsed from the non-wonder files under `data/buildings/`, `<b>.png` exists in `public/games/age-of-dwarves/assets/sprites/buildings/`.
- ✗ Each PNG is 256×256 or 512×512 (matching the resolution chosen for `p2-22`/`p2-23`), PNG with alpha channel, transparent background.
- ✗ Each PNG is 256×256 or 512×512 (matching the resolution chosen for `p2-23`/`p2-24`), PNG with alpha channel, transparent background.
- ✗ Silhouette is legible at city-screen thumbnail scale (128×128 or the current city-screen icon size — whichever `city_screen.tscn` consumes).
- ✗ Style coherence: palette + lighting direction consistent across the 10 buildings. Pipeline-generated via the same prompt library as `p2-22` (`tools/sprite-generation/engine/prompts/` — building branch).
- ✗ Each sprite passed the ranker threshold configured in `p2-21` or was approved via Theater GUI with rating ≥ 4 in `spritegen.db`.
- ✗ Each sprite has a row in `LICENSES.md` (contract satisfied via `p2-27`).
- ✗ Style coherence: palette + lighting direction consistent across the 10 buildings. Pipeline-generated via the same prompt library as `p2-23` (`tools/sprite-generation/engine/prompts/` — building branch).
- ✗ Each sprite passed the ranker threshold configured in `p2-22` or was approved via Theater GUI with rating ≥ 4 in `spritegen.db`.
- ✗ Each sprite has a row in `LICENSES.md` (contract satisfied via `p2-28`).
- ✗ Proof screenshot: city-screen or world-map capture on plum Godot showing at least 4 distinct building sprites rendering on top of the procedural baseline, per `phase-gate-protocol.md`.
## Depends on
- `p2-21` (pipeline runnable end-to-end).
- `p2-22` (pipeline runnable end-to-end).
- `p0-23` (sprite rendering capability — done).
- `p2-27` (LICENSES.md ledger).
- `p2-28` (LICENSES.md ledger).
## Non-goals
- Mundane-wonder sprites (`p2-25`) — separate fidelity bar, separate objective.
- City population-tier sprites (`p2-26`) — different key format (`city_q<N>.png`).
- Mundane-wonder sprites (`p2-26`) — separate fidelity bar, separate objective.
- City population-tier sprites (`p2-27`) — different key format (`city_q<N>.png`).
- Building upgrade-stage visuals (post-EA if ever).
- Improvement / tile-yield icons — those are UI icons, not building sprites.

View file

@ -1,5 +1,5 @@
---
id: p2-25
id: p2-26
title: Mundane-wonder sprites — 24 distinct, higher-fidelity art
priority: p2
status: missing
@ -13,26 +13,26 @@ evidence:
## Summary
`public/games/age-of-dwarves/data/buildings/mundane_wonders.json` declares 24 Civ5-style world wonders for Game 1 (no magic schools — these are mundane flavour wonders). Each needs a **distinct, higher-fidelity** sprite — a generic "fancy building" look drags the whole wonder system's perceived prestige. Split from `p2-24` so the quality bar can be tracked independently.
`public/games/age-of-dwarves/data/buildings/mundane_wonders.json` declares 24 Civ5-style world wonders for Game 1 (no magic schools — these are mundane flavour wonders). Each needs a **distinct, higher-fidelity** sprite — a generic "fancy building" look drags the whole wonder system's perceived prestige. Split from `p2-25` so the quality bar can be tracked independently.
Current wonder roster (2026-04-17 snapshot): `ancestral_forge`, `mead_hall`, `first_mineshaft`, `clan_moot_stone`, `iron_bulwark`, `hall_of_ancestors`, `the_deep_road`, `bardic_circle`, `archive_of_runes`, `royal_runestone`, `grand_observatory`, `covenant_stone`, `the_great_forge`, `iron_crown`, `undermount_vault`, `hall_of_echoes`, `world_pillar`, `well_of_ages`, `the_undying_flame`, `voice_of_ages`, `silent_cartograph`, `shrine_of_names`, `the_cold_anvil`, `hearthless_hall`.
## Acceptance
- ✗ For each wonder id `<w>` in `mundane_wonders.json`, `<w>.png` exists in `public/games/age-of-dwarves/assets/sprites/buildings/` — one distinct silhouette per wonder, no palette-swapped reuse.
- ✗ Each PNG is 512×512, PNG with alpha channel, transparent background. (Higher resolution than the base-building bar in `p2-24`; documented in LICENSES.md header.)
- ✗ Each PNG is 512×512, PNG with alpha channel, transparent background. (Higher resolution than the base-building bar in `p2-25`; documented in LICENSES.md header.)
- ✗ Tier-appropriate grandeur: a wonder's visual complexity reflects its `unlock_tier` in `mundane_wonders.json`. Verified manually — flagged wonders with complexity incongruent to tier get re-generated.
- ✗ Style coherence within the wonder set: palette + lighting direction consistent across the 24, and readable as "ancient / monumental" rather than "functional building".
- ✗ Each sprite passed the ranker threshold configured in `p2-21` with a minimum rating ≥ 4.2 (stricter than `p2-22` / `p2-24`) OR was approved via Theater GUI at rating 5.
- ✗ Each sprite has a row in `LICENSES.md` (contract satisfied via `p2-27`).
- ✗ Each sprite passed the ranker threshold configured in `p2-22` with a minimum rating ≥ 4.2 (stricter than `p2-23` / `p2-25`) OR was approved via Theater GUI at rating 5.
- ✗ Each sprite has a row in `LICENSES.md` (contract satisfied via `p2-28`).
- ✗ Proof screenshot: capture on plum Godot showing a built wonder rendering on a city tile with its sprite overlay, plus the wonder card in the production queue showing the same art. `phase-gate-protocol.md`.
## Depends on
- `p2-21` (pipeline runnable end-to-end).
- `p2-22` (pipeline runnable end-to-end).
- `p0-04` (wonder tracking — done; wonders exist in simulation before we decorate them).
- `p0-23` (sprite rendering capability — done).
- `p2-27` (LICENSES.md ledger).
- `p2-28` (LICENSES.md ledger).
## Non-goals

View file

@ -1,5 +1,5 @@
---
id: p2-26
id: p2-27
title: City population-tier sprites — city_q1 through city_q5
priority: p2
status: missing
@ -18,18 +18,18 @@ evidence:
## Acceptance
- ✗ `city_q1.png`, `city_q2.png`, `city_q3.png`, `city_q4.png`, `city_q5.png` all exist in `public/games/age-of-dwarves/assets/sprites/cities/`.
- ✗ Each PNG is 256×256 or 512×512 (matching the chosen resolution for `p2-22`/`p2-24`), PNG with alpha channel, transparent background.
- ✗ Each PNG is 256×256 or 512×512 (matching the chosen resolution for `p2-23`/`p2-25`), PNG with alpha channel, transparent background.
- ✗ Visual progression: each tier is clearly "larger / denser" than the previous — a player glancing at the map can tell q1 from q3 without zoom.
- ✗ Silhouette legible at the world-map rendered size (the resolved size in `city_renderer.gd` `_draw_city_sprite()`).
- ✗ Each sprite passed the ranker threshold configured in `p2-21` with rating ≥ 4, OR was approved via Theater GUI.
- ✗ Each sprite has a row in `LICENSES.md` (contract satisfied via `p2-27`).
- ✗ Each sprite passed the ranker threshold configured in `p2-22` with rating ≥ 4, OR was approved via Theater GUI.
- ✗ Each sprite has a row in `LICENSES.md` (contract satisfied via `p2-28`).
- ✗ Proof screenshot: plum Godot capture of a game late enough to have cities at three distinct population tiers, showing the tier sprites rendering distinctly on the world map. `phase-gate-protocol.md`.
## Depends on
- `p2-21` (pipeline runnable end-to-end).
- `p2-22` (pipeline runnable end-to-end).
- `p0-23` (sprite rendering capability — done).
- `p2-27` (LICENSES.md ledger).
- `p2-28` (LICENSES.md ledger).
## Non-goals

View file

@ -1,5 +1,5 @@
---
id: p2-27
id: p2-28
title: Sprite provenance ledger — LICENSES.md per-file attribution
priority: p2
status: missing
@ -13,7 +13,7 @@ evidence:
## Summary
Every sprite PNG that ships in `public/games/age-of-dwarves/assets/sprites/` must have a corresponding row in `public/games/age-of-dwarves/assets/sprites/LICENSES.md` recording source, license, author, URL, and SHA256. This is a cross-cutting compliance objective that runs continuously alongside the delivery children (`p2-22` … `p2-26`) — the ledger is complete exactly when every on-disk sprite has a matching row and every row points at an on-disk file.
Every sprite PNG that ships in `public/games/age-of-dwarves/assets/sprites/` must have a corresponding row in `public/games/age-of-dwarves/assets/sprites/LICENSES.md` recording source, license, author, URL, and SHA256. This is a cross-cutting compliance objective that runs continuously alongside the delivery children (`p2-23` … `p2-27`) — the ledger is complete exactly when every on-disk sprite has a matching row and every row points at an on-disk file.
Commercial-use compatibility is non-negotiable. AI-generated output must come from a model on the approved list (`juggernaut-xl-v9`, `epicrealism-xl`, `illustrious-xl-v2`, or current equivalent per CLAUDE.md). Commissioned art must have assigned commercial rights in writing.
@ -24,13 +24,13 @@ Commercial-use compatibility is non-negotiable. AI-generated output must come fr
- ✗ Every PNG file under `assets/sprites/**/*.png` has exactly one row in `LICENSES.md` — no duplicates, no missing entries. Verified by a script (to be authored under `tools/` — e.g. `tools/sprite-license-audit.py`) that diffs `ls assets/sprites/**/*.png` against the row paths.
- ✗ Every row in `LICENSES.md` points at an existing file on disk with the declared SHA256. Same script verifies both directions.
- ✗ No row carries a license that is not commercial-use-compatible. License whitelist documented at the top of `LICENSES.md`.
- ✗ `tools/sprite-generation/engine/installer.py` (touched by `p2-21`) appends a row on every approved install; no sprite lands in `assets/sprites/` without the ledger being updated in the same commit.
- ✗ `tools/sprite-generation/engine/installer.py` (touched by `p2-22`) appends a row on every approved install; no sprite lands in `assets/sprites/` without the ledger being updated in the same commit.
- ✗ Running `python3 tools/sprite-license-audit.py` returns exit 0 with zero mismatch output in CI (integration with `./run verify` tracked as an optional enhancement; the script existing and passing locally is the ship gate).
## Depends on
- `p2-21` (pipeline runnable end-to-end) — installer path is where ledger rows are written.
- `p2-22` … `p2-26` — deliver the sprite files this ledger audits; `p2-27` closes when their combined on-disk set matches the ledger.
- `p2-22` (pipeline runnable end-to-end) — installer path is where ledger rows are written.
- `p2-23` … `p2-27` — deliver the sprite files this ledger audits; `p2-28` closes when their combined on-disk set matches the ledger.
## Non-goals

View file

@ -3,44 +3,74 @@ id: asset-sprite
name: Asset — Sprite
specialization: Generate, commission, and ship unit / building / wonder sprite art per the sprite-rendering capability contract
objectives:
- p2-17
- p2-22
- p2-23
- p2-24
- p2-25
- p2-26
- p2-27
- p2-28
---
## Mandate
Ship the `.png` sprite files that the renderers expect when `ThemeAssets.load_sprite()` is called. The rendering *capability* is being built in p0-23: renderers draw procedural primitives first (unconditional baseline), then overlay a sprite if one exists at the resolved path. Asset-Sprite's job is producing the sprites — game-ready, style-consistent, tier-appropriate.
Ship the `.png` sprite files that the renderers expect when `ThemeAssets.load_sprite()` is called. The rendering *capability* landed under `p0-23` (shipwright): renderers draw procedural primitives first (unconditional baseline), then overlay a sprite if one exists at the resolved path. Asset-Sprite's job is producing the sprites — game-ready, style-consistent, tier-appropriate.
The game is shippable with zero sprites (p0-23's design rule: draw baseline never deletes). Asset-Sprite's delivery progressively replaces the circle-and-letter placeholders with real art, unit by unit, building by building.
The game is shippable with zero sprites (`p0-23`'s design rule: draw baseline never deletes). Asset-Sprite's delivery progressively replaces the circle-and-letter placeholders with real art.
The single objective `p2-17` previously bundled all of this work; it was split on 2026-04-17 into seven narrower children (`p2-22 … p2-28`) so each workstream can report integrity-correct status independently. `p2-17` is retained as a `status: superseded` index stub.
## Owned objectives
| ID | Workstream | Gates |
|---|---|---|
| [p2-22](../objectives/p2-22-sprite-generation-pipeline.md) | Pipeline readiness — `tools/sprite-generation/` end-to-end | Gates p2-23 … p2-27 |
| [p2-23](../objectives/p2-23-unit-sprites-dwarf-roster.md) | Unit sprites — Dwarf-racial roster (m/f variants) | Depends on p2-22 |
| [p2-24](../objectives/p2-24-unit-sprites-wild-creatures.md) | Unit sprites — wild creatures (generic key) | Depends on p2-22 |
| [p2-25](../objectives/p2-25-building-sprites-base-coverage.md) | Building sprites — base roster | Depends on p2-22 |
| [p2-26](../objectives/p2-26-mundane-wonder-sprites.md) | Wonder sprites — 24 distinct at higher fidelity | Depends on p2-22 |
| [p2-27](../objectives/p2-27-city-population-tier-sprites.md) | City population-tier sprites — `city_q1``city_q5` | Depends on p2-22 |
| [p2-28](../objectives/p2-28-sprite-provenance-ledger.md) | `LICENSES.md` per-file attribution | Runs continuously alongside p2-23 … p2-27 |
## Owned surface
- `public/games/age-of-dwarves/assets/sprites/units/<unit_id>_<race>_<sex>.png` — every `data/units/*.json` entry should have a matching sprite variant per race × sex combination the game supports
- `public/games/age-of-dwarves/assets/sprites/buildings/<building_id>.png` — every `data/buildings/*.json` entry + every wonder in `mundane_wonders.json`
- `public/games/age-of-dwarves/assets/sprites/LICENSES.md` — per-file attribution (source, license, author, URL, SHA256)
- `tools/sprite-generation/` — the generation pipeline (prompt authoring, model inference, post-processing, normalization, drop-into-assets)
- Any authoring data: style references, prompt libraries, generation seeds, post-process scripts
- `public/games/age-of-dwarves/assets/sprites/units/<unit_id>_<race>_<sex>.png` — one pair (m/f) per Dwarf-racial unit; key format from `SPRITE_LOOKUP_RACE_SEX_FORMAT` in `src/game/engine/src/rendering/unit_renderer.gd`.
- `public/games/age-of-dwarves/assets/sprites/units/<unit_id>.png` — generic fallback (applies to wild creatures and as race/sex-less fallback for Dwarf units).
- `public/games/age-of-dwarves/assets/sprites/buildings/<building_id>.png` — one per building id in `data/buildings/*.json` (including `mundane_wonders.json`).
- `public/games/age-of-dwarves/assets/sprites/cities/city_q<N>.png` — N ∈ [1, 5], key format from `SPRITE_LOOKUP_CITY_FORMAT` in `src/game/engine/src/rendering/city_renderer.gd`.
- `public/games/age-of-dwarves/assets/sprites/LICENSES.md` — per-file attribution (schema in `p2-28`).
- `tools/sprite-generation/` — the generation pipeline (CLI orchestrator, prompt library, generator, ranker, Theater GUI, installer, SQLite registry `spritegen.db`).
Does NOT own:
- `src/game/engine/src/rendering/*_renderer.gd` — the runtime sprite load + overlay path. Owned by godot-renderer. Asset-Sprite reads the expected path conventions and produces files that match.
- `src/game/engine/src/rendering/*_renderer.gd` — runtime sprite load + overlay path. Owned by `shipwright` via `p0-23`. Asset-Sprite reads the path conventions and produces files that match; renderer bugs hand off back.
- Sprite generation model selection beyond the CLAUDE.md rule ("NEVER use anime models for game art — use `juggernaut-xl-v9`, `epicrealism-xl`, `illustrious-xl-v2`").
- `tools/gen-fallback-sprites.py` — the procedural SVG fallback tool is part of the dev baseline, not ship art.
## Working constraints
- **PNG with alpha channel.** Square canvas, 256×256 or 512×512 (document the choice in LICENSES.md).
- **Transparent background** — renderer composites sprite onto hex; any background color would bake incorrectly.
- **Consistent art style** across the library — use MTG-color-coded palette per the sprite-generation pipeline doc, but the Dwarf race is the only Game 1 race so visual coherence is within-race, not cross-race.
- **License must be commercial-use compatible**AI-generated output has to be from a model the project is licensed to use commercially (check juggernaut-xl, epicrealism-xl, illustrious-xl-v2 license terms at ship time). Commissioned art must have assigned commercial rights.
- **SHA256 recorded in LICENSES.md** for binary-diff traceability.
- **Budget**: 256×256 PNG at reasonable quality ≈ 30-80KB. Full library of ~100-300 sprites ≤ 20MB.
- **Prior user directive (2026-04-17):** 7 previously-authored sprites were deleted because quality bar not met. Slate is clean. Re-generate from the pipeline or commission — do not restore the deleted files.
- **PNG with alpha channel.** Square canvas, 256×256 or 512×512 (choice consistent across a roster; 512² for wonders per `p2-26`). Resolution choice documented once in `LICENSES.md` header.
- **Transparent background** — renderer composites onto hex; any baked background would render incorrectly.
- **Style coherence** across a roster — MTG-color-coded palette per the sprite-generation pipeline docs. Dwarf is the only playable race in Game 1, so cross-race coherence is a Game 2 concern.
- **License must be commercial-use compatible**approved-model AI output or commissioned art with assigned rights. Ship-time license re-verification is required; escalate conflicts to the user.
- **SHA256 per file** in `LICENSES.md` for binary-diff traceability.
- **Budget**: 256² PNG ≈ 3080 KB, 512² ≈ 100250 KB. Full library target ≤ 20 MB.
- **Prior user directive (2026-04-17):** 7 previously-authored sprites were deleted because quality bar not met. Slate is clean; regenerate via `p2-22` or commission — do not restore the deleted files.
## Acceptance loop
Per p2-17's acceptance bullets. Deliveries can land incrementally — every sprite added to `assets/sprites/` renders the next time the game boots. Flip p2-17 to `done` when all declared units + buildings + wonders have at least one sprite variant, LICENSES.md is complete, and a proof screenshot shows a sample map with sprite-rendered entities co-existing with draw-baseline fallbacks.
Per the acceptance bullets of each child objective. Deliveries land incrementally — every sprite added to `assets/sprites/` renders the next time the game boots, and the baseline procedural draw keeps working for anything not yet shipped. An objective flips to `done` only when every acceptance bullet is `✓` with cited evidence, per `.claude/instructions/objective-integrity.md`.
Sequencing:
1. `p2-22` first — without a green pipeline, there's no way to produce style-consistent sprites at quality bar.
2. `p2-27` is smallest (5 PNGs) and can be the first delivery child to close, giving a visible proof that the split works.
3. `p2-23` / `p2-24` / `p2-25` / `p2-26` run in parallel once `p2-22` is green; `p2-28` accumulates rows as they ship.
4. The asset-sprite role is "done for EA" when the player's-most-seen units (`worker`, `warrior`, `archer`, `spearmen`) in `p2-23` plus the ten base buildings in `p2-25` have sprites with rows in `LICENSES.md`. Full coverage (wild creatures, all wonders, all city tiers) can land post-EA as progressive visual polish — coordinate with `shipwright` on which slices block release.
## Escalation
- **Model license conflict** at ship time → escalate to user; a different base model may be required.
- **Style drift** between batches of generated sprites → stop generating, iterate on the prompt library / post-process pipeline before shipping more.
- **Renderer can't load a specific PNG** (e.g. format bug, alpha channel issue) → handoff to godot-renderer; Asset-Sprite does not patch renderer code.
- **Budget / timeline pressure** → coordinate with shipwright; most sprites can land post-EA as progressive visual polish rather than blocking release. Prioritize "player's most-seen units" (Founder, Warrior, Scout) for EA.
- **Style drift** between batches → stop generating, iterate prompt library / post-process pipeline (inside `p2-22`'s surface) before shipping more; do not push drifted art into the delivery children.
- **Renderer can't load a specific PNG** (format bug, alpha-channel issue, etc.) → handoff to `shipwright` / `godot-renderer`. Asset-Sprite does not patch renderer code.
- **Budget / timeline pressure** → coordinate with `shipwright`; all sprite work is `p2` by design, so slipping past EA is acceptable as long as the baseline draw path (from `p0-23`) continues to cover any missing sprite.

View file

@ -1,11 +1,11 @@
{
"generated_at": "2026-04-17T21:58:28Z",
"generated_at": "2026-04-17T22:01:06Z",
"totals": {
"done": 35,
"stub": 6,
"partial": 16,
"missing": 11,
"partial": 16,
"oos": 9,
"stub": 6,
"total": 77
},
"objectives": [
@ -579,16 +579,6 @@
"updated_at": "2026-04-17",
"summary": "The audio capability shipped as **p0-21** — `AudioManager`, manifest, signal wiring, volume sliders all work. What's missing is the 16 actual `.ogg` files the manifest declares. Gameplay is currently silent. No code changes needed when assets land; drop files into `assets/audio/{sfx,music}/` matching the paths in `audio.json`.\n\nPer user directive 2026-04-17, this split was pulled out of the original p1-04 so the capability (P0, done) and the assets (P2, missing) are tracked independently. A silent ship is shippable; a broken audio system is not."
},
{
"id": "p2-17",
"title": "Sprite assets — full unit / building / race / tier coverage",
"priority": "p2",
"status": "missing",
"scope": "game1",
"owner": "asset-sprite",
"updated_at": "2026-04-17",
"summary": "With p0-22 capability in place, the game needs comprehensive sprite coverage:\n- Every unit × race × sex combination declared in `data/units/`\n- Every building declared in `data/buildings/`\n- Per-tier variants where meaningful (T1-T4 sprites can look different from T7-T10)\n\nCurrently 0 sprite files exist — prior 7 were deleted 2026-04-17 per user directive (quality bar not met). Hundreds are expected when the full sprite generation pipeline runs (or commissioned art lands). Slate is clean."
},
{
"id": "p2-18",
"title": "Guide web app — public hosting + deploy pipeline",
@ -630,27 +620,27 @@
"summary": "`simCachePlugin` (Vite dev plugin) pre-computes climate-simulator\nscenarios on `pnpm dev` startup and serves the resulting frames over\n`/__sim-cache/<scenario>/{status,frame/<n>}` so\n`/climate/simulation` can load pre-rendered video-like playback\ninstead of running WASM inline for minutes on cold visits. Today this\nis dev-only; on production / `.next.` deploys there is no server to\nrun the plugin, so the frontend falls back to client-WASM — slow\ncold-start, but works.\n\nThis objective fills the gap: at build time, run each canonical\nscenario headlessly (node + the WASM pkg), emit the same binary frame\nformat `simCachePlugin` serves, and drop the output at\n`dist/__sim-cache/<scenario>/...` so the static deploy serves the\nsame byte streams the dev plugin serves. The frontend doesn't change\n— it still GETs `/__sim-cache/base_no_magic/status?…` and gets the\nsame shape. The `try_files $uri $uri/` line in the\n`mc.next.black.local` vhost (p1-15) already passes them through.\n\nSide effect: this closes the bulk of p2-20 for production. The tsx\npnpm-resolve bug remains in dev, but nobody hits the stall path\nbecause in dev the plugin is the fallback (both paths go through\ntsx, both fail identically — hm, actually, server-mode cold reads\nRedis first; if Redis is warm, no tsx worker is spawned). p2-20\nstill needs its own fix for cold `pnpm dev` runs."
},
{
"id": "p2-21",
"id": "p2-22",
"title": "Sprite generation pipeline — runnable end-to-end",
"priority": "p2",
"status": "missing",
"scope": "game1",
"owner": "asset-sprite",
"updated_at": "2026-04-17",
"summary": "Gate-one objective for every other `asset-sprite` child (`p2-22` … `p2-26`). Before any sprite can legitimately land in `public/games/age-of-dwarves/assets/sprites/`, the `tools/sprite-generation/` pipeline has to run cleanly end-to-end: scan game data → generate variants via the configured model → auto-rank via Sonnet vision → surface in the Theater GUI for human approval → chroma-key + resize + install with LICENSES.md row written.\n\nSlate is clean (user deleted 7 pre-existing sprites on 2026-04-17 for quality-bar failure; the prompt library and ranker had drifted). This objective closes out the \"pipeline works\" half of the split; actual sprite shipping lives in the downstream children."
"summary": "Gate-one objective for every other `asset-sprite` child (`p2-23` … `p2-27`). Before any sprite can legitimately land in `public/games/age-of-dwarves/assets/sprites/`, the `tools/sprite-generation/` pipeline has to run cleanly end-to-end: scan game data → generate variants via the configured model → auto-rank via Sonnet vision → surface in the Theater GUI for human approval → chroma-key + resize + install with LICENSES.md row written.\n\nSlate is clean (user deleted 7 pre-existing sprites on 2026-04-17 for quality-bar failure; the prompt library and ranker had drifted). This objective closes out the \"pipeline works\" half of the split; actual sprite shipping lives in the downstream children."
},
{
"id": "p2-22",
"id": "p2-23",
"title": "Unit sprites — Dwarf-racial roster (m/f variants)",
"priority": "p2",
"status": "missing",
"scope": "game1",
"owner": "asset-sprite",
"updated_at": "2026-04-17",
"summary": "Every Dwarf-controlled combat + support unit declared in `public/games/age-of-dwarves/data/units/*.json` (the non-wild entries — those with a `gender: { male, female }` block) needs a matching pair of PNG sprites in `public/games/age-of-dwarves/assets/sprites/units/` so `UnitRenderer` can overlay the sprite on top of the procedural baseline.\n\nThe current roster of gender-bearing units in `data/units/` (2026-04-17 snapshot): `archer`, `berserker`, `cavalry`, `pikeman`, `runesmith`, `spearmen`, `warrior`, `worker` — 8 unit types × 2 genders = 16 PNG files. Race id is `dwarf` (singular — confirmed in `public/games/age-of-dwarves/data/races.json`); the `Player.race_id` fallback in `unit_renderer.gd:_resolve_race_id()` resolves to that value at runtime.\n\nSlate is clean — the 7 previously-authored sprites were deleted 2026-04-17 for quality-bar failure. Do not restore; regenerate via `p2-21` once the pipeline is green."
"summary": "Every Dwarf-controlled combat + support unit declared in `public/games/age-of-dwarves/data/units/*.json` (the non-wild entries — those with a `gender: { male, female }` block) needs a matching pair of PNG sprites in `public/games/age-of-dwarves/assets/sprites/units/` so `UnitRenderer` can overlay the sprite on top of the procedural baseline.\n\nThe current roster of gender-bearing units in `data/units/` (2026-04-17 snapshot): `archer`, `berserker`, `cavalry`, `pikeman`, `runesmith`, `spearmen`, `warrior`, `worker` — 8 unit types × 2 genders = 16 PNG files. Race id is `dwarf` (singular — confirmed in `public/games/age-of-dwarves/data/races.json`); the `Player.race_id` fallback in `unit_renderer.gd:_resolve_race_id()` resolves to that value at runtime.\n\nSlate is clean — the 7 previously-authored sprites were deleted 2026-04-17 for quality-bar failure. Do not restore; regenerate via `p2-22` once the pipeline is green."
},
{
"id": "p2-23",
"id": "p2-24",
"title": "Unit sprites — wild creatures & fauna (generic, no race/sex)",
"priority": "p2",
"status": "missing",
@ -660,7 +650,7 @@
"summary": "Wild creatures in `public/games/age-of-dwarves/data/units/*.json` (entries with `unit_type: \"wild\"`) do not participate in the race×sex permutation. `UnitRenderer` falls back to `SPRITE_LOOKUP_GENERIC_FORMAT = \"sprites/units/%s.png\"` (`unit_renderer.gd:53`) for them — a single sprite per creature id.\n\nThe current wild roster (2026-04-17 snapshot): `ancient_hydra`, `basilisk_wild`, `dire_bear`, `dire_wolf`, `drake_wild`, `elder_wyrm`, `feral_spider`, `fire_imp`, `frostfang_alpha`, `garden_snail`, `lava_elemental`, `shambling_dead`, `stone_sentinel`, `wild_wyvern`, `wolf_pack` — 15 creatures, 15 PNG files.\n\nEach is a recognisable silhouette at the renderer's 56×56 hex render size (verified via `sprite_proof.tscn` scaling behaviour established in `p0-23`)."
},
{
"id": "p2-24",
"id": "p2-25",
"title": "Building sprites — base game coverage (non-wonder)",
"priority": "p2",
"status": "missing",
@ -670,17 +660,17 @@
"summary": "Every standard building declared in `public/games/age-of-dwarves/data/buildings/*.json` (excluding `manifest.json`, `stub.json`, and `mundane_wonders.json`) needs a sprite at `public/games/age-of-dwarves/assets/sprites/buildings/<building_id>.png`. City-screen UI and, where the world-map eventually surfaces building silhouettes, the renderer both consume these.\n\nCurrent base roster (2026-04-17 snapshot, 10 buildings): `ale_hall`, `barracks`, `bathhouse`, `colosseum`, `forge`, `library`, `marketplace`, `monument`, `temple`, `walls`."
},
{
"id": "p2-25",
"id": "p2-26",
"title": "Mundane-wonder sprites — 24 distinct, higher-fidelity art",
"priority": "p2",
"status": "missing",
"scope": "game1",
"owner": "asset-sprite",
"updated_at": "2026-04-17",
"summary": "`public/games/age-of-dwarves/data/buildings/mundane_wonders.json` declares 24 Civ5-style world wonders for Game 1 (no magic schools — these are mundane flavour wonders). Each needs a **distinct, higher-fidelity** sprite — a generic \"fancy building\" look drags the whole wonder system's perceived prestige. Split from `p2-24` so the quality bar can be tracked independently.\n\nCurrent wonder roster (2026-04-17 snapshot): `ancestral_forge`, `mead_hall`, `first_mineshaft`, `clan_moot_stone`, `iron_bulwark`, `hall_of_ancestors`, `the_deep_road`, `bardic_circle`, `archive_of_runes`, `royal_runestone`, `grand_observatory`, `covenant_stone`, `the_great_forge`, `iron_crown`, `undermount_vault`, `hall_of_echoes`, `world_pillar`, `well_of_ages`, `the_undying_flame`, `voice_of_ages`, `silent_cartograph`, `shrine_of_names`, `the_cold_anvil`, `hearthless_hall`."
"summary": "`public/games/age-of-dwarves/data/buildings/mundane_wonders.json` declares 24 Civ5-style world wonders for Game 1 (no magic schools — these are mundane flavour wonders). Each needs a **distinct, higher-fidelity** sprite — a generic \"fancy building\" look drags the whole wonder system's perceived prestige. Split from `p2-25` so the quality bar can be tracked independently.\n\nCurrent wonder roster (2026-04-17 snapshot): `ancestral_forge`, `mead_hall`, `first_mineshaft`, `clan_moot_stone`, `iron_bulwark`, `hall_of_ancestors`, `the_deep_road`, `bardic_circle`, `archive_of_runes`, `royal_runestone`, `grand_observatory`, `covenant_stone`, `the_great_forge`, `iron_crown`, `undermount_vault`, `hall_of_echoes`, `world_pillar`, `well_of_ages`, `the_undying_flame`, `voice_of_ages`, `silent_cartograph`, `shrine_of_names`, `the_cold_anvil`, `hearthless_hall`."
},
{
"id": "p2-26",
"id": "p2-27",
"title": "City population-tier sprites — city_q1 through city_q5",
"priority": "p2",
"status": "missing",
@ -689,6 +679,16 @@
"updated_at": "2026-04-17",
"summary": "`CityRenderer` looks up a city's sprite via `SPRITE_LOOKUP_CITY_FORMAT = \"sprites/cities/city_q%d.png\"` (`city_renderer.gd:29`) with the quality bucket computed as `clampi(city.population / CITY_QUALITY_BUCKET + 1, 1, CITY_QUALITY_MAX)` — five buckets, five PNGs. Tiny scope relative to the other children, but the key format and renderer path are distinct enough to warrant its own objective — can flip `done` early."
},
{
"id": "p2-28",
"title": "Sprite provenance ledger — LICENSES.md per-file attribution",
"priority": "p2",
"status": "missing",
"scope": "game1",
"owner": "asset-sprite",
"updated_at": "2026-04-17",
"summary": "Every sprite PNG that ships in `public/games/age-of-dwarves/assets/sprites/` must have a corresponding row in `public/games/age-of-dwarves/assets/sprites/LICENSES.md` recording source, license, author, URL, and SHA256. This is a cross-cutting compliance objective that runs continuously alongside the delivery children (`p2-23` … `p2-27`) — the ledger is complete exactly when every on-disk sprite has a matching row and every row points at an on-disk file.\n\nCommercial-use compatibility is non-negotiable. AI-generated output must come from a model on the approved list (`juggernaut-xl-v9`, `epicrealism-xl`, `illustrious-xl-v2`, or current equivalent per CLAUDE.md). Commissioned art must have assigned commercial rights in writing."
},
{
"id": "g2-01",
"title": "Ley lines — Game 2 (Age of Kzzykt)",

View file

@ -3,8 +3,10 @@ import episodes from '@resources/episodes.json'
// Episode accent colors — app navigation branding (UI-only, stays in TypeScript)
const EP1_COLOR = '#c07040' // dwarf copper
const EP2_COLOR = '#4a7c59' // kzzykt green
const EP3_COLOR = '#7c5cbf' // elven violet
const [ep1] = episodes
const [ep1, ep2, ep3] = episodes
export const NAV: NavGroup[] = [
// ─── Common (cross-episode) ───────────────────────────────────────────────
@ -113,4 +115,48 @@ export const NAV: NavGroup[] = [
{ to: '/playing/lenses', icon: '🔍', label: 'Lenses' },
],
},
// ─── Episode 2: Age of Kzzykt — The Hive ────────────────────────────────
{
episodeHeader: { number: 2, name: ep2.display_name, color: EP2_COLOR, link: ep2.route! },
title: 'Kzzykt & The Hive',
items: [
{ to: ep2.route!, icon: '📋', label: ep2.name },
{ to: '/worlds/the-hive', icon: '🐛', label: 'The Hive' },
],
},
{
title: 'Ley Lines & Magic',
items: [
{ to: '/magic', icon: '🌿', label: 'Magic Overview' },
{ to: '/magic/ley-lines', icon: '〰', label: 'Ley Lines' },
],
},
// ─── Episode 3: Age of Elves — Silvandel ─────────────────────────────────
{
episodeHeader: { number: 3, name: ep3.display_name, color: EP3_COLOR, link: ep3.route! },
title: 'Elves & Silvandel',
items: [
{ to: ep3.route!, icon: '📋', label: ep3.name },
{ to: '/worlds/silvandel', icon: '🌲', label: 'Silvandel' },
],
},
{
title: 'Schools of Magic',
items: [
{ to: '/magic/schools', icon: '📚', label: 'All Schools' },
{ to: '/magic/schools/life', icon: '💚', label: 'Life' },
{ to: '/magic/schools/death', icon: '💀', label: 'Death' },
{ to: '/magic/schools/chaos', icon: '🔥', label: 'Chaos' },
{ to: '/magic/schools/aether', icon: '✨', label: 'Aether' },
],
},
{
title: 'Archons & Ascension',
items: [
{ to: '/magic/archons', icon: '👁', label: 'Archons' },
{ to: '/magic/ascension', icon: '🌟', label: 'Arcane Ascension' },
],
},
]