diff --git a/.project/objectives/README.md b/.project/objectives/README.md index af813e03..7644eec7 100644 --- a/.project/objectives/README.md +++ b/.project/objectives/README.md @@ -16,9 +16,9 @@ |---|---|---|---|---|---|---|---| | **P0** | 44 | 0 | 0 | 0 | 0 | 0 | 44 | | **P1** | 79 | 0 | 13 | 1 | 0 | 1 | 94 | -| **P2** | 102 | 0 | 17 | 6 | 5 | 1 | 131 | +| **P2** | 102 | 0 | 19 | 5 | 5 | 1 | 132 | | **P3 (oos)** | 22 | 0 | 2 | 0 | 0 | 29 | 53 | -| **total** | **247** | **0** | **32** | **7** | **5** | **31** | **322** | +| **total** | **247** | **0** | **34** | **6** | **5** | **31** | **323** | @@ -31,9 +31,9 @@ | [asset-sprite](../team-leads/asset-sprite.md) | 6 | | [shipwright](../team-leads/shipwright.md) | 4 | | [unassigned](../team-leads/unassigned.md) | 3 | +| [wireguard](../team-leads/wireguard.md) | 2 | | [asset-audio](../team-leads/asset-audio.md) | 1 | | [testwright](../team-leads/testwright.md) | 1 | -| [wireguard](../team-leads/wireguard.md) | 1 | @@ -316,8 +316,9 @@ | [p2-82](p2-82-climate-input-save-fidelity.md) | ✅ done | Climate-input save-fidelity — persist (or re-derive) worldgen-static grid inputs across save/load | — | 2026-06-09 | | [p2-83](p2-83.md) | ❌ missing | Phase/round state machine + speculative parallel simulation of player-action-independent turn work | [simulator-infra](../team-leads/simulator-infra.md) | 2026-06-09 | | [p2-84](p2-84.md) | ❌ missing | "Dev-only compute profiling — per-feature CPU/RAM/GPU cost over time, trigger-attributed, zero-cost in release" | [simulator-infra](../team-leads/simulator-infra.md) | 2026-06-09 | -| [p2-85](p2-85-poi-sprites-and-tooltips.md) | 🔴 stub | "POI sprites + hover tooltips — lairs (and resources) legible on the map" | [asset-sprite](../team-leads/asset-sprite.md) | 2026-06-18 | +| [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-18 | | [p2-86](p2-86-mcp-rendered-driver.md) | 🟡 partial | "Claude-player MCP — rendered driver mode (drive UI + capture screenshots)" | [simulator-infra](../team-leads/simulator-infra.md) | 2026-06-18 | +| [p2-87-single-color-system-sot](p2-87-single-color-system-sot.md) | 🟡 partial | "Single game-wide colour system — one source of truth, layered tokens, every consumer derives from it" | [wireguard](../team-leads/wireguard.md) | 2026-06-18 | ## Out of Scope (Game 2 / Game 3) diff --git a/.project/objectives/p2-86-mcp-rendered-driver.md b/.project/objectives/p2-86-mcp-rendered-driver.md index 278e41a7..a135f728 100644 --- a/.project/objectives/p2-86-mcp-rendered-driver.md +++ b/.project/objectives/p2-86-mcp-rendered-driver.md @@ -26,18 +26,32 @@ complementing the headless state API. ## Acceptance criteria -- [ ] A second harness mode runs a **windowed/offscreen Godot** instance of the - real scenes (`world_map`, `city_screen`, menus) with `gl_compatibility`. -- [ ] New MCP tools: **`magic_civ_screenshot`** (capture viewport → - PNG via `get_viewport().get_texture().get_image().save_png`, same mechanism as - `standin_sprite_proof.gd`), **`magic_civ_open_screen(screen)`** (navigate UI), - optional `magic_civ_navigate` / `magic_civ_click`. -- [ ] Godot-side **MCP-driver autoload** that receives open-screen/screenshot - commands (over the existing harness stdio/socket) and returns the image path. -- [ ] Works on plum (windowed); document the cloud path (weston/xvfb + GPU node, - per the cloud-pivot handoff). -- [ ] Proof: from a Claude session, open `city_screen` and capture a screenshot - via the MCP tool. +- [x] A rendered boot path that loads the **real game** windowed with + `gl_compatibility` — done via `MC_AUTO_START=1` (`main.gd`, commit a1d5b3187), + which skips the menu into an interactive seeded game. +- [x] Godot-side **MCP-driver autoload** (`mcp_render_driver.gd`) that handles + `screenshot` / `open_screen` / `ping` / quit against the live rendered game and + returns the image path. Done via a **localhost TCP socket** (commit 22197a81c). +- [x] Works on plum (windowed); cloud path documented (weston/xvfb + GPU node). +- [ ] New MCP tools: **`magic_civ_screenshot`**, **`magic_civ_open_screen`** in + `index.ts` + `harness.ts` methods (connect to the driver's TCP port). +- [ ] `npm install` (never run — p2-67 phase 13) + `.mcp.json` entry + restart. +- [ ] Proof: from a Claude session, open a screen and capture via the MCP tool. + +## Progress (2026-06-18) — Phase 1 done + +- `MC_AUTO_START` boots straight into an interactive game (auto-start prereq). +- `mcp_render_driver` autoload, active on `MC_MCP_RENDER=1`, serves JSON-Lines + over a polled **TCP socket** (`MC_MCP_PORT`, default 8787). +- **Design pivot — TCP, not stdin:** `OS.read_string_from_stdin` blocks an open + pipe and freezes a rendered main loop; a `TCPServer` polled in `_process` is + non-blocking and also avoids Godot's windowed-stdout block-buffering (which + would have starved the MCP's stdout reader anyway). `player_api_main`'s stdin + pump is fine only because it runs `--headless`. +- **Verified end-to-end without MCP:** a raw TCP client → + `ping {ok}`, `screenshot` → a real **3420×1923 PNG** of the live world map. +- Phase 2 = the TS MCP tools + `harness.ts` TCP transport + npm install + + `.mcp.json` + restart (the only step that needs a Claude restart to verify). ## Verified architecture (2026-06-18) + implementation plan diff --git a/public/games/age-of-dwarves/data/objectives.json b/public/games/age-of-dwarves/data/objectives.json index f4b1de3e..18080ad7 100644 --- a/public/games/age-of-dwarves/data/objectives.json +++ b/public/games/age-of-dwarves/data/objectives.json @@ -1,13 +1,13 @@ { - "generated_at": "2026-06-19T00:56:27Z", + "generated_at": "2026-06-19T01:30:48Z", "totals": { "done": 247, "missing": 5, + "partial": 34, "oos": 31, "in_progress": 0, - "stub": 7, - "partial": 32, - "total": 322 + "stub": 6, + "total": 323 }, "objectives": [ { @@ -338,7 +338,7 @@ "scope": "game1", "owner": "shipwright", "updated_at": "2026-04-18", - "summary": "Implement a scripted opening sequence that runs on turns **-1**, **0**, and **1** before normal gameplay begins. Turn numbering skips from -1 to 0 to 1 (no \"turn -0.5\" or similar; -1 and 0 are both real turns but the player has no unit to command).\n\n1. **Turn -1 — Dispersed wanderers.** A **spawn box** is placed around each player's designated starting region. Inside the box, `N` ordinary free-dwarf wanderers spawn — **no `player_ancestor` flag, no pre-decided allegiance**. They are just freepeople. Each wanderer independently rolls a movement direction for turn -1 → 0. The roll is biased so that **at least `min_ancestors_to_form_tribe` (default 3) are guaranteed to roll \"toward box center\"** — these become the tribe founders at resolution time, *emergently*, not by pre-tagging. The remaining wanderers roll freely and may move outward or laterally. Fog is partially lifted so the player sees the whole box. The only legal input is **End Turn** (or Enter).\n2. **Turn 0 — Convergence / tribe formation.** Wanderers step along their rolled directions (deterministic from seed). At end-of-turn-0 resolution: the wanderers that ended up within `tribe_convergence_radius` of the box centroid merge into a single **Dwarf Tribe** unit at the centroid hex and are consumed. This is the player's founding tribe. **Wanderers that did NOT converge are NOT consumed** — they remain on the map as ordinary freepeople NPCs and continue their wander behavior (per `public/resources/villages/freepeople.json` rules). Pairs/trios of surviving non-converged wanderers may later coalesce into `nomadic_band` camps → grow into **freehavens** → evolve into city-states adjacent to the player (human or AI). This is the same mechanic as the player's own founding, applied generally: **any** 3+ freepeople that get within convergence radius form a camp; camps grow into havens. Again the only legal input is End Turn during this opening.\n3. **Turn 1 — First city.** The Dwarf Tribe unit appears under player control with exactly one available action: **Found Capital**. On founding, the capital's starting population is determined by the mode (see below), the Dwarf Tribe unit is consumed, and normal Game 1 play begins. All *subsequent* settlers built by cities are ordinary **Founder** units that always produce a pop-1 city.\n\n### Starting-population modes\n\n| Mode | Formula | Cap |\n|---|---|---|\n| **Tournament** | Starting pop = **1**, regardless of how many wanderers converged (min 3 still required). Guaranteed-convergence count is pinned to exactly 3; no extras are biased inward. | Fixed. |\n| **Lucky** (default for single-player casual) | Starting pop = `1 + floor((wanderers_converged - 3) / 3)` — each wanderer past the 3rd contributes **+1/3 pop**, rounded down at founding. Extra inward-biased rolls (beyond the guaranteed 3) are possible so variance can go up. | `max_lucky_bonus_pop = 3` (pop 4 from 12 converged). Tunable in `setup.json`. |\n\nRationale: tournament mode guarantees identical starting conditions across all five AI clans + human player for balanced tournaments / multi-seed validation batches. Lucky mode lets the spawn roll matter and rewards regions where more wanderers happen to converge (slightly favoring bountiful biomes in a later \"starting position type\" selector — out of scope here).\n\n### Roll bias mechanics\n\nFor each player's spawn box of `N` wanderers (`N ≈ 3..12`, seeded per map):\n- **Tournament**: exactly 3 wanderers get `direction = inward`; the remaining `N-3` roll uniformly from all 6 hex directions.\n- **Lucky**: 3 wanderers are pinned inward (floor guarantee); each of the remaining `N-3` independently rolls `inward_bias_prob` (default `0.33`) to also go inward, else uniform. This lets 3–`N` converge.\n- \"Inward\" means \"one of the 2 hex directions whose dot product with `centroid - wanderer_pos` is most positive\" — picked uniformly among ties, still deterministic from seed.\n\n### Non-converging wanderers become ordinary freepeople\n\nWanderers that drift outward / laterally on turn 0 are not special. They persist as standard freepeople NPCs and feed into the existing system:\n- They continue wandering via the scripted AI in `public/resources/ai/freepeople/freepeople.json`.\n- When 3+ freepeople (from *any* source — prologue drift, ongoing camp expansion, migration) get within `tribe_convergence_radius` of each other, they form a `nomadic_band` camp (`freepeople.json:camp_types[0]`).\n- Camps grow per `freepeople.json:growth` — at `expansion_threshold = 30` they may become **freehavens**, and high-ecology-tier havens may eventually emerge as city-states neighboring the player.\n- This means the opening cinematic *also* seeds rival neighbors: players who spawned with a dense box get more surviving drifters → more potential adjacent freehavens → more mid-game pressure. That pressure is symmetric across tournament mode (all players get `N=baseline`) and asymmetric in lucky mode.\n\n### Why Dwarf Tribe ≠ Founder\n\n- **Dwarf Tribe** (new unit): spawned only by the turn-0 convergence event. Carries `founding_pop_override: int` set at spawn time. Has one action: **Found Capital**. Cannot be built by cities. Never appears again after turn 1.\n- **Founder** (existing settler/pioneer unit): built normally by cities starting from turn 2+. Always founds a pop-1 city. No `founding_pop_override`.\n\nKeeping them separate means the variance only exists at game-start, not inside the mid-game economy." + "summary": "Implement a scripted opening sequence that runs on turns **-1**, **0**, and **1** before normal gameplay begins. Turn numbering skips from -1 to 0 to 1 (no \"turn -0.5\" or similar; -1 and 0 are both real turns but the player has no unit to command).\n\n1. **Turn -1 — Dispersed wanderers.** A **spawn box** is placed around each player's designated starting region. Inside the box, `N` ordinary free-dwarf wanderers spawn — **no `player_ancestor` flag, no pre-decided allegiance**. They are just freepeople. Each wanderer independently rolls a movement direction for turn -1 → 0. The roll is biased so that **at least `min_ancestors_to_form_tribe` (default 3) are guaranteed to roll \"toward box center\"** — these become the tribe founders at resolution time, *emergently*, not by pre-tagging. The remaining wanderers roll freely and may move outward or laterally. Fog is partially lifted so the player sees the whole box. The only legal input is **End Turn** (or Enter).\n2. **Turn 0 — Convergence / tribe formation.** Wanderers step along their rolled directions (deterministic from seed). At end-of-turn-0 resolution: the wanderers that ended up within `tribe_convergence_radius` of the box centroid merge into a single **Dwarf Tribe** unit at the centroid hex and are consumed. This is the player's founding tribe. **Wanderers that did NOT converge are NOT consumed** — they remain on the map as ordinary freepeople NPCs and continue their wander behavior (per `public/resources/villages/freepeople.json` rules). Pairs/trios of surviving non-converged wanderers may later coalesce into `nomadic_band` camps → grow into **freehavens** → evolve into city-states adjacent to the player (human or AI). This is the same mechanic as the player's own founding, applied generally: **any** 3+ freepeople that get within convergence radius form a camp; camps grow into havens. Again the only legal input is End Turn during this opening.\n3. **Turn 1 — First city.** The Dwarf Tribe unit appears under player control as a **movable unit** with action bar `[\"found_capital\", \"move\"]` and **1 movement point**: the player may either found the capital on the centroid hex immediately or relocate it one hex before founding. On founding, the capital's starting population is determined by the mode (see below), the Dwarf Tribe unit is consumed, and normal Game 1 play begins. (AI/NPC tribes do not exercise the move option — they auto-found at the centroid; only player-controlled tribes get the move-vs-found choice.) All *subsequent* settlers built by cities are ordinary **Founder** units that always produce a pop-1 city.\n\n### Starting-population modes\n\n| Mode | Formula | Cap |\n|---|---|---|\n| **Tournament** | Starting pop = **1**, regardless of how many wanderers converged (min 3 still required). Guaranteed-convergence count is pinned to exactly 3; no extras are biased inward. | Fixed. |\n| **Custom** (default for single-player casual) | Starting pop = `1 + floor((wanderers_converged - 3) / 3)` — each wanderer past the 3rd contributes **+1/3 pop**, rounded down at founding. Extra inward-biased rolls (beyond the guaranteed 3) are possible so variance can go up. | `custom_max_bonus_pop = 3`; with the custom wanderer count capped at 9 the practical ceiling is **pop 3 from 9 converged**. Tunable in `setup.json`. |\n\nRationale: tournament mode guarantees identical starting conditions across all five AI clans + human player for balanced tournaments / multi-seed validation batches. Custom mode lets the spawn roll matter and rewards regions where more wanderers happen to converge (slightly favoring bountiful biomes in a later \"starting position type\" selector — out of scope here).\n\n### Roll bias mechanics\n\nFor each player's spawn box of `N` wanderers (`N ≈ 3..9`, seeded per map; tournament pins `N = 3`, custom rolls `N ∈ [3, 9]`):\n- **Tournament**: exactly 3 wanderers get `direction = inward`; the remaining `N-3` roll uniformly from all 6 hex directions.\n- **Custom**: 3 wanderers are pinned inward (floor guarantee); each of the remaining `N-3` independently rolls `inward_bias_prob` (default `0.33`) to also go inward, else uniform. This lets 3–`N` converge.\n- \"Inward\" means \"one of the 2 hex directions whose dot product with `centroid - wanderer_pos` is most positive\" — picked uniformly among ties, still deterministic from seed.\n\n### Non-converging wanderers become ordinary freepeople\n\nWanderers that drift outward / laterally on turn 0 are not special. They persist as standard freepeople NPCs and feed into the existing system:\n- They continue wandering via the scripted AI in `public/resources/ai/freepeople/freepeople.json`.\n- When 3+ freepeople (from *any* source — prologue drift, ongoing camp expansion, migration) get within `tribe_convergence_radius` of each other, they form a `nomadic_band` camp (`freepeople.json:camp_types[0]`).\n- Camps grow per `freepeople.json:growth` — at `expansion_threshold = 30` they may become **freehavens**, and high-ecology-tier havens may eventually emerge as city-states neighboring the player.\n- This means the opening cinematic *also* seeds rival neighbors: players who spawned with a dense box get more surviving drifters → more potential adjacent freehavens → more mid-game pressure. That pressure is symmetric across tournament mode (all players get `N=baseline`) and asymmetric in custom mode.\n\n### Why Dwarf Tribe ≠ Founder\n\n- **Dwarf Tribe** (new unit): spawned only by the turn-0 convergence event. Carries `founding_pop_override: int` set at spawn time. **Movable** under player control — action bar `[\"found_capital\", \"move\"]`, 1 MP — so the player can reposition one hex before founding; AI/NPC tribes auto-found at the centroid and never move. Cannot be built by cities. Never appears again after turn 1.\n- **Founder** (existing settler/pioneer unit): built normally by cities starting from turn 2+. Always founds a pop-1 city. No `founding_pop_override`.\n\nKeeping them separate means the variance only exists at game-start, not inside the mid-game economy." }, { "id": "p0-37", @@ -2684,7 +2684,7 @@ "id": "p2-85", "title": "\"POI sprites + hover tooltips — lairs (and resources) legible on the map\"", "priority": "p2", - "status": "stub", + "status": "partial", "scope": "game1", "owner": "asset-sprite", "updated_at": "2026-06-18", @@ -2700,6 +2700,16 @@ "updated_at": "2026-06-18", "summary": "" }, + { + "id": "p2-87-single-color-system-sot", + "title": "\"Single game-wide colour system — one source of truth, layered tokens, every consumer derives from it\"", + "priority": "p2", + "status": "partial", + "scope": "game1", + "owner": "wireguard", + "updated_at": "2026-06-18", + "summary": "" + }, { "id": "g2-01", "title": "Ley lines — Game 2 (Age of Kzzykt)",