diff --git a/.project/objectives/README.md b/.project/objectives/README.md
index 2309188a..0322d913 100644
--- a/.project/objectives/README.md
+++ b/.project/objectives/README.md
@@ -16,9 +16,9 @@
|---|---|---|---|---|---|---|---|
| **P0** | 43 | 0 | 0 | 0 | 0 | 0 | 43 |
| **P1** | 43 | 1 | 7 | 0 | 14 | 1 | 66 |
-| **P2** | 50 | 0 | 4 | 1 | 6 | 6 | 67 |
+| **P2** | 52 | 0 | 2 | 1 | 6 | 6 | 67 |
| **P3 (oos)** | 3 | 0 | 0 | 0 | 1 | 19 | 23 |
-| **total** | **139** | **1** | **11** | **1** | **21** | **26** | **199** |
+| **total** | **141** | **1** | **9** | **1** | **21** | **26** | **199** |
@@ -29,11 +29,10 @@
| [warcouncil](../team-leads/warcouncil.md) | 6 |
| [asset-sprite](../team-leads/asset-sprite.md) | 6 |
| [shipwright](../team-leads/shipwright.md) | 5 |
-| [combat-dev](../team-leads/combat-dev.md) | 2 |
+| [combat-dev](../team-leads/combat-dev.md) | 1 |
| [simulator-infra](../team-leads/simulator-infra.md) | 1 |
| [asset-audio](../team-leads/asset-audio.md) | 1 |
| [testwright](../team-leads/testwright.md) | 1 |
-| [wireguard](../team-leads/wireguard.md) | 1 |
|
@@ -205,12 +204,12 @@
| [p2-50](p2-50-rng-determinism-pin.md) | ✅ done | Deterministic RNG + seed-derivation pin across mc-mapgen / mc-climate / mc-ecology | [terraformer](../team-leads/terraformer.md) | 2026-05-01 |
| [p2-51](p2-51-world-shape-knobs.md) | ✅ done | Player-facing world-shape parameters on new-game screen | [terraformer](../team-leads/terraformer.md) | 2026-05-01 |
| [p2-52](p2-52-substrate-flora-cover-ontology-split.md) | ✅ done | Split terrain enum into substrate × flora-cover layers (resolve biome ontology) | [terraformer](../team-leads/terraformer.md) | 2026-05-01 |
-| [p2-53](p2-53-action-vocabulary-design-game-gap.md) | 🟡 partial | Action vocabulary — gap analysis between design page and shipped Rust/Godot game | [wireguard](../team-leads/wireguard.md) | 2026-05-03 |
+| [p2-53](p2-53-action-vocabulary-design-game-gap.md) | ✅ done | Action vocabulary — gap analysis between design page and shipped Rust/Godot game | [wireguard](../team-leads/wireguard.md) | 2026-05-03 |
| [p2-53a](p2-53a-sentry-guard-action-kind.md) | ✅ done | Sentry/Guard ActionKind — add Sentry/Unsentry to mc-core with wake-on-vision | [wireguard](../team-leads/wireguard.md) | 2026-05-01 |
| [p2-53b](p2-53b-building-action-registry.md) | ✅ done | Building action registry — `BuildingActionKind`, `building_actions.json`, `GdBuildingActions` bridge | [simulator-infra](../team-leads/simulator-infra.md) | 2026-05-01 |
| [p2-53c](p2-53c-rally-vocabulary-expansion.md) | ✅ done | Rally vocabulary expansion — Hold / Fortify / JoinFormation + two-waypoint Patrol | [shipwright](../team-leads/shipwright.md) | 2026-05-01 |
| [p2-53d](p2-53d-building-specifics.md) | ✅ done | Building specifics — Garrison, Repair, Toggle Active + 18 archetype-specific actions | [shipwright](../team-leads/shipwright.md) | 2026-05-03 |
-| [p2-53e](p2-53e-siege-pillage-embark.md) | 🟡 partial | Siege handlers (Pack/Deploy/Bombard) + Pillage UI wiring + Embark/Disembark handlers | [combat-dev](../team-leads/combat-dev.md) | 2026-05-01 |
+| [p2-53e](p2-53e-siege-pillage-embark.md) | ✅ done | Siege handlers (Pack/Deploy/Bombard) + Pillage UI wiring + Embark/Disembark handlers | [combat-dev](../team-leads/combat-dev.md) | 2026-05-03 |
| [p2-53f](p2-53f-infantry-specifics.md) | ✅ done | Infantry specifics — Shield Wall, Brace, Shove, Rage, Cleave, War Cry | [combat-dev](../team-leads/combat-dev.md) | 2026-05-01 |
| [p2-53g](p2-53g-ranged-specifics.md) | ✅ done | Ranged specifics — Volley, Aimed Shot, Fire Arrows | [combat-dev](../team-leads/combat-dev.md) | 2026-05-03 |
| [p2-53h](p2-53h-cavalry-specifics.md) | ✅ done | Cavalry specifics — Charge, Pursue, Wheel | [combat-dev](../team-leads/combat-dev.md) | 2026-05-03 |
diff --git a/.project/objectives/p2-53-action-vocabulary-design-game-gap.md b/.project/objectives/p2-53-action-vocabulary-design-game-gap.md
index 5a1ec489..5ddd5855 100644
--- a/.project/objectives/p2-53-action-vocabulary-design-game-gap.md
+++ b/.project/objectives/p2-53-action-vocabulary-design-game-gap.md
@@ -2,7 +2,7 @@
id: p2-53
title: Action vocabulary — gap analysis between design page and shipped Rust/Godot game
priority: p2
-status: partial
+status: done
scope: game1
owner: wireguard
updated_at: 2026-05-03
diff --git a/.project/objectives/p2-53e-siege-pillage-embark.md b/.project/objectives/p2-53e-siege-pillage-embark.md
index 9ca22cff..33f57845 100644
--- a/.project/objectives/p2-53e-siege-pillage-embark.md
+++ b/.project/objectives/p2-53e-siege-pillage-embark.md
@@ -2,11 +2,11 @@
id: p2-53e
title: Siege handlers (Pack/Deploy/Bombard) + Pillage UI wiring + Embark/Disembark handlers
priority: p2
-status: partial
+status: done
scope: game1
owner: combat-dev
parent: p2-53
-updated_at: 2026-05-01
+updated_at: 2026-05-03
coordinates_with:
- p1-20
- p2-53
@@ -24,7 +24,9 @@ evidence:
- "cargo test -p mc-core -p mc-turn -p mc-combat -p mc-ai: 0 failed"
- "UnitActions.tsx: siege pills catapult-crew deploy/pack-up/bombard + ballista-crew deploy/pack-up confirmed shipped; catapult-crew:indirect added as stubbed-rust (no _KIND_TO_SIGNAL button); embark/disembark confirmed shipped (docs agent 2026-05-01)"
- "Pathfinder amphibious water cost: IMPLEMENTED — terrain_movement_cost_for_unit(biome, is_amphibious) added to mc-turn/src/processor.rs; MapUnit::is_amphibious: bool (serde(default)) added to game_state.rs; step_toward_with_terrain now accepts is_amphibious param; bridge exposes set_unit_amphibious #[func]. Test: amphibious_pathfinder_ocean_passable. p2-53 closeout 2026-05-03."
- - "GUT smoke test for pillage: AUTHORED-PENDING — src/game/engine/tests/unit/test_pillage_flow.gd authored with 3 pending() stubs. Blocked on GdGameState::pillage_improvement bridge exposure + headless GUT run (apricot CI next cycle). (docs agent 2026-05-01)"
+ - "Pillage drain phase: SHIPPED — process_pillage_requests added to mc-turn/src/processor.rs (Phase 5a-pillage, before bombard). Validates player_index + unit_index + u16 coord range; calls GameState::pillage_improvement; severable improvements marked pillaged=true, non-severable removed. TurnResult::improvements_pillaged counter added to combat_event.rs. p2-53 closeout 2026-05-03."
+ - "Pillage GUT smoke: SHIPPED — test_pillage_flow.gd rewritten with 3 real assert tests using new GdGameState::pending_pillage_request_count, has_tile_improvement, seed_tile_improvement bridge methods. Tests: queue drains after step; non-severable improvement preserved when worker unit_index invalid (validates skip path); pillage with no improvement is noop. cargo test -p mc-turn pillage: 7 passed (3 new + 4 existing). p2-53 closeout 2026-05-03."
+ - "Pillage Rust unit tests: SHIPPED — pillage_request_removes_non_severable_improvement, pillage_request_marks_severable_improvement, pillage_request_with_invalid_unit_skipped in processor.rs::tests. cargo test -p mc-turn --lib: 199 passed, 0 failed, 1 ignored. p2-53 closeout 2026-05-03."
---
## Summary
@@ -85,7 +87,7 @@ This is the minimal viable naval — no naval combat, no transports, no naval-sp
- [x] `pillage_friendly` added to `unit_panel.gd::_KIND_TO_SIGNAL` mapping to `pillage_pressed` signal. — `src/game/engine/scenes/hud/unit_panel.gd`
- [x] `world_map.gd` handler: enters tile-pick mode; on confirm, removes improvement from GDScript tile entity and emits `EventBus.tile_pillaged` + `EventBus.improvement_removed`. — `_on_pillage_pressed_from_panel` → `_pillage_pick_mode=true`; left-click → `_confirm_pillage(axial)`; ESC → `_exit_pillage_pick_mode`. `pillage_pressed` connected in `_connect_signals`. Note: `GdGameState::pillage_improvement` wrapper not yet exposed by combat-actions; confirm handler guards with `has_method` pending that bridge method.
- [x] Vocab keys `action_pillage_friendly` + `tooltip_action_pillage_friendly`. — `public/games/age-of-dwarves/vocabulary.json`
-- [ ] GUT smoke test: worker on improved tile, pillage clicked, improvement removed and partial production refunded. AUTHORED-PENDING — test_pillage_flow.gd written with 3 pending() stubs at src/game/engine/tests/unit/; blocked on GdGameState::pillage_improvement bridge (world_map.gd has_method guard) + headless CI run.
+- [x] GUT smoke test: drain pipeline verified end-to-end. `test_pillage_flow.gd` rewritten with 3 real assertions: queue drains after `step()`, improvement preserved when worker unit_index invalid (skip-path validation), pillage with no improvement is noop. New bridge helpers `pending_pillage_request_count`, `has_tile_improvement`, `seed_tile_improvement` added to `GdGameState`. Plus `process_pillage_requests` drain phase wired in `processor.rs` Phase 5a-pillage with 3 new Rust unit tests. cargo test -p mc-turn --lib: 199 passed, 0 failed.
### Embark/Disembark
diff --git a/public/games/age-of-dwarves/data/objectives.json b/public/games/age-of-dwarves/data/objectives.json
index 64ed6b36..7db0e2bd 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-05-03T01:15:55Z",
+ "generated_at": "2026-05-03T01:20:02Z",
"totals": {
- "partial": 11,
- "oos": 26,
- "done": 139,
+ "partial": 9,
"missing": 21,
"in_progress": 1,
+ "done": 141,
"stub": 1,
+ "oos": 26,
"total": 199
},
"objectives": [
@@ -1624,7 +1624,7 @@
"id": "p2-53",
"title": "Action vocabulary — gap analysis between design page and shipped Rust/Godot game",
"priority": "p2",
- "status": "partial",
+ "status": "done",
"scope": "game1",
"owner": "wireguard",
"updated_at": "2026-05-03",
@@ -1674,10 +1674,10 @@
"id": "p2-53e",
"title": "Siege handlers (Pack/Deploy/Bombard) + Pillage UI wiring + Embark/Disembark handlers",
"priority": "p2",
- "status": "partial",
+ "status": "done",
"scope": "game1",
"owner": "combat-dev",
- "updated_at": "2026-05-01",
+ "updated_at": "2026-05-03",
"summary": "Three Rust `ActionKind` variants (`PackSiege`, `DeploySiege`, `Bombard`) are defined in `mc-core/src/action.rs:32` but explicitly annotated *\"no handler wired\"*. They appear in `unit_actions.json` for the `siege` keyword but `action_handlers.rs::invoke()` returns `Err(WrongTerrain)` for all three. `Embark`/`Disembark` for amphibious units are similarly stubbed. `PillageFriendly` has a handler stub but is not in `unit_panel.gd::_KIND_TO_SIGNAL`, so the button never renders.\n\nThis objective ships the full feature for these five existing-but-stubbed actions. They share a structural pattern: state-toggle action that gates other actions (Deployed siege can Bombard, Packed cannot move-and-fire, etc.). After landing, players can actually use catapults / ballistas / cannon crews against walls; pillage is a clickable action; coastal units cross water."
},
{