diff --git a/.project/objectives/DASHBOARD_CATEGORIES.md b/.project/objectives/DASHBOARD_CATEGORIES.md
index 714172c9..ad016b45 100644
--- a/.project/objectives/DASHBOARD_CATEGORIES.md
+++ b/.project/objectives/DASHBOARD_CATEGORIES.md
@@ -403,11 +403,6 @@
| [p2-20](p2-20-guide-sim-cache-pnpm-resolve.md) | β
done | P2 | Fix simCachePlugin pre-warm worker β tsx can't resolve @magic-civ/physics-rs through pnpm symlink | [tourguide](../team-leads/tourguide.md) | π’ |
| [p2-21](p2-21-guide-simcache-static-bake.md) | β
done | P2 | Bake pre-computed sim-cache frames into the static build | [tourguide](../team-leads/tourguide.md) | π’ |
| [p2-22](p2-22-sprite-generation-pipeline.md) | β
done | P1 | Sprite generation pipeline β runnable end-to-end | [asset-sprite](../team-leads/asset-sprite.md) | π’ |
-| [p2-23](p2-23-unit-sprites-dwarf-roster.md) | π‘ partial | P1 | Unit sprites β Dwarf-racial roster (m/f variants) | [asset-sprite](../team-leads/asset-sprite.md) | π’ |
-| [p2-24](p2-24-unit-sprites-wild-creatures.md) | π‘ partial | P1 | Unit sprites β wild creatures & fauna (generic, no race/sex) | [asset-sprite](../team-leads/asset-sprite.md) | π’ |
-| [p2-25](p2-25-building-sprites-base-coverage.md) | π‘ partial | P1 | Building sprites β base game coverage (non-wonder) | [asset-sprite](../team-leads/asset-sprite.md) | π’ |
-| [p2-26](p2-26-mundane-wonder-sprites.md) | π‘ partial | P1 | Mundane-wonder sprites β 24 distinct, higher-fidelity art | [asset-sprite](../team-leads/asset-sprite.md) | π’ |
-| [p2-27](p2-27-city-population-tier-sprites.md) | π‘ partial | P1 | City population-tier sprites β city_q1 through city_q5 | [asset-sprite](../team-leads/asset-sprite.md) | π’ |
| [p2-28](p2-28-sprite-provenance-ledger.md) | β
done | P1 | Sprite provenance ledger β LICENSES.md per-file attribution | [asset-sprite](../team-leads/asset-sprite.md) | π’ |
| [p2-29](p2-29-guide-welcome-homepage-theme-alignment.md) | β
done | P2 | Welcome modal + HomePage lore + guide theme align to the player's chosen race/gender | [tourguide](../team-leads/tourguide.md) | π’ |
| [p2-30](p2-30-guide-shared-primitives.md) | β
done | P2 | Consolidate duplicate page styled-components into shared PagePrimitives | [tourguide](../team-leads/tourguide.md) | π’ |
@@ -501,7 +496,6 @@
| [p2-80](p2-80-mc-worldsim-integration.md) | β
done | P1 | mc-worldsim orchestration crate β drive the existing worldsim engines in the playable turn | β | π’ |
| [p2-81](p2-81.md) | β
done | P2 | Improvement effects authored-but-unwired β move moisture/wind/erosion/movement to Rust | β | π’ |
| [p2-82](p2-82-climate-input-save-fidelity.md) | β
done | P2 | Climate-input save-fidelity β persist (or re-derive) worldgen-static grid inputs across save/load | β | π’ |
-| [p2-85](p2-85-poi-sprites-and-tooltips.md) | π‘ partial | P2 | POI sprites + hover tooltips β lairs (and resources) legible on the map | [asset-sprite](../team-leads/asset-sprite.md) | π’ |
| [p2-86](p2-86-mcp-rendered-driver.md) | β
done | P2 | Claude-player MCP β rendered driver mode (drive UI + capture screenshots) | [simulator-infra](../team-leads/simulator-infra.md) | π’ |
| [p2-87-single-color-system-sot](p2-87-single-color-system-sot.md) | β
done | P2 | Single game-wide colour system β one source of truth, layered tokens, every consumer derives from it | [wireguard](../team-leads/wireguard.md) | π’ |
| [p3-01](p3-01-courier-diplomacy.md) | β
done | P3 | Courier-gated diplomacy β open borders + shared maps via tech-tiered courier units | [envoy](../team-leads/envoy.md) | π’ |
diff --git a/.project/objectives/README.md b/.project/objectives/README.md
index f799f4b8..832e1072 100644
--- a/.project/objectives/README.md
+++ b/.project/objectives/README.md
@@ -15,10 +15,10 @@
| Priority | π΅ | π‘ | π΄ | β | β« | β
| Total |
|---|---|---|---|---|---|---|---|
| **P0** | 0 | 0 | 0 | 0 | 0 | 44 | 44 |
-| **P1** | 0 | 5 | 0 | 0 | 1 | 88 | 94 |
-| **P2** | 0 | 1 | 0 | 0 | 1 | 132 | 134 |
+| **P1** | 0 | 0 | 0 | 0 | 1 | 88 | 89 |
+| **P2** | 0 | 0 | 0 | 0 | 1 | 132 | 133 |
| **P3 (oos)** | 0 | 0 | 0 | 0 | 29 | 26 | 55 |
-| **total** | **0** | **6** | **0** | **0** | **31** | **290** | **327** |
+| **total** | **0** | **0** | **0** | **0** | **31** | **290** | **321** |
@@ -26,26 +26,10 @@
| Team Lead | Remaining |
|---|---|
-| [asset-sprite](../team-leads/asset-sprite.md) | 6 |
+| β | 0 |
|
-## P1 β Ship-readiness
-
-| ID | Status | Title | Tags | Owner | Updated | Blocked |
-|---|---|---|---|---|---|---|
-| [p2-23](p2-23-unit-sprites-dwarf-roster.md) | π‘ partial | Unit sprites β Dwarf-racial roster (m/f variants) | β | [asset-sprite](../team-leads/asset-sprite.md) | 2026-06-04 | π’ unblocked |
-| [p2-24](p2-24-unit-sprites-wild-creatures.md) | π‘ partial | Unit sprites β wild creatures & fauna (generic, no race/sex) | β | [asset-sprite](../team-leads/asset-sprite.md) | 2026-06-04 | π’ unblocked |
-| [p2-25](p2-25-building-sprites-base-coverage.md) | π‘ partial | Building sprites β base game coverage (non-wonder) | β | [asset-sprite](../team-leads/asset-sprite.md) | 2026-06-04 | π’ unblocked |
-| [p2-26](p2-26-mundane-wonder-sprites.md) | π‘ partial | Mundane-wonder sprites β 24 distinct, higher-fidelity art | β | [asset-sprite](../team-leads/asset-sprite.md) | 2026-06-04 | π’ unblocked |
-| [p2-27](p2-27-city-population-tier-sprites.md) | π‘ partial | City population-tier sprites β city_q1 through city_q5 | β | [asset-sprite](../team-leads/asset-sprite.md) | 2026-06-04 | π’ unblocked |
-
-## P2 β Polish
-
-| ID | Status | Title | Tags | Owner | Updated | Blocked |
-|---|---|---|---|---|---|---|
-| [p2-85](p2-85-poi-sprites-and-tooltips.md) | π‘ partial | POI sprites + hover tooltips β lairs (and resources) legible on the map | β | [asset-sprite](../team-leads/asset-sprite.md) | 2026-06-23 | π’ unblocked |
-
## Out of Scope
> These objectives are explicitly deferred. They are tracked for visibility but not blocking the current release.
@@ -92,6 +76,12 @@
|---|---|---|---|---|---|---|
| [p0-20d](p0-20d-gpu-walltime-real-host.md) | β»οΈ superseded | GPU MCTS wall-time gate β measure on real-discrete-GPU test host | β | [warcouncil](../team-leads/warcouncil.md) | 2026-05-05 | π’ unblocked |
| [p1-30b](p1-30b-parallel-mcts-rollouts.md) | β»οΈ superseded | Parallel MCTS rollouts for huge-map decisive games (closes p1-22's huge-map sub-gate) | perf, mcts, tactical-ai | [warcouncil](../team-leads/warcouncil.md) | 2026-05-05 | π’ unblocked |
+| [p2-23](p2-23-unit-sprites-dwarf-roster.md) | β»οΈ superseded | Unit sprites β Dwarf-racial roster (m/f variants) | β | [asset-sprite](../team-leads/asset-sprite.md) | 2026-06-23 | π’ unblocked |
+| [p2-24](p2-24-unit-sprites-wild-creatures.md) | β»οΈ superseded | Unit sprites β wild creatures & fauna (generic, no race/sex) | β | [asset-sprite](../team-leads/asset-sprite.md) | 2026-06-23 | π’ unblocked |
+| [p2-25](p2-25-building-sprites-base-coverage.md) | β»οΈ superseded | Building sprites β base game coverage (non-wonder) | β | [asset-sprite](../team-leads/asset-sprite.md) | 2026-06-23 | π’ unblocked |
+| [p2-26](p2-26-mundane-wonder-sprites.md) | β»οΈ superseded | Mundane-wonder sprites β 24 distinct, higher-fidelity art | β | [asset-sprite](../team-leads/asset-sprite.md) | 2026-06-23 | π’ unblocked |
+| [p2-27](p2-27-city-population-tier-sprites.md) | β»οΈ superseded | City population-tier sprites β city_q1 through city_q5 | β | [asset-sprite](../team-leads/asset-sprite.md) | 2026-06-23 | π’ unblocked |
| [p1-27d](p1-27d-additive-value-estimate.md) | β»οΈ superseded | Add `value_estimate_abstract` GdMcTreeController method β non-lossy MCTS service caller | β | [warcouncil](../team-leads/warcouncil.md) | 2026-04-25 | π’ unblocked |
| [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 | π’ unblocked |
+| [p2-85](p2-85-poi-sprites-and-tooltips.md) | β»οΈ superseded | POI sprites + hover tooltips β lairs (and resources) legible on the map | β | [asset-sprite](../team-leads/asset-sprite.md) | 2026-06-23 | π’ unblocked |
diff --git a/.project/objectives/objectives.json b/.project/objectives/objectives.json
index 5fb21620..602eb81f 100644
--- a/.project/objectives/objectives.json
+++ b/.project/objectives/objectives.json
@@ -1,13 +1,13 @@
{
- "generated_at": "2026-06-23T13:27:30Z",
+ "generated_at": "2026-06-23T17:17:04Z",
"totals": {
"done": 290,
"in_progress": 0,
- "partial": 6,
+ "partial": 0,
"stub": 0,
"missing": 0,
"oos": 31,
- "total": 327
+ "total": 321
},
"objectives": [
{
@@ -1426,61 +1426,6 @@
"blocked_by": [],
"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-23",
- "title": "Unit sprites β Dwarf-racial roster (m/f variants)",
- "priority": "p1",
- "status": "partial",
- "scope": "game1",
- "owner": "asset-sprite",
- "updated_at": "2026-06-04",
- "blocked_by": [],
- "summary": "Every Dwarf-controlled combat + support unit Game 1 subscribes to (`manifest.json` `subscribes.units`, resolved against `public/resources/units/.json` β the non-wild entries, those with a `gender: { male, female }` block) needs matching PNG sprites in `public/games/age-of-dwarves/assets/sprites/units/` so `UnitRenderer` can overlay the sprite on top of the procedural baseline. (The objective originally cited `data/units/*.json`; unit data has since moved to the shared `resources/` library with a per-game manifest subscription.)\n\nThe 2026-04-17 snapshot listed 8 gender-bearing units (`archer`, `berserker`, `cavalry`, `pikeman`, `runesmith`, `spearmen`, `warrior`, `worker`); the live roster has grown to 103 gendered dwarf units (+15 wild β `p2-24`, +42 plain support/civilian) = 160 subscribed. Race id is `dwarf` (singular β `public/resources/races/dwarves.json` `id: dwarf`); `Player.race_id` resolves to that at runtime (`unit_renderer.gd:_resolve_race_id()`), `gender_preset` β {male, female}.\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.\n\n**Stand-in coverage (2026-06-04).** Pending the bespoke paid-art pass, every renderer\nunit slot is filled with an OSS stand-in (game-icons.net CC-BY-3.0) via\n`tools/standin-sprites/build_standins.py`. Coverage is data-driven off the live\nGame-1 subscription (`public/games/age-of-dwarves/manifest.json` β `subscribes.units`,\n160 units β far larger than the 2026-04-17 8-unit snapshot the roster has since\ngrown past), keyed to the renderer-constructed paths the engine actually loads\n(`sprites/units/.png` + `_dwarf_male.png` / `_dwarf_female.png` for the\n103 gendered dwarf units; `unit.gd` sets `type_id = resource id`, `player.gd`\n`gender_preset` β {male, female}, race `dwarf`). 366 PNGs on disk, all RGBA/56px,\none `LICENSES.md` row each. These are PLACEHOLDER silhouettes, NOT the bespoke\nDwarf-racial m/f art the objective ultimately requires β the final-art bullets\nbelow stay β and status is **partial**."
- },
- {
- "id": "p2-24",
- "title": "Unit sprites β wild creatures & fauna (generic, no race/sex)",
- "priority": "p1",
- "status": "partial",
- "scope": "game1",
- "owner": "asset-sprite",
- "updated_at": "2026-06-04",
- "blocked_by": [],
- "summary": "Wild creatures (unit resources with `faction: \"wild\"` / `keywords: [\"wild\"]`, in `public/resources/units/`; the objective originally said `unit_type: \"wild\"` in `data/units/` β both the field and the path have since moved) do not participate in the raceΓsex permutation. `UnitRenderer` falls back to `SPRITE_LOOKUP_GENERIC_FORMAT = \"sprites/units/%s.png\"` (`unit_renderer.gd:60`) 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`).\n\n**Stand-in coverage (2026-06-04).** All 15 wild units (`faction: \"wild\"` in the live\n`subscribes.units` set β the same roster as the 2026-04-17 snapshot) are filled with\nOSS stand-ins (game-icons.net CC-BY-3.0) at `sprites/units/.png`, single sprite\nper id, no race/sex suffix, via `tools/standin-sprites/build_standins.py`. (The\nseparate 61-species ecology `fauna` manifest is NOT in scope here β those are\nsim-only species, not rendered map units.) PLACEHOLDER art; final-art bullets below\nstay β, status **partial**."
- },
- {
- "id": "p2-25",
- "title": "Building sprites β base game coverage (non-wonder)",
- "priority": "p1",
- "status": "partial",
- "scope": "game1",
- "owner": "asset-sprite",
- "updated_at": "2026-06-04",
- "blocked_by": [],
- "summary": "Every standard (non-wonder) building Game 1 subscribes to β `manifest.json` `subscribes.buildings`, resolved against `public/resources/buildings/.json`, taking the entries WITHOUT a `wonder_type` field (wonders β `p2-26`) β needs a sprite at `public/games/age-of-dwarves/assets/sprites/buildings/.png`. (The objective originally said `data/buildings/*.json`; building data has since moved to the shared `resources/` library with a per-game manifest subscription.) `city_renderer.gd:223` loads this flat, id-keyed path; the city-screen UI consumes the same files.\n\nCurrent base roster (2026-04-17 snapshot, 10 buildings): `ale_hall`, `barracks`, `bathhouse`, `colosseum`, `forge`, `library`, `marketplace`, `monument`, `temple`, `walls`.\n\n**Stand-in coverage (2026-06-04).** The live building roster has grown well past the\n10-building snapshot: `manifest.json` `subscribes.buildings` lists 166 entries, of\nwhich 103 are non-wonder base buildings (the other 62 are wonders β `p2-26`). All 103\nare filled with OSS stand-ins (game-icons.net CC-BY-3.0) on a stone plate at 64px,\nwritten to `sprites/buildings/.png` β the flat, id-keyed path the renderer\nactually loads (`city_renderer.gd:223` `\"sprites/buildings/%s.png\" % bid`; no\n`wonders/` subdir, no resource-`sprite`-field read). Built via\n`tools/standin-sprites/build_standins.py`. PLACEHOLDER art; final-art bullets below\nstay β, status **partial**. (One subscribed id, `market`, has no resource file β a\ndata gap logged in `STANDINS.md`, not a sprite gap.)"
- },
- {
- "id": "p2-26",
- "title": "Mundane-wonder sprites β 24 distinct, higher-fidelity art",
- "priority": "p1",
- "status": "partial",
- "scope": "game1",
- "owner": "asset-sprite",
- "updated_at": "2026-06-04",
- "blocked_by": [],
- "summary": "Game 1's world wonders (Civ5-style, no magic schools β mundane flavour wonders) are the subscribed buildings carrying a `wonder_type` field in `public/resources/buildings/.json`. (The objective originally cited `data/buildings/mundane_wonders.json` with 24 wonders; the data has since moved to the shared `resources/` library and the live count is 62.) 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`.\n\n**Stand-in coverage (2026-06-04).** The wonder roster has grown past the 24-wonder\nsnapshot: the live subscription now has 62 wonders (buildings with a `wonder_type`\nfield). All 62 are filled with OSS stand-ins (game-icons.net CC-BY-3.0) on a **bronze\nplate at 96px** (higher fidelity than the 64px base-building stone plate, to read as\n\"monumental\"), written to the flat id-keyed path `sprites/buildings/.png` the\nrenderer loads (`city_renderer.gd:223`; `standin_sprite_proof.gd` MUNDANE WONDERS rows\nconfirm the flat path). Built via `tools/standin-sprites/build_standins.py`. These are\ngeneric silhouettes β they do NOT meet this objective's distinct-per-wonder,\ntier-appropriate-grandeur, ranker β₯ 4.2 bar. Final-art bullets below stay β, status\n**partial**."
- },
- {
- "id": "p2-27",
- "title": "City population-tier sprites β city_q1 through city_q5",
- "priority": "p1",
- "status": "partial",
- "scope": "game1",
- "owner": "asset-sprite",
- "updated_at": "2026-06-04",
- "blocked_by": [],
- "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.\n\n**Stand-in coverage (2026-06-04).** All five tiers filled with OSS stand-ins\n(game-icons.net CC-BY-3.0, `hut β huts-village β village β hill-fort β castle` β a\nclear size/density progression) at `sprites/cities/city_q.png`, the exact path\n`city_renderer.gd:29` `SPRITE_LOOKUP_CITY_FORMAT` loads. Built via\n`tools/standin-sprites/build_standins.py`. PLACEHOLDER art; final-art bullets (native\n256/512px, ranker, reviewed proof screenshot) stay β, status **partial**."
- },
{
"id": "p2-28",
"title": "Sprite provenance ledger β LICENSES.md per-file attribution",
@@ -2967,17 +2912,6 @@
"blocked_by": [],
"summary": "**A development-time profiling layer that attributes compute cost (CPU / RAM / GPU) to the feature that incurred it, over game-time, tagged by what triggered it β so optimization effort is data-driven, not guessed. It MUST compile/gate out to zero overhead in the shipped game.**"
},
- {
- "id": "p2-85",
- "title": "POI sprites + hover tooltips β lairs (and resources) legible on the map",
- "priority": "p2",
- "status": "partial",
- "scope": "game1",
- "owner": "asset-sprite",
- "updated_at": "2026-06-23",
- "blocked_by": [],
- "summary": ""
- },
{
"id": "p2-86",
"title": "Claude-player MCP β rendered driver mode (drive UI + capture screenshots)",
@@ -3879,10 +3813,5 @@
]
}
],
- "remaining_by_lead": [
- {
- "owner": "asset-sprite",
- "remaining": 6
- }
- ]
+ "remaining_by_lead": []
}