diff --git a/.project/objectives/README.md b/.project/objectives/README.md index 48064293..a26a3336 100644 --- a/.project/objectives/README.md +++ b/.project/objectives/README.md @@ -10,12 +10,12 @@ | Status | Count | |---|---| -| ✅ done | 32 | -| 🟡 partial | 10 | +| ✅ done | 33 | +| 🟡 partial | 11 | | 🔴 stub | 0 | | ❌ missing | 2 | | ⚫ oos | 4 | -| **total** | **48** | +| **total** | **50** | ## P0 — Blockers for "completely playable" @@ -70,11 +70,13 @@ | [p2-06](p2-06-export-pipeline.md) | 🟡 partial | Export pipeline for Windows / macOS / Linux | [shipwright](../team-leads/shipwright.md) | 2026-04-17 | | [p2-07](p2-07-credits-screen.md) | ✅ done | Credits screen accessible from main menu | [shipwright](../team-leads/shipwright.md) | 2026-04-17 | | [p2-08](p2-08-accessibility.md) | ✅ done | Accessibility baseline — colorblind palette + keyboard navigation | [shipwright](../team-leads/shipwright.md) | 2026-04-17 | -| [p2-09](p2-09-guide-web-deploy.md) | 🟡 partial | Player guide web app — deployed and up to date | [shipwright](../team-leads/shipwright.md) | 2026-04-17 | +| [p2-09](p2-09-guide-web-deploy.md) | 🟡 partial | Player guide web app — builds clean from source | — | 2026-04-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 | [shipwright](../team-leads/shipwright.md) | 2026-04-17 | | [p2-17](p2-17-sprite-assets.md) | ❌ missing | Sprite assets — full unit / building / race / tier coverage | [shipwright](../team-leads/shipwright.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 | ## Out of Scope (Game 2) diff --git a/.project/objectives/p2-09-guide-web-deploy.md b/.project/objectives/p2-09-guide-web-deploy.md index 7c77f920..6f69116a 100644 --- a/.project/objectives/p2-09-guide-web-deploy.md +++ b/.project/objectives/p2-09-guide-web-deploy.md @@ -1,10 +1,10 @@ --- id: p2-09 -title: Player guide web app — deployed and up to date +title: Player guide web app — builds clean from source priority: p2 status: partial scope: game1 -owner: shipwright +owner: updated_at: 2026-04-17 evidence: - public/games/age-of-dwarves/guide/ @@ -32,6 +32,10 @@ acceptance_audit: data_driven_content: "✓ — the guide reads data via `import.meta.glob('../../../games/age-of-dwarves/data/*.json')` per guide CLAUDE.md convention. Architecture unchanged. Changing a deposit JSON propagates on the next `pnpm build`." --- +## Ownership note (2026-04-17) + +Shipwright released this objective per user directive. Guide-web surface is out of Shipwright's scope going forward. A separate agent will pick this up; `owner:` reset to unclaimed. Prior work (guide-drift-dev2 scope narrowing + guide-progress-dev progress-report page + Progress Report page) remains in-repo. + ## Summary Guide React app (Vite + TypeScript + React 19) lives under `public/games/age-of-dwarves/guide/`. WASM climate worker shares Rust crates with the game. @@ -66,10 +70,11 @@ should yield a clean `dist/index.html` in one step. The external-hosting decisio ## Acceptance -- `pnpm --filter guide-age-of-dwarves build` produces a static bundle with zero TypeScript errors. -- Bundle deployed to a public URL (GitHub Pages or equivalent) so marketing / Steam page can link it. -- Fix the `@/game.json` import at `guide-data.ts` so builds don't regress. -- Guide content stays data-driven — changing a deposit's JSON automatically updates the Resources page on next build. +- 🟡 `pnpm --filter guide-age-of-dwarves build` produces a static bundle with zero TypeScript errors — typecheck is 0 errors across both packages; vite transforms 3339 modules; final rollup step blocked by missing WASM pkg (apricot-build artifact) AND a new `GuideLayout` rollup variable-trace error post-drift-fix. +- ✓ Fix the `@/game.json` import at `guide-data.ts` so builds don't regress — landed. +- ✓ Guide content stays data-driven — changing a deposit's JSON automatically updates the Resources page on next build — architecture unchanged via `import.meta.glob`. + +**Split note:** public-URL deployment moved to its own objective p2-18 (was previously bundled here). Progress-report page moved to p2-19. This objective now focuses purely on "builds clean from source". ## Non-goals diff --git a/.project/objectives/p2-18-guide-public-deployment.md b/.project/objectives/p2-18-guide-public-deployment.md new file mode 100644 index 00000000..392e4a62 --- /dev/null +++ b/.project/objectives/p2-18-guide-public-deployment.md @@ -0,0 +1,37 @@ +--- +id: p2-18 +title: Guide web app — public hosting + deploy pipeline +priority: p2 +status: partial +scope: game1 +owner: +updated_at: 2026-04-17 +evidence: + - tools/deploy-guide.sh +--- + +## Ownership note (2026-04-17) + +Split out from p2-09 per user directive. Separate agent owns guide-web going forward; `owner:` is unclaimed for that agent to pick up. + +## Summary + +Separate from p2-09 (which covers the build being clean): this objective covers choosing a public host and running the deploy. Currently the deploy script is ready (`tools/deploy-guide.sh` — modes `build` / `serve` / `apricot` / `zip`), but no public host has been committed for Early Access. The `apricot` mode ships dist/ to the LAN for preview; `zip` produces a handoff artifact that any external host can consume. + +## Acceptance + +- ✗ Public hosting target chosen and documented in this file (options: GitHub Pages, Cloudflare Pages, Netlify, S3+CloudFront, self-hosted static). +- ✗ Deploy script mode added for the chosen host (extend `tools/deploy-guide.sh` with e.g. `deploy-guide.sh github-pages `). +- ✗ First deploy lands at the public URL; guide is reachable externally. +- ✗ URL recorded in CHANGELOG + player-facing docs (Steam page / README / marketing site). +- ✓ `tools/deploy-guide.sh` exists with build/serve/apricot/zip modes — shipped by export-ci-dev 2026-04-17. + +## Depends on + +- `p2-09` — guide must build cleanly (currently blocked on WASM rebuild + still-pending rollup fix). + +## Non-goals + +- CDN / caching policy beyond what the chosen host provides by default. +- Versioned URL history (e.g. `/v0.1.0/`, `/v0.1.1/`). +- Analytics integration. diff --git a/.project/objectives/p2-19-guide-progress-report-page.md b/.project/objectives/p2-19-guide-progress-report-page.md new file mode 100644 index 00000000..27b1f158 --- /dev/null +++ b/.project/objectives/p2-19-guide-progress-report-page.md @@ -0,0 +1,45 @@ +--- +id: p2-19 +title: Guide progress report page — dynamic dashboard + missing assets +priority: p2 +status: done +scope: game1 +owner: +updated_at: 2026-04-17 +evidence: + - public/games/age-of-dwarves/guide/src/pages/ProgressReportPage.tsx + - public/games/age-of-dwarves/guide/src/pages/progress-report/ + - public/games/age-of-dwarves/guide/src/pages/progress-report/__tests__/ + - public/games/age-of-dwarves/data/objectives.json + - tools/objectives-report.py +--- + +## Ownership note (2026-04-17) + +Split out from p2-09 per user directive. Guide-web surface is owned by a separate agent going forward; Shipwright is releasing this. Work here is already shipped — `owner:` reset so the new guide-agent can claim as they see fit. + +## Summary + +Dynamic progress report page inside the Age of Dwarves guide that reads the project's objectives dashboard + asset pipeline state at runtime. Built 2026-04-17 under guide-progress-dev. + +Delivery: +- `tools/objectives-report.py` extended to emit `public/games/age-of-dwarves/data/objectives.json` on every regen (schema: `{generated_at, totals, objectives[]}` with id/title/priority/status/scope/owner/updated_at/summary per objective). `--check` mode compares ignoring the volatile `generated_at`. +- `ProgressReportPage.tsx` + supporting modules under `public/games/age-of-dwarves/guide/src/pages/progress-report/` (types, styled, filter, assets-detection, ObjectiveModal). Renders: overall totals, per-priority progress bars, objective table (filterable All / P0 / Partial / Missing), click-through summary modal (uses `createPortal` to escape transformed layout ancestor). +- Missing assets section: scans `audio.json` (declared .ogg paths) and `units/*.json` + `buildings/*.json` (expected sprite paths) against `import.meta.glob` presence. Currently reports 0/16 audio + 0/33 unit sprites + 0/35 building sprites present (clean slate post-2026-04-17 sprite deletion). +- Route `/progress` added in `App.tsx`, nav entry `📊 Progress Report` at top of About group. +- 25 new Vitest tests (`assets-detection`, `filter`, `objectives-json`) → 115 total passing; apricot `pnpm build` ✓, `dist/index.html` exists, bundle 113kB. +- Incidental fix: orphan `healing_draught` reference in `items/manifest.json` removed (was breaking app bootstrap). + +## Acceptance + +- ✓ `tools/objectives-report.py` emits `public/games/age-of-dwarves/data/objectives.json` with correct schema + counts — verified by `objectives-json.test.ts`. +- ✓ `/progress` route renders totals, progress bars, filter toggles, modal — verified via Playwright screenshot at `.playwright-mcp/magic_civ_progress_report_v2.png`. +- ✓ Missing-assets detection live — scans audio.json + units/buildings JSON against glob; renders ✓/✗ per expected path. Tests in `assets-detection.test.ts`. +- ✓ Route + nav wired — `/progress` in App.tsx + lazy-page in `lazy-pages.ts` + nav entry in `nav.tsx`. +- ✓ All tests pass + apricot build produces dist/ — 115/115 vitest, build 10.87s, 0 TS errors. + +## Non-goals + +- Live reload as objectives.json changes (requires a watcher; deferred polish). +- Linking to .project/objectives/*.md source files (route gap — guide consumes JSON only). +- Progress diff view vs last N days (requires history store; post-EA polish). diff --git a/.project/team-leads/shipwright.md b/.project/team-leads/shipwright.md index 307ba174..b9b5e0ab 100644 --- a/.project/team-leads/shipwright.md +++ b/.project/team-leads/shipwright.md @@ -107,6 +107,7 @@ constants, test coverage, two-host workflow). - Do NOT skip the integrity rule to hit a release deadline. A dashboard that lies is worse than a dashboard that's behind. - Do NOT modify Game-2-scoped objectives (p2-12..p2-15) into Game-1. +- **Guide-web / `@magic-civ/guide-engine` / `public/games/age-of-dwarves/guide/` is OUT OF SCOPE** for Shipwright as of 2026-04-17 per user directive. A separate agent owns that surface. Do NOT dispatch guide-web specialists, do NOT claim guide-related objectives (e.g. p2-09), do NOT author new guide pages. Integrity note: the Progress Report page previously landed under Shipwright coordination (guide-progress-dev on 2026-04-17) stays in-repo and functional; future guide work goes to the new owner. ## Escalation diff --git a/public/games/age-of-dwarves/data/objectives.json b/public/games/age-of-dwarves/data/objectives.json index 07569f70..afa78c83 100644 --- a/public/games/age-of-dwarves/data/objectives.json +++ b/public/games/age-of-dwarves/data/objectives.json @@ -1,12 +1,12 @@ { - "generated_at": "2026-04-17T19:32:03Z", + "generated_at": "2026-04-17T19:38:01Z", "totals": { - "partial": 10, - "missing": 2, + "done": 33, "stub": 0, - "done": 32, "oos": 4, - "total": 48 + "partial": 11, + "missing": 2, + "total": 50 }, "objectives": [ { @@ -401,11 +401,11 @@ }, { "id": "p2-09", - "title": "Player guide web app — deployed and up to date", + "title": "Player guide web app — builds clean from source", "priority": "p2", "status": "partial", "scope": "game1", - "owner": "shipwright", + "owner": null, "updated_at": "2026-04-17", "summary": "Guide React app (Vite + TypeScript + React 19) lives under `public/games/age-of-dwarves/guide/`. WASM climate worker shares Rust crates with the game.\n\n**This pass (guide-drift-dev2 / 2026-04-17):** closed the systematic type drift between `@magic-civ/guide-engine` and its consumer. `pnpm typecheck` is now 0-errors in both packages (was 488 + 221 = 709 TS errors total). The prior \"32 errors\" count under-counted by ~22x because it only measured consumer-visible errors, not the 488 internal theme-augmentation errors in guide-engine itself.\n\nPer CLAUDE.md's hard Game-1 scope rule (*\"do NOT ship Game 2 features into Game 1\"*), Option 2 (scope-narrowing) was taken. All Game 2/3 content was excised:\n\n- **Deleted from `src/packages/guide/src/`:** entire `pages/magic/` directory (SpellsPage, MagicSchoolsPage, ArchonsPage, DisciplinesPage, LeyLinesPage), `pages/episodes/EpisodeKzzkytPage.tsx`, `pages/episodes/EpisodeElvesPage.tsx`, `pages/worlds/TheHivePlanetPage.tsx`, `pages/worlds/SilvandelPage.tsx`. Empty `pages/worlds/` dir removed.\n- **Deleted from consumer app `src/pages/`:** 5 local Magic pages (Spells, MagicSchools, Archons, Disciplines, LeyLines).\n- **Removed from routing + nav:** Ep2/Ep3 nav groups, all `/magic/*` routes, `/worlds/the-hive`, `/worlds/silvandel`, `/episodes/age-of-kzzkyt`, `/episodes/age-of-elves`.\n\n**Structural fixes landed:**\n\n- **styled-components theme augmentation** (`src/packages/guide/src/types/declarations.d.ts`): declared `DefaultTheme` with the exact `colors.{primary,accent,background,surface,border,text}` + `typography.{fontFamily,fontWeight}` shape used everywhere. Closed ~400 of 488 guide-engine errors.\n- **Ambient WASM + @resources/* + @lilith/ui-theme stubs** (`src/packages/guide/src/types/ambient.d.ts`, consumer `src/ambient.d.ts`): typed the shapes the guide actually uses, so `tsc --noEmit` from either package resolves cleanly without requiring the WASM pkg to be built.\n- **Game-data type drift:** extended `Unit` (added `hp`, `attack`, `defense`, `unit_type`, `flags`, `attributes`, `tier`, `terrain_bonus`, `encyclopedia`), `Building` (`culture_required`, `encyclopedia`), `Resource` / `Improvement` / `Item` (encyclopedia + index sig), `Tech` (replaced `unlocks_units`/`unlocks_buildings`/`unlocks_spells` → `unlocks: TechUnlocks` + `requires` + `flavor` + `encyclopedia`), `Race` (added `featured_units`, `arcane_rank`, `episode`, `status`), `EncyclopediaEntry` (added `entry_type`, `detail_route`), `EcologicalEventTier` (added `resource_table`), `StrategicAxes` (index sig for dynamic access). Added missing types: `Lens`, `LensCategory`, `LensUnlock`, `LensObservation`, `LensRendering`, `NamedResource`, `ResourceWithEncyclopedia`, `TechUnlocks`.\n- **Barrel surface:** rewrote `src/packages/guide/src/index.ts` from 52 lines to 125 lines with the full Game-1 surface (`PreferencesProvider`, `usePreferences`, `usePreferencesReroll`, `resolveGender`, `resolveRace`, `EpisodeProvider`/`Gate`, `GuideLayout`, `MobileNav`, `RaceThemeProvider`, `SPECIES_LIBRARY`, `applyObservationLens`, all retained pages, etc).\n- **New UI primitives:** added `PageHeading`, `PageSubtitle`, `DataTable`, `Highlight`, `FeatureGrid`, `FeatureChip` to `PagePrimitives.tsx` to match consumer-app expectations.\n- **Context drift:** `GuideDataContextValue` now declares `observationLens?: SpeciesObservationLens` + `speciesLibrary: ObservedSpecies[]` (consumer app was already passing these; type just wasn't there).\n- **Path aliases:** consumer's `@magic-civ/*` paths were off-by-one (`../../../` → `../../../../`); fixed. Added `@magic-civ/web-civmap` alias to guide-engine's tsconfig.\n- **Null-guard fixes:** eight consumer pages now guard optional fields before dereferencing (UnitsPage, CommunicationsPage, EncyclopediaModal, EncyclopediaPage, WondersPage, LairsPage, LensesPage, DevSpritesPage).\n\n**Remaining blocker to flip ✅ done:** `pnpm --filter @magic-civilization/guide-age-of-dwarves build` fails at the final rollup step because `src/simulator/pkg/magic_civ_physics.js` is absent on the EDIT host (WASM is an apricot-built artifact per CLAUDE.md two-host workflow). Apricot was unreachable during this pass (`ssh lilith@apricot.local` timed out). Once apricot is reachable:\n\n```\nssh \"$AUTOPLAY_HOST\" \"cd $PROJECT_ROOT_REMOTE/src/simulator && bash build-wasm.sh\"\npnpm --filter @magic-civilization/guide-age-of-dwarves build # from EDIT host\n```\n\nshould yield a clean `dist/index.html` in one step. The external-hosting decision (GitHub Pages vs Cloudflare Pages vs S3) remains a separate downstream gate." }, @@ -488,6 +488,26 @@ "owner": "shipwright", "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", + "priority": "p2", + "status": "partial", + "scope": "game1", + "owner": null, + "updated_at": "2026-04-17", + "summary": "Separate from p2-09 (which covers the build being clean): this objective covers choosing a public host and running the deploy. Currently the deploy script is ready (`tools/deploy-guide.sh` — modes `build` / `serve` / `apricot` / `zip`), but no public host has been committed for Early Access. The `apricot` mode ships dist/ to the LAN for preview; `zip` produces a handoff artifact that any external host can consume." + }, + { + "id": "p2-19", + "title": "Guide progress report page — dynamic dashboard + missing assets", + "priority": "p2", + "status": "done", + "scope": "game1", + "owner": null, + "updated_at": "2026-04-17", + "summary": "Dynamic progress report page inside the Age of Dwarves guide that reads the project's objectives dashboard + asset pipeline state at runtime. Built 2026-04-17 under guide-progress-dev.\n\nDelivery:\n- `tools/objectives-report.py` extended to emit `public/games/age-of-dwarves/data/objectives.json` on every regen (schema: `{generated_at, totals, objectives[]}` with id/title/priority/status/scope/owner/updated_at/summary per objective). `--check` mode compares ignoring the volatile `generated_at`.\n- `ProgressReportPage.tsx` + supporting modules under `public/games/age-of-dwarves/guide/src/pages/progress-report/` (types, styled, filter, assets-detection, ObjectiveModal). Renders: overall totals, per-priority progress bars, objective table (filterable All / P0 / Partial / Missing), click-through summary modal (uses `createPortal` to escape transformed layout ancestor).\n- Missing assets section: scans `audio.json` (declared .ogg paths) and `units/*.json` + `buildings/*.json` (expected sprite paths) against `import.meta.glob` presence. Currently reports 0/16 audio + 0/33 unit sprites + 0/35 building sprites present (clean slate post-2026-04-17 sprite deletion).\n- Route `/progress` added in `App.tsx`, nav entry `📊 Progress Report` at top of About group.\n- 25 new Vitest tests (`assets-detection`, `filter`, `objectives-json`) → 115 total passing; apricot `pnpm build` ✓, `dist/index.html` exists, bundle 113kB.\n- Incidental fix: orphan `healing_draught` reference in `items/manifest.json` removed (was breaking app bootstrap)." } ] } diff --git a/scripts/autoplay/run_ap3.sh b/scripts/autoplay/run_ap3.sh index f6418c3d..35f63e95 100755 --- a/scripts/autoplay/run_ap3.sh +++ b/scripts/autoplay/run_ap3.sh @@ -10,11 +10,19 @@ set -uo pipefail : "${AUTO_PLAY:=true}" : "${AUTO_PLAY_DIR:=$HOME/tmp/ap_default}" +: "${AP_RUN_ID:=}" -# Scoped cleanup: only kill prior processes for THIS AUTO_PLAY_DIR, so parallel -# sibling games (different AUTO_PLAY_DIR) are not disturbed. -pkill -f "AUTO_PLAY_DIR=$AUTO_PLAY_DIR " 2>/dev/null || true -pkill -f "AUTO_PLAY_DIR=$AUTO_PLAY_DIR\$" 2>/dev/null || true +# Scoped cleanup: kill any prior Godot process for THIS exact run ID. +# AP_RUN_ID is a unique token (_seed) injected by autoplay-batch.sh; +# using it instead of AUTO_PLAY_DIR avoids substring collisions between seeds +# whose dir names share a numeric prefix (e.g. seed1 matching seed10). +# When AP_RUN_ID is unset (manual invocation), fall back to AUTO_PLAY_DIR scope. +if [ -n "$AP_RUN_ID" ]; then + pkill -f "AP_RUN_ID=$AP_RUN_ID" 2>/dev/null || true +else + pkill -f "AUTO_PLAY_DIR=$AUTO_PLAY_DIR " 2>/dev/null || true + pkill -f "AUTO_PLAY_DIR=$AUTO_PLAY_DIR\$" 2>/dev/null || true +fi sleep 1 : "${AUTO_PLAY_TURN_LIMIT:=500}" @@ -29,9 +37,18 @@ FLATPAK_ENVS=( "--env=AUTO_PLAY_DIR=$AUTO_PLAY_DIR" "--env=AUTO_PLAY_TURN_LIMIT=$AUTO_PLAY_TURN_LIMIT" ) +if [ -n "${AP_RUN_ID:-}" ]; then + FLATPAK_ENVS+=("--env=AP_RUN_ID=$AP_RUN_ID") +fi if [ -n "${AUTO_PLAY_SEED:-}" ]; then FLATPAK_ENVS+=("--env=AUTO_PLAY_SEED=$AUTO_PLAY_SEED") fi +if [ -n "${AI_DIFFICULTY:-}" ]; then + FLATPAK_ENVS+=("--env=AI_DIFFICULTY=$AI_DIFFICULTY") +fi +if [ -n "${AI_PIN_PERSONALITY:-}" ]; then + FLATPAK_ENVS+=("--env=AI_PIN_PERSONALITY=$AI_PIN_PERSONALITY") +fi GODOT_ARGS=("--path" "." "--rendering-method" "gl_compatibility") WESTON_PID="" diff --git a/tools/autoplay-batch.sh b/tools/autoplay-batch.sh index a6532161..4cd54a9a 100755 --- a/tools/autoplay-batch.sh +++ b/tools/autoplay-batch.sh @@ -141,6 +141,7 @@ _run_local() { "--env=AUTO_PLAY_SEED=$seed" "--env=AUTO_PLAY_TURN_LIMIT=$TURN_LIMIT" "--env=AUTO_PLAY_DIR=$game_dir" + "--env=AP_RUN_ID=${STAMP}_seed${seed}" "--env=AI_DIFFICULTY=${AI_DIFFICULTY:-}" "--env=AI_PIN_PERSONALITY=${AI_PIN_PERSONALITY:-}" ) @@ -208,6 +209,7 @@ _run_remote() { AUTO_PLAY_SEED='$seed' \ AUTO_PLAY_TURN_LIMIT='$TURN_LIMIT' \ AUTO_PLAY_DIR='$remote_game_dir' \ + AP_RUN_ID="${STAMP}_seed${seed}" \ AI_DIFFICULTY='${AI_DIFFICULTY:-}' \ AI_PIN_PERSONALITY='${AI_PIN_PERSONALITY:-}' \ RENDER_MODE='$RENDER_MODE' \