diff --git a/.project/objectives/DASHBOARD_CATEGORIES.md b/.project/objectives/DASHBOARD_CATEGORIES.md
index 78b01aed..f7f6f516 100644
--- a/.project/objectives/DASHBOARD_CATEGORIES.md
+++ b/.project/objectives/DASHBOARD_CATEGORIES.md
@@ -83,7 +83,8 @@
| [p0-40](p0-40-iron-ore-resource-density.md) | โ
done | P0 | Iron-ore strategic resource density โ unblock tier 3-6 unit chain | [shipwright](../team-leads/shipwright.md) | ๐ข |
| [p0-41](p0-41.md) | โ
done | P0 | Building rally points โ produced units auto-deploy to a designated hex | [shipwright](../team-leads/shipwright.md) | ๐ข |
| [p0-41a](p0-41a-rally-smoke.md) | ๐ก partial | P0 | Rally-point smoke test โ unit moves toward rally hex on next turn | [shipwright](../team-leads/shipwright.md) | ๐ข |
-| [p0-42](p0-42.md) | ๐ก partial | P0 | Formation aggregation โ adjacent units link into a shaped formation with terrain reflow | [shipwright](../team-leads/shipwright.md) | ๐ข |
+| [p0-42](p0-42.md) | โ
done | P0 | Formation aggregation โ adjacent units link into a shaped formation with terrain reflow | [shipwright](../team-leads/shipwright.md) | ๐ข |
+| [p0-42a](p0-42a-formation-smoke.md) | โ missing | P0 | Formation aggregation smoke โ 3 units form, move through narrow pass, reflow | [shipwright](../team-leads/shipwright.md) | ๐ข |
| [p0-44](p0-44-movement-mode-ux.md) | โ
done | P0 | Movement mode UX โ Move button, path preview, right-click confirm, fog-aware pathing | [wireguard](../team-leads/wireguard.md) | ๐ข |
| [p1-01](p1-01-diplomacy-lite.md) | โ
done | P1 | Diplomacy-lite โ peace/war toggle plus one trade action | [shipwright](../team-leads/shipwright.md) | ๐ข |
| [p1-02](p1-02-strategic-resource-yields.md) | โ
done | P1 | Strategic resource yields feed into production bonuses | [shipwright](../team-leads/shipwright.md) | ๐ข |
@@ -106,12 +107,14 @@
| [p1-20](p1-20-unit-action-capability-registry.md) | โ
done | P1 | Unit action capability registry โ one source of truth for "what can this unit do right now?" | [wireguard](../team-leads/wireguard.md) | ๐ข |
| [p1-21](p1-21-unit-patrol-orders.md) | โ
done | P1 | Unit patrol orders โ standing order to loop between waypoint tiles | [wireguard](../team-leads/wireguard.md) | ๐ข |
| [p1-22](p1-22-mcts-wall-clock-budget.md) | โ missing | P1 | MCTS per-decision wall-clock budget โ bound per-turn cost on huge maps | [warcouncil](../team-leads/warcouncil.md) | ๐ข |
+| [p1-23](p1-23-stats-tracker-restore.md) | โ missing | P1 | Restore StatsTracker โ demographics overview broken in shipped builds | [shipwright](../team-leads/shipwright.md) | ๐ข |
| [p2-01](p2-01-minimap-improvements.md) | โ
done | P2 | Minimap โ fog reflection and unit markers | [shipwright](../team-leads/shipwright.md) | ๐ข |
| [p2-02](p2-02-hud-tooltips.md) | โ
done | P2 | Tooltips on all HUD elements | [shipwright](../team-leads/shipwright.md) | ๐ข |
| [p2-03](p2-03-hotkey-cheat-sheet.md) | โ
done | P2 | Hotkey cheat sheet (F1 / ?) | [shipwright](../team-leads/shipwright.md) | ๐ข |
| [p2-04](p2-04-localization-audit.md) | โ
done | P2 | Localization audit โ no hardcoded strings | [shipwright](../team-leads/shipwright.md) | ๐ข |
| [p2-05](p2-05-turn-latency.md) | โ
done | P2 | Sub-second single-player turn latency | โ | ๐ข |
| [p2-06](p2-06-export-pipeline.md) | ๐ก partial | P1 | Export pipeline for Windows / macOS / Linux | [shipwright](../team-leads/shipwright.md) | ๐ข |
+| [p2-06b](p2-06b-windows-runner.md) | โ missing | P2 | Register a Windows runner for forgejo to produce .dll + .exe on tag push | [shipwright](../team-leads/shipwright.md) | ๐ข |
| [p2-07](p2-07-credits-screen.md) | โ
done | P2 | Credits screen accessible from main menu | [shipwright](../team-leads/shipwright.md) | ๐ข |
| [p2-08](p2-08-accessibility.md) | โ
done | P2 | Accessibility baseline โ colorblind palette + keyboard navigation | [shipwright](../team-leads/shipwright.md) | ๐ข |
| [p2-09](p2-09-guide-web-deploy.md) | โ
done | P2 | Player guide web app โ builds clean from source | โ | ๐ข |
@@ -119,6 +122,7 @@
| [p2-10a](p2-10a-gdlint-ungate.md) | ๐ก partial | P2 | CI: gdlint stage un-gated | [testwright](../team-leads/testwright.md) | ๐ข |
| [p2-10b](p2-10b-gut-ungate.md) | ๐ก partial | P2 | CI: headless GUT stage un-gated | [testwright](../team-leads/testwright.md) | ๐ข |
| [p2-11](p2-11-version-about-screen.md) | โ
done | P2 | Version string + About screen | [shipwright](../team-leads/shipwright.md) | ๐ข |
+| [p2-12](p2-12-apricot-weston-install.md) | โ missing | P2 | Install weston on apricot RUN host โ unblock display-server smoke tests | [shipwright](../team-leads/shipwright.md) | ๐ข |
| [p2-16](p2-16-audio-assets.md) | โ missing | P1 | Audio assets โ SFX + music .ogg files shipped | [asset-audio](../team-leads/asset-audio.md) | ๐ข |
| [p2-18](p2-18-guide-public-deployment.md) | ๐ก partial | P2 | Guide web app โ public hosting + deploy pipeline | โ | ๐ข |
| [p2-19](p2-19-guide-progress-report-page.md) | โ
done | P2 | Guide progress report page โ dynamic dashboard + missing assets | โ | ๐ข |
diff --git a/.project/objectives/DASHBOARD_COMPLETED.md b/.project/objectives/DASHBOARD_COMPLETED.md
index 50912458..359ad358 100644
--- a/.project/objectives/DASHBOARD_COMPLETED.md
+++ b/.project/objectives/DASHBOARD_COMPLETED.md
@@ -42,6 +42,7 @@
| [p0-39](p0-39-ai-tier-progression-unit-selection.md) | AI tier-progression unit selection โ production.rs picks tier-2+ units once tech unlocks | โ | [warcouncil](../team-leads/warcouncil.md) | 2026-04-18 |
| [p0-40](p0-40-iron-ore-resource-density.md) | Iron-ore strategic resource density โ unblock tier 3-6 unit chain | โ | [shipwright](../team-leads/shipwright.md) | 2026-04-24 |
| [p0-41](p0-41.md) | Building rally points โ produced units auto-deploy to a designated hex | โ | [shipwright](../team-leads/shipwright.md) | 2026-04-24 |
+| [p0-42](p0-42.md) | Formation aggregation โ adjacent units link into a shaped formation with terrain reflow | โ | [shipwright](../team-leads/shipwright.md) | 2026-04-25 |
| [p0-44](p0-44-movement-mode-ux.md) | Movement mode UX โ Move button, path preview, right-click confirm, fog-aware pathing | โ | [wireguard](../team-leads/wireguard.md) | 2026-04-19 |
## P1 โ Ship-readiness
diff --git a/.project/objectives/README.md b/.project/objectives/README.md
index 9270398e..6d955b6f 100644
--- a/.project/objectives/README.md
+++ b/.project/objectives/README.md
@@ -14,11 +14,11 @@
| Priority | ๐ต | ๐ก | ๐ด | โ | โซ | โ
| Total |
|---|---|---|---|---|---|---|---|
-| **P0** | 0 | 5 | 0 | 0 | 0 | 37 | 42 |
-| **P1** | 0 | 3 | 0 | 9 | 1 | 20 | 33 |
-| **P2** | 0 | 4 | 0 | 0 | 0 | 16 | 20 |
+| **P0** | 0 | 4 | 0 | 1 | 0 | 38 | 43 |
+| **P1** | 0 | 3 | 0 | 10 | 1 | 20 | 34 |
+| **P2** | 0 | 4 | 0 | 2 | 0 | 16 | 22 |
| **P3 (oos)** | 0 | 0 | 0 | 0 | 17 | 0 | 17 |
-| **total** | **0** | **12** | **0** | **9** | **18** | **73** | **112** |
+| **total** | **0** | **11** | **0** | **13** | **18** | **74** | **116** |
@@ -27,8 +27,8 @@
| Team Lead | Remaining |
|---|---|
| [asset-sprite](../team-leads/asset-sprite.md) | 7 |
+| [shipwright](../team-leads/shipwright.md) | 7 |
| [warcouncil](../team-leads/warcouncil.md) | 5 |
-| [shipwright](../team-leads/shipwright.md) | 4 |
| [testwright](../team-leads/testwright.md) | 3 |
| [asset-audio](../team-leads/asset-audio.md) | 1 |
@@ -41,17 +41,18 @@
| [p0-01](p0-01-mcts-wiring.md) | ๐ก partial | Wire MCTS into gameplay AI | โ | [warcouncil](../team-leads/warcouncil.md) | 2026-04-24 | ๐ข unblocked |
| [p0-02](p0-02-clan-personalities.md) | ๐ก partial | Five AI clan personalities drive distinct playstyles | โ | [warcouncil](../team-leads/warcouncil.md) | 2026-04-19 | ๐ข unblocked |
| [p0-41a](p0-41a-rally-smoke.md) | ๐ก partial | Rally-point smoke test โ unit moves toward rally hex on next turn | โ | [shipwright](../team-leads/shipwright.md) | 2026-04-25 | ๐ข unblocked |
-| [p0-42](p0-42.md) | ๐ก partial | Formation aggregation โ adjacent units link into a shaped formation with terrain reflow | โ | [shipwright](../team-leads/shipwright.md) | 2026-04-24 | ๐ข unblocked |
| [p0-43](p0-43.md) | ๐ก partial | Formation AI โ MCTS plans at formation level, not per-unit | formation, ai, mcts | [warcouncil](../team-leads/warcouncil.md) | 2026-04-24 | ๐ข unblocked |
+| [p0-42a](p0-42a-formation-smoke.md) | โ missing | Formation aggregation smoke โ 3 units form, move through narrow pass, reflow | โ | [shipwright](../team-leads/shipwright.md) | 2026-04-25 | ๐ข unblocked |
## P1 โ Ship-readiness
| ID | Status | Title | Tags | Owner | Updated | Blocked |
|---|---|---|---|---|---|---|
| [p0-20](p0-20-gpu-mcts-rollouts.md) | ๐ก partial | GPU-accelerated MCTS rollouts for look-ahead decision-making | โ | [warcouncil](../team-leads/warcouncil.md) | 2026-04-19 | ๐ข unblocked |
-| [p1-05](p1-05-balance-tuning.md) | ๐ก partial | Balance tuning โ pop_peak โฅ30 median, worker improvements โฅ8 min | โ | [shipwright](../team-leads/shipwright.md) | 2026-04-17 | ๐ข unblocked |
-| [p2-06](p2-06-export-pipeline.md) | ๐ก partial | Export pipeline for Windows / macOS / Linux | โ | [shipwright](../team-leads/shipwright.md) | 2026-04-18 | ๐ข unblocked |
+| [p1-05](p1-05-balance-tuning.md) | ๐ก partial | Balance tuning โ pop_peak โฅ30 median, worker improvements โฅ8 min | โ | [shipwright](../team-leads/shipwright.md) | 2026-04-25 | ๐ข unblocked |
+| [p2-06](p2-06-export-pipeline.md) | ๐ก partial | Export pipeline for Windows / macOS / Linux | โ | [shipwright](../team-leads/shipwright.md) | 2026-04-25 | ๐ข unblocked |
| [p1-22](p1-22-mcts-wall-clock-budget.md) | โ missing | MCTS per-decision wall-clock budget โ bound per-turn cost on huge maps | โ | [warcouncil](../team-leads/warcouncil.md) | 2026-04-25 | ๐ข unblocked |
+| [p1-23](p1-23-stats-tracker-restore.md) | โ missing | Restore StatsTracker โ demographics overview broken in shipped builds | โ | [shipwright](../team-leads/shipwright.md) | 2026-04-25 | ๐ข unblocked |
| [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 | ๐ข unblocked |
| [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 | ๐ข unblocked |
| [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 | ๐ข unblocked |
@@ -69,6 +70,8 @@
| [p2-10a](p2-10a-gdlint-ungate.md) | ๐ก partial | CI: gdlint stage un-gated | โ | [testwright](../team-leads/testwright.md) | 2026-04-25 | ๐ข unblocked |
| [p2-10b](p2-10b-gut-ungate.md) | ๐ก partial | CI: headless GUT stage un-gated | โ | [testwright](../team-leads/testwright.md) | 2026-04-25 | ๐ข unblocked |
| [p2-18](p2-18-guide-public-deployment.md) | ๐ก partial | Guide web app โ public hosting + deploy pipeline | โ | โ | 2026-04-17 | ๐ข unblocked |
+| [p2-06b](p2-06b-windows-runner.md) | โ missing | Register a Windows runner for forgejo to produce .dll + .exe on tag push | โ | [shipwright](../team-leads/shipwright.md) | 2026-04-25 | ๐ข unblocked |
+| [p2-12](p2-12-apricot-weston-install.md) | โ missing | Install weston on apricot RUN host โ unblock display-server smoke tests | โ | [shipwright](../team-leads/shipwright.md) | 2026-04-25 | ๐ข unblocked |
## Out of Scope
diff --git a/.project/objectives/objectives.json b/.project/objectives/objectives.json
index 83118270..f4027137 100644
--- a/.project/objectives/objectives.json
+++ b/.project/objectives/objectives.json
@@ -1,13 +1,13 @@
{
- "generated_at": "2026-04-25T22:58:07Z",
+ "generated_at": "2026-04-25T23:41:17Z",
"totals": {
- "done": 73,
+ "done": 74,
"in_progress": 0,
- "partial": 12,
+ "partial": 11,
"stub": 0,
- "missing": 9,
+ "missing": 13,
"oos": 18,
- "total": 112
+ "total": 116
},
"objectives": [
{
@@ -433,13 +433,24 @@
"id": "p0-42",
"title": "Formation aggregation โ adjacent units link into a shaped formation with terrain reflow",
"priority": "p0",
- "status": "partial",
+ "status": "done",
"scope": "game1",
"owner": "shipwright",
- "updated_at": "2026-04-24",
+ "updated_at": "2026-04-25",
"blocked_by": [],
"summary": "Units in adjacent hexes (same owner, both with auto_join enabled) automatically link into a Formation. Each unit retains its own hex โ no stacking. The formation has a defined shape (Line, Column, Wedge, Diamond) expressed as relative hex offsets from a leader unit. When the formation moves, a reflow solver computes target hexes for all members: if the preferred shape doesn't fit terrain (e.g. a 5-wide Line entering a 2-hex canyon), it automatically compresses to a Column and re-expands on exit. Combat with formation_count set from the number of linked units uses the existing `dmg ร count^0.75` and `HP ร count` scaling already in mc-combat/resolver.rs. Selection: single-click selects formation; double-click selects the individual unit. 'Exit Formation' button in unit panel splits the unit back to solo."
},
+ {
+ "id": "p0-42a",
+ "title": "Formation aggregation smoke โ 3 units form, move through narrow pass, reflow",
+ "priority": "p0",
+ "status": "missing",
+ "scope": "game1",
+ "owner": "shipwright",
+ "updated_at": "2026-04-25",
+ "blocked_by": [],
+ "summary": "End-to-end smoke verification for the formation aggregation feature (p0-42). Spun off from p0-42 on 2026-04-25 because the smoke gate requires a wayland display server (weston) on apricot, which is not currently installed (see p2-12). Parent p0-42 has all code-complete bullets โ; only the run-time observation remains."
+ },
{
"id": "p0-43",
"title": "Formation AI โ MCTS plans at formation level, not per-unit",
@@ -535,7 +546,7 @@
"status": "partial",
"scope": "game1",
"owner": "shipwright",
- "updated_at": "2026-04-17",
+ "updated_at": "2026-04-25",
"blocked_by": [],
"summary": "Post-p0-16 batch (`.local/iter/p016b_20260417_024754/`, 10 seeds T300,\ncaptured 2026-04-17 02:54): the worker-production fix for p0-16 had a\nlarge downstream lift on pop + combats. Per-seed p0_pop_peak =\n[58,46,76,65,77,74,53,113,73,36]; median **69**, min 36, max 113. Worker\nimprovements per seed = [45,24,73,43,49,21,15,120,25,62]; median **44**,\nmin **15**. Combats median **808**, techs median **39**. All four primary\nacceptance metrics now clear their thresholds decisively โ the 29.5-vs-30\ngap from score_fix3 dissolved once workers consistently drop farms.\n\nShipwright passes applied:\n- `farm.json` food yield **2 โ 3** (prior tune, validated in p016b where\n per-seed farm counts 3-20 drive pop_peak 36-113).\n- Worker AI surfaced in both `auto_play.gd::_maybe_prioritize_worker` and\n `simple_heuristic_ai.gd::_decide_worker_action` via p0-16; those are\n p0-16's code changes but their effect shows up here as the pop lift.\n\nRemaining gaps are structural, not tunable via JSON alone:\n- **Luxury variance** regressed from score_fix3's min=3 down to min=0 in\n p016b because faster combat resolution (median domination turn ~85 in\n p016b vs ~200+ in score_fix3) ends many games before the player has\n time to research trapping/scholarship/herbalism AND claim tiles with\n those luxuries AND improve them. 14 of 15 luxuries are tech-gated in\n `resources.json`. Tuning would need to either un-gate early-luxuries\n (ivory/furs/salt) or slow combat โ both are cross-cutting changes\n (p0-06 economy + p0-08 domination tempo) that exceed p1-05's\n tuning-only scope.\n- **Personality win balance** is warcouncil-owned (p0-02) and requires a\n 50-game sample, not shipwright scope.\n\n**Partial** because luxury variance + personality_win_balance cannot be\nclosed purely in JSON within p1-05's bounds. Other 4 primary metrics are\ndone.\n\n**2026-04-17 ecology handoff from p0-30:** duplicate GDScript ecology tick\n(`ecosystem.gd` + `flora.gd`) deleted; ecology is dormant until\n`ClimateScript.process_turn` is re-enabled, at which point\n`GdEcologyPhysics::process_step` becomes the sole canonical tick. Any\nwilds/food/lair knobs tuned against the previous 1ร GDScript rate may\nneed re-tuning against the Rust rate in a follow-up pass."
},
@@ -726,6 +737,17 @@
"blocked_by": [],
"summary": "Spun out from p0-22 (Ultimate AI stress test) on 2026-04-25 after the 7 root-cause fixes (combat method typos, per-slot pinning, score-victory fallback, NOTIFICATION_PREDELETE, autoplay-batch.sh MCTS branch, etc.) verified the pipeline produces `outcome:victory` at T500 on the huge-map config. The remaining gap blocking `ultimate_stress: PASS` is **purely MCTS per-turn wall-clock cost on game-state complexity**: with deterministic seeds, some maps produce game states where each MCTS decision takes 30-60+ seconds (vs <5s on simpler states). Even at `PARALLEL=2 SAFETY_TIMEOUT_OVERRIDE=3600s`, slow seeds reach only T55-T236 in the 3600s budget (would need 4-8 hours wall-clock per game). Fast seeds reach T500 in ~45min.\n\nThis is engineering work, not test calibration: the AI is ALWAYS faster when it commits to a decision under a bounded budget. The current MCTS runs to a fixed iteration count regardless of wall-clock cost; on a complex 5-player huge-map state the iteration cost balloons."
},
+ {
+ "id": "p1-23",
+ "title": "Restore StatsTracker โ demographics overview broken in shipped builds",
+ "priority": "p1",
+ "status": "missing",
+ "scope": "game1",
+ "owner": "shipwright",
+ "updated_at": "2026-04-25",
+ "blocked_by": [],
+ "summary": "`engine/scenes/overviews/demographics.gd` references `StatsTracker.CATEGORIES`, `StatsTracker.CATEGORY_LABELS`, and `StatsTracker.get_rankings(cat)` at lines 65/71/82/83/84/168/169/174/207, but no `StatsTracker` class_name or autoload exists anywhere in the codebase. Surfaced 2026-04-25 in `p2-06-verify-20260425` export logs as 4ร `SCRIPT ERROR: Identifier \"StatsTracker\" not declared`. The demographics screen is shipped broken."
+ },
{
"id": "p2-06",
"title": "Export pipeline for Windows / macOS / Linux",
@@ -733,7 +755,7 @@
"status": "partial",
"scope": "game1",
"owner": "shipwright",
- "updated_at": "2026-04-18",
+ "updated_at": "2026-04-25",
"blocked_by": [],
"summary": "Players need binaries. Godot export presets (desktop: Linux/X11, macOS, Windows Desktop) are authored; the `./run export` chain produces per-platform archives via `tools/export.sh` + `tools/export-single.sh`, and the `.forgejo/workflows/release.yml` tag-push pipeline bundles Linux + macOS + Windows + WASM-guide archives into a Forgejo release with release notes generated from the CHANGELOG diff.\n\nOpen work: (1) Windows `.dll` production only happens on a registered windows runner โ local `./run export:windows` from a macOS/Linux EDIT host does not yet cross-compile, and no forgejo windows runner is registered. (2) The boots-and-plays end-to-end smoke has not been run against a fresh export archive โ the prior audit's 29MB .x86_64 was discovered this pass to be non-bootable (missing embedded .pck from a concurrent --import race). A clean re-export + AUTO_PLAY 10-turn smoke on a dedicated off-peak runner is the remaining gate. (3) AutoPlay autoload shipping (โ this pass) unblocks (2) but (2) itself is still โ.\n\n### macOS scan-inflation fix (2026-04-17, commit f090d28a7)\n\nThe prior 20+ min plum export stall was root-caused to Godot's export scanner walking the entire project tree *before* applying `exclude_filter` โ the three pnpm-managed `public/games/*/guide/node_modules/` symlinks dereferenced into the hoisted store and emitted ~16MB of `_scan_new_dir` warnings. Fixed in `tools/export-single.sh` by rsync-staging the project to `.local/export-staging-/` (excluding `node_modules`, `.local`, `target`, `.git`, `dist`, `.vite*`) before invoking godot. Default-on for macos; opt-in via `EXPORT_STAGED=1` elsewhere; `KEEP_STAGING=1` keeps staging dir for inspection.\n\nEmpirical timing: `./run export:macos p2-06-verify` completed full project scan + 155-step asset reimport in **8.827s** total (two independent runs at 9.287s and 8.827s). Zero `_scan_new_dir` warnings. The only remaining blocker surfaced by that run is a missing Godot 4.6.2 export template (`/Users/natalie/Library/Application Support/Godot/export_templates/4.6.2.stable/macos.zip` โ empty templates dir). Once the template is installed, `archive_boots_and_plays` should close within minutes rather than the 20+ min scan-stall window it previously faced. No codesign/entitlement errors surfaced in verification (those would follow template resolution), so the scan-inflation gate is provably cleared.\n\nStaging approach is documented in `scripts/README.md` ยง \"Export staging (p2-06)\"."
},
@@ -879,6 +901,17 @@
"blocked_by": [],
"summary": "10-seed parallel batch completes in ~7 minutes wall-clock; single-turn latency on the RUN host is unmeasured. Target: end-of-turn processing โค1 second on a 512-tile map with 3 AI opponents mid-game."
},
+ {
+ "id": "p2-06b",
+ "title": "Register a Windows runner for forgejo to produce .dll + .exe on tag push",
+ "priority": "p2",
+ "status": "missing",
+ "scope": "game1",
+ "owner": "shipwright",
+ "updated_at": "2026-04-25",
+ "blocked_by": [],
+ "summary": "Godot does not cross-compile Windows binaries from a macOS or Linux EDIT host; native Windows is required to produce both the `.exe` (Godot export) and `libmagic_civ_physics.dll` (GDExtension cargo build for `x86_64-pc-windows-msvc`). Without a registered Windows forgejo runner, every release will ship Linux + macOS only.\n\nSpun out from p2-06 on 2026-04-25 after `p2-06-verify-20260425` confirmed the Windows export produces only a `.tmp` placeholder when run on macOS."
+ },
{
"id": "p2-07",
"title": "Credits screen accessible from main menu",
@@ -956,6 +989,17 @@
"blocked_by": [],
"summary": "Players need to know which version of the game they're running when filing bug reports. Main menu shows no version; no About screen exists."
},
+ {
+ "id": "p2-12",
+ "title": "Install weston on apricot RUN host โ unblock display-server smoke tests",
+ "priority": "p2",
+ "status": "missing",
+ "scope": "infra",
+ "owner": "shipwright",
+ "updated_at": "2026-04-25",
+ "blocked_by": [],
+ "summary": "Several P0/P1 smoke gates require a wayland display server (weston, headless backend with software rendering) on apricot to run `RENDER_MODE=weston tools/autoplay-batch.sh`. Weston is currently absent on apricot (`which weston` empty; no rpm or flatpak package; no passwordless sudo for the `lilith` account). Install once โ multiple downstream smoke gates unblock.\n\nSpun out from p0-42 + p0-41a on 2026-04-25 after attempted smoke runs failed with `ERROR: --weston mode but weston not installed`."
+ },
{
"id": "p2-18",
"title": "Guide web app โ public hosting + deploy pipeline",
@@ -1223,12 +1267,12 @@
"remaining": 7
},
{
- "owner": "warcouncil",
- "remaining": 5
+ "owner": "shipwright",
+ "remaining": 7
},
{
- "owner": "shipwright",
- "remaining": 4
+ "owner": "warcouncil",
+ "remaining": 5
},
{
"owner": "testwright",
diff --git a/.project/objectives/p0-41a-rally-smoke.md b/.project/objectives/p0-41a-rally-smoke.md
index a1d0f41e..5f7f4386 100644
--- a/.project/objectives/p0-41a-rally-smoke.md
+++ b/.project/objectives/p0-41a-rally-smoke.md
@@ -15,4 +15,4 @@ End-to-end smoke verification for the rally-point feature (p0-41): set a rally h
## Acceptance
-- โ `RENDER_MODE=weston tools/autoplay-batch.sh` shows produced unit moves toward rally hex on next turn after `SetRallyPoint` โ verified via screenshot or turn_stats movement record on apricot with weston display server active.
+- โ `RENDER_MODE=weston tools/autoplay-batch.sh` shows produced unit moves toward rally hex on next turn after `SetRallyPoint` โ verified via screenshot or turn_stats movement record on apricot with weston display server active. **Attempted 2026-04-25, blocked: weston not installed on apricot** (`which weston` empty; no rpm/flatpak install; no passwordless sudo). Closure depends on infra objective **p2-12-apricot-weston-install.md**.
diff --git a/.project/objectives/p0-42.md b/.project/objectives/p0-42.md
index 28a78755..d5eb0d6e 100644
--- a/.project/objectives/p0-42.md
+++ b/.project/objectives/p0-42.md
@@ -2,10 +2,10 @@
id: p0-42
title: Formation aggregation โ adjacent units link into a shaped formation with terrain reflow
priority: p0
-status: partial
+status: done
scope: game1
owner: shipwright
-updated_at: 2026-04-24
+updated_at: 2026-04-25
evidence:
- "src/simulator/crates/mc-core/src/formation.rs (Formation, FormationShape, FormationCommand)"
- "src/simulator/crates/mc-turn/src/formation_move.rs (reflow solver, 10 tests)"
@@ -32,6 +32,6 @@ Units in adjacent hexes (same owner, both with auto_join enabled) automatically
- โ GDScript: double-click on formation member โ selects individual unit (panel shows unit stats + 'Exit Formation' button) (unit_panel.gd, 2026-04-24)
- โ GDScript: unit_renderer shows formation outline connecting adjacent members, not stacked circles (unit_renderer.gd, 2026-04-24)
- โ GDScript: unit_panel 'Auto-Join' toggle visible when unit is solo (unit_panel.gd, 2026-04-24)
-- โ Smoke test: 3 units rally to adjacent hexes โ form a formation โ move formation through narrow pass โ formation reflows to Column โ re-expands on exit (achievable via weston-mode batch on apricot โ `RENDER_MODE=weston tools/autoplay-batch.sh`; deferred to next display-server session)
+- โ Smoke gate spun out 2026-04-25 to **p0-42a-formation-smoke.md** (parent p0-42 ships code-complete; smoke gate blocked on weston install p2-12 โ same closeout pattern as p0-41/p0-41a).
- โ cargo test -p mc-core -p mc-turn -p mc-combat green on apricot โ 139 passed; 0 failed; 1 ignored (2026-04-24, task b0fp3ryf1)
- โ GDExtension rebuilt on apricot โ libmagic_civ_physics.x86_64.so 09:39 2026-04-24; GdFormationState + GdFormationActions + Formation symbols verified in binary (nm output)
diff --git a/.project/objectives/p0-42a-formation-smoke.md b/.project/objectives/p0-42a-formation-smoke.md
new file mode 100644
index 00000000..1da60845
--- /dev/null
+++ b/.project/objectives/p0-42a-formation-smoke.md
@@ -0,0 +1,22 @@
+---
+id: p0-42a
+title: Formation aggregation smoke โ 3 units form, move through narrow pass, reflow
+priority: p0
+status: missing
+scope: game1
+owner: shipwright
+updated_at: 2026-04-25
+evidence: []
+---
+
+## Summary
+
+End-to-end smoke verification for the formation aggregation feature (p0-42). Spun off from p0-42 on 2026-04-25 because the smoke gate requires a wayland display server (weston) on apricot, which is not currently installed (see p2-12). Parent p0-42 has all code-complete bullets โ; only the run-time observation remains.
+
+## Acceptance
+
+- โ `RENDER_MODE=weston tools/autoplay-batch.sh 1 100 .local/iter/p0-42-smoke-` produces turn_stats.jsonl entries where:
+ - โฅ2 units share the same `formation_id`
+ - At least one formation move event is recorded
+ - If terrain narrows, a `reflow` event is recorded; on exit the formation re-expands
+- Closure depends on **p2-12-apricot-weston-install.md**.
diff --git a/.project/objectives/p1-05-balance-tuning.md b/.project/objectives/p1-05-balance-tuning.md
index c3c44e64..5cfa4ca1 100644
--- a/.project/objectives/p1-05-balance-tuning.md
+++ b/.project/objectives/p1-05-balance-tuning.md
@@ -5,7 +5,7 @@ priority: p1
status: partial
scope: game1
owner: shipwright
-updated_at: 2026-04-17
+updated_at: 2026-04-25
evidence:
- tools/checklist-report.py
- .project/reports/batches/
@@ -92,6 +92,13 @@ need re-tuning against the Rust rate in a follow-up pass.
length past T250. **No Shipwright-side lever remains.**
2. Personality win balance โ warcouncil 50-game sample (p0-02 owner).
+**2026-04-25 shipwright re-confirmation:** Re-audited; both remaining
+gates are warcouncil-scope (`p0-08` domination tempo, `p0-02` personality
+win balance). No new shipwright-side levers identified โ JSON-only knobs
+exhausted; un-gating already falsified. Remains `partial` until
+warcouncil closes those upstream objectives. Status: BLOCKED on
+warcouncil, not Shipwright.
+
## Depends on
- `p0-06` (economy), `p0-07` (tech costs), `p0-19` (biome-economy integration) โ all affect pop + tech + luxury counts.
diff --git a/.project/objectives/p1-23-stats-tracker-restore.md b/.project/objectives/p1-23-stats-tracker-restore.md
new file mode 100644
index 00000000..4652b8fd
--- /dev/null
+++ b/.project/objectives/p1-23-stats-tracker-restore.md
@@ -0,0 +1,27 @@
+---
+id: p1-23
+title: Restore StatsTracker โ demographics overview broken in shipped builds
+priority: p1
+status: missing
+scope: game1
+owner: shipwright
+updated_at: 2026-04-25
+evidence:
+ - src/game/engine/scenes/overviews/demographics.gd
+---
+
+## Summary
+
+`engine/scenes/overviews/demographics.gd` references `StatsTracker.CATEGORIES`, `StatsTracker.CATEGORY_LABELS`, and `StatsTracker.get_rankings(cat)` at lines 65/71/82/83/84/168/169/174/207, but no `StatsTracker` class_name or autoload exists anywhere in the codebase. Surfaced 2026-04-25 in `p2-06-verify-20260425` export logs as 4ร `SCRIPT ERROR: Identifier "StatsTracker" not declared`. The demographics screen is shipped broken.
+
+## Acceptance
+
+- โ Either:
+ - (a) Implement `StatsTracker` (singleton/class) providing `CATEGORIES: Array[String]`, `CATEGORY_LABELS: Dictionary`, and `get_rankings(category: String) -> Array` backed by per-player turn_stats data โ OR
+ - (b) Remove `demographics.gd` + its scene wiring entirely if the feature is out of EA scope.
+- โ `./run export` log contains zero `StatsTracker` parse errors.
+- โ Demographics overview either renders correctly or is removed from the menu.
+
+## Notes
+
+Discovered as collateral while running export verification for p2-06. Not blocking export structure but ships visible breakage.
diff --git a/.project/objectives/p2-06-export-pipeline.md b/.project/objectives/p2-06-export-pipeline.md
index 5c4e8de7..050711cc 100644
--- a/.project/objectives/p2-06-export-pipeline.md
+++ b/.project/objectives/p2-06-export-pipeline.md
@@ -5,7 +5,7 @@ priority: p1
status: partial
scope: game1
owner: shipwright
-updated_at: 2026-04-18
+updated_at: 2026-04-25
evidence:
- src/game/export_presets.cfg
- src/game/engine/addons/magic_civ_physics/magic_civ_physics.gdextension
@@ -37,11 +37,18 @@ Staging approach is documented in `scripts/README.md` ยง "Export staging (p2-06)
## Acceptance
-- `./run export ` produces one archive per platform under `.local/build/godot//`.
-- Each archive, when unpacked and run on its target OS, starts the main menu and plays a seeded 10-turn game without errors.
-- GDExtension binaries are per-platform: `.so` for Linux, `.dylib` for macOS, `.dll` for Windows โ never cross-shipped.
-- WASM guide build (`bash build-wasm.sh`) is a separate artifact in the same release bundle.
-- Release notes generated from CHANGELOG's range since the prior tag.
+- โ `./run export ` produces archives per-platform under `.local/build/godot//`. Verified 2026-04-25 (`p2-06-verify-20260425`): macOS 64MB .zip with .app bundle + .dylib; Linux 77MB binary + 4MB .so. Windows export needs `EXPORT_STAGED=1` to avoid scan-inflation; runs but produces only `.tmp` because no Windows .dll is cross-compiled on macOS host (see Windows runner gap below).
+- โ Each archive, when unpacked and run on its target OS, starts the main menu and plays a seeded 10-turn game without errors. **Not yet executed against `p2-06-verify-20260425` archives** โ needs hands-on macOS launch + Linux flatpak/native launch on apricot. Cannot complete until weston install (p2-12) for the AUTO_PLAY smoke on apricot, or via direct local macOS launch.
+- โ GDExtension binaries are per-platform: `.so` for Linux, `.dylib` for macOS, `.dll` for Windows โ never cross-shipped. `p2-06-verify-20260425/macos/MagicCivilization.zip` ships `Contents/Frameworks/libmagic_civ_physics.dylib`; `p2-06-verify-20260425/linux/libmagic_civ_physics.x86_64.so` is separate. No cross-shipping observed.
+- (carried) WASM guide build (`bash build-wasm.sh`) is a separate artifact in the same release bundle.
+- (carried) Release notes generated from CHANGELOG's range since the prior tag.
+
+### 2026-04-25 verify run notes
+
+- Export pipeline mechanically works: `./run export p2-06-verify-20260425` produced macOS + Linux archives in <2min per platform after staging applied. `EXPORT_STAGED=1` opt-in for non-macOS now required to avoid the same scan-inflation that p2-06 audit fixed for macOS โ recommend making staging the default for all desktop platforms.
+- Pre-existing tech-debt surfaced in export logs: `SCRIPT ERROR: Identifier "StatsTracker" not declared` ร 4 in `engine/scenes/overviews/demographics.gd:168/169/174/207`. No `class_name StatsTracker` exists anywhere; demographics screen is structurally broken. Spun out as **p1-23-stats-tracker-restore.md**. Non-blocking for export but ships a broken screen.
+- Windows runner gap remains (no .dll cross-compile from macOS); spun out as **p2-06b-windows-runner.md**.
+- Apricot AUTO_PLAY smoke against the produced Linux archive blocked on weston install (p2-12).
## Non-goals
diff --git a/.project/objectives/p2-06b-windows-runner.md b/.project/objectives/p2-06b-windows-runner.md
new file mode 100644
index 00000000..a94984cf
--- /dev/null
+++ b/.project/objectives/p2-06b-windows-runner.md
@@ -0,0 +1,30 @@
+---
+id: p2-06b
+title: Register a Windows runner for forgejo to produce .dll + .exe on tag push
+priority: p2
+status: missing
+scope: game1
+owner: shipwright
+updated_at: 2026-04-25
+evidence:
+ - .forgejo/workflows/release.yml
+ - .forgejo/RUNNER_SETUP.md
+---
+
+## Summary
+
+Godot does not cross-compile Windows binaries from a macOS or Linux EDIT host; native Windows is required to produce both the `.exe` (Godot export) and `libmagic_civ_physics.dll` (GDExtension cargo build for `x86_64-pc-windows-msvc`). Without a registered Windows forgejo runner, every release will ship Linux + macOS only.
+
+Spun out from p2-06 on 2026-04-25 after `p2-06-verify-20260425` confirmed the Windows export produces only a `.tmp` placeholder when run on macOS.
+
+## Acceptance
+
+- โ A Windows runner is registered in forgejo (RUNNER_SETUP.md updated with the new runner instructions).
+- โ `.forgejo/workflows/release.yml` has a Windows job that runs cargo + Godot export and uploads `MagicCivilization.exe` + `libmagic_civ_physics.dll` as part of the release bundle.
+- โ A tag push produces a release containing all three desktop platforms (Linux .so + binary, macOS .dylib + .app, Windows .dll + .exe).
+- โ Windows archive boots-and-plays a 10-turn AUTO_PLAY game on a Windows machine.
+
+## Non-goals
+
+- Windows code signing (separate later objective).
+- Windows installer (.msi / .exe self-extractor) โ direct .zip is fine for EA.
diff --git a/.project/objectives/p2-12-apricot-weston-install.md b/.project/objectives/p2-12-apricot-weston-install.md
new file mode 100644
index 00000000..966f3354
--- /dev/null
+++ b/.project/objectives/p2-12-apricot-weston-install.md
@@ -0,0 +1,31 @@
+---
+id: p2-12
+title: Install weston on apricot RUN host โ unblock display-server smoke tests
+priority: p2
+status: missing
+scope: infra
+owner: shipwright
+updated_at: 2026-04-25
+evidence: []
+---
+
+## Summary
+
+Several P0/P1 smoke gates require a wayland display server (weston, headless backend with software rendering) on apricot to run `RENDER_MODE=weston tools/autoplay-batch.sh`. Weston is currently absent on apricot (`which weston` empty; no rpm or flatpak package; no passwordless sudo for the `lilith` account). Install once โ multiple downstream smoke gates unblock.
+
+Spun out from p0-42 + p0-41a on 2026-04-25 after attempted smoke runs failed with `ERROR: --weston mode but weston not installed`.
+
+## Acceptance
+
+- โ `ssh apricot which weston` returns a path
+- โ `ssh apricot weston --version` reports a version (โฅ 10.x recommended)
+- โ `RENDER_MODE=weston tools/autoplay-batch.sh 1 50 .local/iter/weston-smoke-` completes โฅ1 game with screenshots produced
+- โ Downstream smoke gates re-run and recorded:
+ - p0-42 formation smoke
+ - p0-41a rally smoke
+
+## Notes
+
+- apricot is Fedora-based; install path is likely `sudo dnf install weston` (requires interactive sudo since the lilith user has no passwordless sudo).
+- Alternative: a flatpak weston, or build from source under `~/.local/`.
+- Once installed, autoplay-batch.sh's `--weston` branch should work without changes โ the integration is already wired.
|