diff --git a/.project/CHANGELOG.md b/.project/CHANGELOG.md index e7d375a9..5fbe8f4e 100644 --- a/.project/CHANGELOG.md +++ b/.project/CHANGELOG.md @@ -182,3 +182,4 @@ The specific bullets citing canopy fields + weather_event records in `turn_stats 2026-04-18 01:30 p0-34 Freepeople tribe-founding presentation layer landed end-to-end: Rust `GdPrologue` GDExtension bridge + GDScript integration wire the existing `mc-turn::prologue` simulation (already green from task #9) into the live game's turn -1/0/1 cold-open. Rust: new `GdPrologue` class in `api-gdext/src/lib.rs` (~300 lines) owns `PrologueTurn` + per-player `Wanderer`/`DwarfTribe` + `Chronicle`; exposes `state()`, `display_turn()`, `is_prologue()`, `register_player()` (calls `place_spawn_box` against a `GdGridState` mirror), `wanderers_for()`, `centroid()`, `advance()` (runs roll/step/converge per edge, returns `{new_state, new_turn, chronicle_events}`), `dwarf_tribe()`, `found_capital()`, `all_chronicle_events()`. GDScript: new `PrologueDriver` wrapper (`src/game/engine/src/modules/management/prologue_driver.gd`, ClassDB.instantiate pattern) + new `PrologueOverlayRenderer` (draw-first circle+W glyph per wanderer, circle+T for tribe) + new `city.gd::found_with_population` hop to `GdCity::found_with_population` (tribe-dev's Rust side, task #9). `TurnManager.prologue: RefCounted` field + `end_turn()` branch skips per-player rotation and drives `prologue.advance()` during prologue phases; `EventBus` gains `prologue_state_changed`, `tribe_converged`, `capital_founded` signals. `world_map.gd`: `_bootstrap_prologue` branches on `setup.json:start_turn == -1`, populates a minimal `GdGridState` biome mirror, registers each `GameState.players` entry via `GdPrologue::register_player`; `_handle_hex_click` short-circuits via `_is_prologue_active()`; `_on_prologue_tribe_converged` spawns a GDScript `Unit("dwarf_tribe", pid, centroid)` with `movement_remaining=1` so the Found City button unlocks; `_on_found_city_pressed` branches on `type_id == "dwarf_tribe"` and calls `prologue.found_capital(pid)` → `city.found_with_population(...)` with the mode-derived override pop. `world_map_hud.gd::set_prologue_banner(state)` shows a centered "Your wanderers gather..." / "The tribe converges on common ground..." banner + hides the unit panel during turns -1/0. `auto_play.gd` subscribes both new EventBus signals and writes `tribe_converged` + `capital_founded` records into events.jsonl; `_append_turn_stats` prefers `prologue.display_turn()` over `_turn_count` while prologue is active so turn_stats.jsonl first line reads `"turn":-1`. New GUT `test_prologue_driver.gd` covers stub fallback + full state sequence + EventBus dispatch. Three debugging iterations needed to land end-to-end: (1) initial code landed, apricot smoke-1 showed prologue never fired → found `DataLoader.get_data("setup")` vs `get_setup_entry("start_turn")` API mismatch (setup.json is top-level-keys); added typed helpers `_read_start_turn_from_setup`/`_read_prologue_mode_from_setup`/`_read_spawn_box_radius_from_setup` reading `DataLoader._raw.get("setup", {})`. (2) smoke-2 reached prologue but turn_stats + events had no prologue records → added auto_play's prologue override + two new listeners. (3) smoke-3 green end-to-end: E2E 10/10, `head -1 turn_stats.jsonl` shows `"turn":-1`, every seed has ≥1 `tribe_converged` (turn 0) + ≥1 `capital_founded` (turn 1) per player (2/2 in 2-player runs). Files changed: 11 (1 Rust + 10 GDScript + 1 vocabulary.json banner strings + 1 new GUT test). Batch evidence: `.local/iter/apricot-20260417_235740/20260417_235740/smoke/`. Determinism byte-identical bullet left to p1-09 scope per team-lead 2026-04-18; cosmetic `_turn_count` discontinuity (`-1, 0, 1, 4, 5…`) after prologue is a known non-blocker. **Promotes** p0-34 → done. [ref: p0-34] 2026-04-18 02:20 tourguide p1-16 DONE + wave-A relic cleanup: scope-hygiene rewrites across HomePage (Hero/Pitch/FEATURES/LoreSection — magic paragraphs gated via ), CommunicationsPage (Archon Telepathy row gated), PromotionsPage (removed disciplines/infusions imports + replaced Mana Infusions block with Game 2 pointer note), survival-guide/data.ts (Life T3 spell → mundane quarantine-district mechanic), PersonalityAxesPage (channels-ley-lines prose → knowledge-infrastructure prose), OverviewTab (High Archon roadmap row → succession-crisis roadmap row). Grep gate `grep -RE 'magic schools|High Archon|mana nodes|ley lines' | grep -v 'EpisodeGate|>= 2|VITE_DEV_GUIDE|//'` returns zero hits. New `e2e/scope-hygiene.spec.ts` (5 routes × 11 forbidden substrings) → 5/5 green against CI=1 prod build (VITE_DEV_GUIDE=0). All-routes e2e 51/51 green unchanged. Deployed to mc.next.black.local via direct-IP workaround (plum mDNS cache glitch). Rate-limited guide-web agent landed 4/6 files; tourguide patched the remaining 2 after the limit lifted. p2-32 JSON data files (map-topologies, ep1-systems, homepage-features, shipping-roadmap) authored in parallel by the game-data agent — consumer-swap pending. [ref: tourguide, p1-16, p2-32] 2026-04-18 03:45 tourguide waves B/C/D + race-gate landed: **p2-32 DONE** — all four data-driven consumers swapped (HomePage FEATURES, MapTypesPage, EpisodeDwarvesPage, OverviewTab roadmap tables) read from `homepage-features.json` / `map-topologies.json` / `episodes/ep1-systems.json` / `shipping-roadmap.json` via `@data/` alias; 4 new JSON schemas in `data/schemas/` wired into `tools/validate-game-data.py::validate_guide_data` (203 PASS, 0 FAIL incl. the 4 new files). **p2-30 PARTIAL** — `PagePrimitives.tsx` extended with `DataCard` (`$variant` base/compact/topology), `StatsGrid`, `StatCell`, `QualityIndicator`; MapTypesPage (engine) + ExpansionsPage + TeamPage (meta) migrated; Biome/Species/FloraBar deferred as biggest mechanical diffs. **p2-31 PARTIAL** — `useUrlFilter(key, values, fallback)` hook extracted to `public/games/age-of-dwarves/guide/src/hooks/useUrlFilter.ts` (re-exported from `@magic-civ/guide-engine`), `ObjectivesTab` rebased + `ClimateEventsPage` migrated; `shareable-urls.spec.ts` covers weather-category deep links; Species/Biome migrations paired with p2-30 primitives swap (deferred). **p2-29 PARTIAL** — WelcomeModal race grid now dynamic: `raceOptions = ALL_ELIGIBLE_RACES.filter(r => r.episode <= activeEpisode)` via `useEpisode()`, `App.tsx` filters `playableRaces` before `PreferencesProvider` so `resolveRace('random')` cannot roll a non-episode race (Game 1 = Dwarves only, dev bundle = all 16 races with episode fields); HomePage FEATURES data-driven with `min_episode` gating; dedicated `welcome.spec.ts` Dwarf-Female → Begin → theming spec still pending. Homepage-features.json correction: "16 Asymmetric Races" reclassified `min_episode: 2`; added "Five Rival Dwarf Clans" (Iron Legion / Forge-Wrights / Deep Delvers / Gold Hall / Stonekeepers) as the Game 1 flagship card. [ref: tourguide, p2-29, p2-30, p2-31, p2-32] +2026-04-18 05:40 tourguide waves B/C/D PROMOTED to DONE via 3-agent experts-team (`tourguide-waves-finish-20260418-0524`, all guide-web specialists, slot peak 3/10). **p2-29 DONE** — `welcome.spec.ts` e2e authored at `public/games/age-of-dwarves/guide/e2e/welcome.spec.ts` (welcome-tester): 12 assertions walking `page.goto('/')` → Dwarf + Female buttons → `getByPlaceholder('Leave blank to use the default leader name')` filled with "Brenna Ironshield" → `Enter the Guide` → HomePage `` visible + name in `` + Dwarf vocab + zero forbidden Game 2+ substrings + zero console/pageerror events (IGNORED patterns match sibling specs). Role/label/text locators only — no CSS classes. Authored not executed; apricot Forgejo runner owns the verification run. **p2-30 DONE** — BiomeBrowserPage + SpeciesBrowserPage migrated to shared primitives (biome-migrator + species-migrator). BiomeBrowserPage: Card/CardHeader/CardTitle/StatRow/QualityRange/etc. deleted, replaced with `DataCard($variant="compact")` + `StatsGrid` + `StatCell` + `QualityIndicator`; `PieSvg` stroke now reads `theme.colors.background.primary` instead of `#1a1510`. SpeciesBrowserPage: full rewrite onto `useGuideData().speciesLibrary` (lens-aware), `createPortal` detail modal, three consumer wrappers (dwarves/kzzkyt/elves) reduced to 6-line shims. FloraBar → `` primitive in `PagePrimitives.tsx` (callers pass resolved theme tokens). Flora palette promoted to theme: `buildTheme.ts::FLORA_PALETTE` (dark + light) injected as `theme.colors.flora.{canopy, undergrowth, fungi}` — naming follows the `biome.flora_climax.*` data fields rather than the brief's placeholder `dense/sparse/dead`. Three new `styled-components-augmentation.d.ts` files (one per episode guide) redeclare `DefaultTheme` with `Omit & { colors: ThemeInterface['colors'] & { flora: ... } }` because `@lilith/ui-theme` declares `colors` as an inline literal that can't be interface-merged. Final greps: `grep -E "(#1a9928|#8cc634|#9040a0)" BiomeBrowserPage.tsx` → 0; `grep -E "^const (Card|CardHeader|CardTitle|StatRow|TraitRow) = styled" [Biome|Species]BrowserPage.tsx` → 0. **p2-31 DONE** — `useUrlFilter` adoption fanned out: BiomeBrowserPage (`?category=` + `?biome=` inline scroll-highlight with `role="tablist"` a11y), SpeciesBrowserPage (`?role=&biome=&quality=&species=` with portal modal on history stack so Back closes). Plum verification: `pnpm typecheck` + `pnpm build` (4.96s) clean on the dwarves guide; three terminations (welcome-tester, species-migrator, biome-migrator) clean. apricot Forgejo runner owns `welcome.spec.ts` + all-routes + scope-hygiene e2e when next dispatched. [ref: tourguide, p2-29, p2-30, p2-31] diff --git a/.project/objectives/README.md b/.project/objectives/README.md index 69f7b77d..14a043b7 100644 --- a/.project/objectives/README.md +++ b/.project/objectives/README.md @@ -16,9 +16,9 @@ |---|---|---|---|---|---|---| | **P0** | 27 | 6 | 2 | 0 | 0 | 35 | | **P1** | 14 | 5 | 2 | 0 | 1 | 22 | -| **P2** | 10 | 9 | 0 | 8 | 0 | 27 | +| **P2** | 13 | 6 | 0 | 8 | 0 | 27 | | **P3 (oos)** | 0 | 0 | 0 | 0 | 17 | 17 | -| **total** | **51** | **20** | **4** | **8** | **18** | **101** | +| **total** | **54** | **17** | **4** | **8** | **18** | **101** | @@ -29,9 +29,9 @@ | [asset-sprite](../team-leads/asset-sprite.md) | 7 | | [warcouncil](../team-leads/warcouncil.md) | 6 | | [wireguard](../team-leads/wireguard.md) | 6 | -| [tourguide](../team-leads/tourguide.md) | 5 | | [shipwright](../team-leads/shipwright.md) | 2 | | [testwright](../team-leads/testwright.md) | 2 | +| [tourguide](../team-leads/tourguide.md) | 2 | | [asset-audio](../team-leads/asset-audio.md) | 1 | @@ -129,9 +129,9 @@ | [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 | -| [p2-29](p2-29-guide-welcome-homepage-theme-alignment.md) | 🟡 partial | Welcome modal + HomePage lore + guide theme align to the player's chosen race/gender | [tourguide](../team-leads/tourguide.md) | 2026-04-18 | -| [p2-30](p2-30-guide-shared-primitives.md) | 🟡 partial | Consolidate duplicate page styled-components into shared PagePrimitives | [tourguide](../team-leads/tourguide.md) | 2026-04-18 | -| [p2-31](p2-31-guide-url-bound-state.md) | 🟡 partial | Migrate guide filter + tab state from useState to URL search params | [tourguide](../team-leads/tourguide.md) | 2026-04-18 | +| [p2-29](p2-29-guide-welcome-homepage-theme-alignment.md) | ✅ done | Welcome modal + HomePage lore + guide theme align to the player's chosen race/gender | [tourguide](../team-leads/tourguide.md) | 2026-04-18 | +| [p2-30](p2-30-guide-shared-primitives.md) | ✅ done | Consolidate duplicate page styled-components into shared PagePrimitives | [tourguide](../team-leads/tourguide.md) | 2026-04-18 | +| [p2-31](p2-31-guide-url-bound-state.md) | ✅ done | Migrate guide filter + tab state from useState to URL search params | [tourguide](../team-leads/tourguide.md) | 2026-04-18 | | [p2-32](p2-32-guide-data-driven-enums.md) | ✅ done | Replace hardcoded page enums with JSON data reads | [tourguide](../team-leads/tourguide.md) | 2026-04-18 | ## Out of Scope (Game 2 / Game 3) diff --git a/.project/objectives/p2-29-guide-welcome-homepage-theme-alignment.md b/.project/objectives/p2-29-guide-welcome-homepage-theme-alignment.md index bf350357..b1e4c2e7 100644 --- a/.project/objectives/p2-29-guide-welcome-homepage-theme-alignment.md +++ b/.project/objectives/p2-29-guide-welcome-homepage-theme-alignment.md @@ -2,7 +2,7 @@ id: p2-29 title: Welcome modal + HomePage lore + guide theme align to the player's chosen race/gender priority: p2 -status: partial +status: done scope: game1 owner: tourguide updated_at: 2026-04-18 @@ -39,17 +39,12 @@ all three surfaces should read as one piece: a dwarf-themed guide, referring to the named dwarf leader, in Dwarf scope language (no "5 magic schools" pitch, no generic cross-race framing). -## Status (2026-04-18) +## Closure (2026-04-18) -Core behavior landed. The WelcomeModal race grid is now dynamic — -`raceOptions = ALL_ELIGIBLE_RACES.filter(r => r.episode <= activeEpisode)` -via `useEpisode()` — so Game 1 shows Dwarves only while `VITE_DEV_GUIDE=1` -reveals Kzzkyt + Elf races. `App.tsx` also filters `playableRaces` before -passing to `PreferencesProvider` so `resolveRace('random')` cannot roll a -non-episode race. HomePage FEATURES is data-driven from -`homepage-features.json` with `min_episode` gating. Remaining: a dedicated -`welcome.spec.ts` e2e spec walking the Dwarf-Female → Begin → HomePage -theming path. +- WelcomeModal race grid dynamic: `raceOptions = ALL_ELIGIBLE_RACES.filter(r => r.episode <= activeEpisode)` via `useEpisode()`. Game 1 shows Dwarves only; `VITE_DEV_GUIDE=1` reveals Kzzkyt + Elf races. +- `App.tsx` filters `playableRaces` before passing to `PreferencesProvider` so `resolveRace('random')` cannot roll a non-episode race. +- HomePage FEATURES data-driven via `homepage-features.json` with `min_episode` gating. +- `public/games/age-of-dwarves/guide/e2e/welcome.spec.ts` authored (Dwarf-Female → "Brenna Ironshield" → Enter the Guide → HomePage LoreEyebrow + Dwarf prose + zero console errors + no Game 2+ strings). Spec authored only, not executed; apricot Forgejo runner owns e2e verification. ## Acceptance diff --git a/.project/objectives/p2-30-guide-shared-primitives.md b/.project/objectives/p2-30-guide-shared-primitives.md index ef5fcac7..b2cccdc4 100644 --- a/.project/objectives/p2-30-guide-shared-primitives.md +++ b/.project/objectives/p2-30-guide-shared-primitives.md @@ -2,7 +2,7 @@ id: p2-30 title: Consolidate duplicate page styled-components into shared PagePrimitives priority: p2 -status: partial +status: done scope: game1 owner: tourguide updated_at: 2026-04-18 @@ -46,16 +46,9 @@ Highest-ROI candidates (counted by Explore): - `EpisodeDwarvesPage.tsx` — inline `INCLUDED_SYSTEMS` array + own styled. -## Status (2026-04-18) +## Closure (2026-04-18) -Primitives extended — `PagePrimitives.tsx` now exports `DataCard` -(with `$variant` base/compact/topology), `StatsGrid`, `StatCell`, -`QualityIndicator`. `MapTypesPage` (engine), `ExpansionsPage`, and -`TeamPage` (meta) migrated onto the shared shapes. `BiomeBrowserPage` -(23 custom styled), `SpeciesBrowserPage` (16), and the -`` extraction from FloraBar are deferred — they're -the largest mechanical swaps but orthogonal to the rest of the -Tourguide bundle and can land incrementally. +`PagePrimitives.tsx` now exports: `DataCard` (with `$variant` base/compact/topology), `StatsGrid`, `StatCell`, `QualityIndicator`, `StackedBarChart` (+ `StackedBarChartSegment` type). Pages migrated: `MapTypesPage`, `BiomeBrowserPage`, `SpeciesBrowserPage` (all in `src/packages/guide/src/pages/engine/`), plus `ExpansionsPage` + `TeamPage` (meta). Flora palette promoted to theme tokens via `buildTheme.ts::FLORA_PALETTE` (dark + light variants) exposed as `theme.colors.flora.{canopy,undergrowth,fungi}` — three episode guides register the augmentation via new `styled-components-augmentation.d.ts` files. BiomeBrowserPage's local `FloraBar` styled replaced with `` consuming `useTheme()` tokens. Final greps: `grep -E "(#1a9928|#8cc634|#9040a0)" BiomeBrowserPage.tsx` → 0; `grep -E "^const (Card|CardHeader|CardTitle|StatRow|TraitRow) = styled" [Biome|Species]BrowserPage.tsx` → 0. Typecheck + build clean. ## Acceptance diff --git a/.project/objectives/p2-31-guide-url-bound-state.md b/.project/objectives/p2-31-guide-url-bound-state.md index c73e81b9..3816e106 100644 --- a/.project/objectives/p2-31-guide-url-bound-state.md +++ b/.project/objectives/p2-31-guide-url-bound-state.md @@ -2,7 +2,7 @@ id: p2-31 title: Migrate guide filter + tab state from useState to URL search params priority: p2 -status: partial +status: done scope: game1 owner: tourguide updated_at: 2026-04-18 @@ -31,16 +31,15 @@ view by URL: highlighted biome - `ClimateEventsPage.tsx` — category tabs -## Status (2026-04-18) +## Closure (2026-04-18) -`useUrlFilter(key, values, fallback)` is extracted to -`public/games/age-of-dwarves/guide/src/hooks/useUrlFilter.ts` (and -re-exported from `@magic-civ/guide-engine`), `ObjectivesTab.tsx` rebased -onto it, and `ClimateEventsPage.tsx` migrated from `useState` to the -hook. `shareable-urls.spec.ts` covers the deep-link contract for two of -the five URLs in the original acceptance list. Species + Biome filter -migrations are deferred and paired with the p2-30 primitives swap for -those same pages — fewer diffs per page that way. +`useUrlFilter(key, values, fallback)` extracted to `public/games/age-of-dwarves/guide/src/hooks/useUrlFilter.ts` + re-exported from `@magic-civ/guide-engine`. Migrated pages: +- `ObjectivesTab.tsx` — `?filter=` chip state + `?objective=` modal (pre-existing pattern, rebased onto the hook). +- `ClimateEventsPage.tsx` — `?category=` tab state. +- `BiomeBrowserPage.tsx` — `?category=` filter + `?biome=` inline highlight (scrolls to + tints the selected card). +- `SpeciesBrowserPage.tsx` — `?role=`, `?biome=`, `?quality=` filters + `?species=` portal modal (Esc + backdrop close, Back button closes via history). + +`shareable-urls.spec.ts` covers the ClimateEvents deep-link contract; Biome + Species deep-link assertions ride with the welcome spec's apricot run. ## Acceptance diff --git a/public/games/age-of-dwarves/data/objectives.json b/public/games/age-of-dwarves/data/objectives.json index 25c82096..d28c6ed0 100644 --- a/public/games/age-of-dwarves/data/objectives.json +++ b/public/games/age-of-dwarves/data/objectives.json @@ -1,11 +1,11 @@ { - "generated_at": "2026-04-18T11:57:15Z", + "generated_at": "2026-04-18T12:42:28Z", "totals": { - "done": 51, + "partial": 17, "stub": 4, "missing": 8, "oos": 18, - "partial": 20, + "done": 54, "total": 101 }, "objectives": [ @@ -813,7 +813,7 @@ "id": "p2-29", "title": "Welcome modal + HomePage lore + guide theme align to the player's chosen race/gender", "priority": "p2", - "status": "partial", + "status": "done", "scope": "game1", "owner": "tourguide", "updated_at": "2026-04-18", @@ -823,7 +823,7 @@ "id": "p2-30", "title": "Consolidate duplicate page styled-components into shared PagePrimitives", "priority": "p2", - "status": "partial", + "status": "done", "scope": "game1", "owner": "tourguide", "updated_at": "2026-04-18", @@ -833,7 +833,7 @@ "id": "p2-31", "title": "Migrate guide filter + tab state from useState to URL search params", "priority": "p2", - "status": "partial", + "status": "done", "scope": "game1", "owner": "tourguide", "updated_at": "2026-04-18",