diff --git a/.project/objectives/DASHBOARD_CATEGORIES.md b/.project/objectives/DASHBOARD_CATEGORIES.md
index 395bbfdc..f0db16af 100644
--- a/.project/objectives/DASHBOARD_CATEGORIES.md
+++ b/.project/objectives/DASHBOARD_CATEGORIES.md
@@ -7,6 +7,7 @@
| ID | Status | Priority | Title | Owner | Blocked |
|---|---|---|---|---|---|
| [p0-43](p0-43.md) | ✅ done | P0 | Formation AI — MCTS plans at formation level, not per-unit | [warcouncil](../team-leads/warcouncil.md) | 🟢 |
+| [p1-29b](p1-29b-tier-gap-ai-quality.md) | 🔴 stub | P1 | AI tech tier gap — structural research path quality (low-pop AI fails to reach t1+) | [warmaster](../team-leads/warmaster.md) | 🟢 |
## balance
@@ -14,6 +15,7 @@
|---|---|---|---|---|---|
| [p1-29](p1-29.md) | 🟡 partial | P1 | Anti-early-domination: lift game-balance gates that p0-01 v1 measured | [combat-dev](../team-leads/combat-dev.md) | 🟢 |
| [p1-29a](p1-29a-last-stand-defense.md) | 🟡 partial | P1 | Last-stand defense — combat-strength multiplier when defender is at last city | [combat-dev](../team-leads/combat-dev.md) | 🟢 |
+| [p1-29b](p1-29b-tier-gap-ai-quality.md) | 🔴 stub | P1 | AI tech tier gap — structural research path quality (low-pop AI fails to reach t1+) | [warmaster](../team-leads/warmaster.md) | 🟢 |
## batch
@@ -82,6 +84,12 @@
|---|---|---|---|---|---|
| [p1-30](p1-30.md) | ✅ done | P1 | Optimize `_build_tactical_state` — 8000-tile GDScript dict-build per AI turn blocks p1-22 huge-map gate | [warcouncil](../team-leads/warcouncil.md) | 🟢 |
+## tech
+
+| ID | Status | Priority | Title | Owner | Blocked |
+|---|---|---|---|---|---|
+| [p1-29b](p1-29b-tier-gap-ai-quality.md) | 🔴 stub | P1 | AI tech tier gap — structural research path quality (low-pop AI fails to reach t1+) | [warmaster](../team-leads/warmaster.md) | 🟢 |
+
## tooling
| ID | Status | Priority | Title | Owner | Blocked |
@@ -335,7 +343,7 @@
| [p3-07b](p3-07b-four-damage-channels.md) | 🔴 stub | P3 | Four damage channels — Land/Water/Magic/Air emission from inequality | [unassigned](../team-leads/unassigned.md) | 🔒 p3-07a |
| [p3-10a](p3-10a-lair-assault-mode.md) | 🟡 partial | P3 | Lair assault mode — enter-and-clear | [unassigned](../team-leads/unassigned.md) | 🟢 |
| [p3-10b](p3-10b-lair-siege-mode.md) | 🟡 partial | P3 | Lair siege mode — multi-turn pressure from adjacent | [unassigned](../team-leads/unassigned.md) | 🔒 p3-10a |
-| [p3-10c](p3-10c-lair-raid-mode.md) | 🟡 partial | P3 | Lair raid mode — grab-and-exit | [combat-dev](../team-leads/combat-dev.md) | 🔒 p3-10a |
+| [p3-10c](p3-10c-lair-raid-mode.md) | ✅ done | P3 | Lair raid mode — grab-and-exit | [combat-dev](../team-leads/combat-dev.md) | 🟢 |
| [p3-11](p3-11-pioneer-engineer-action-points.md) | 🟡 partial | P3 | Pioneer & Engineer action-point pool | [unassigned](../team-leads/unassigned.md) | 🟢 |
| [p3-12](p3-12-fauna-stat-derivation-from-traits.md) | ✅ done | P3 | Fauna combat stat derivation — regenerate from traits | [terraformer](../team-leads/terraformer.md) | 🟢 |
| [p3-13a](p3-13a-extend-meteorological-events.md) | ✅ done | P3 | Extend meteorological events — drought, flood, dust_storm | [unassigned](../team-leads/unassigned.md) | 🟢 |
diff --git a/.project/objectives/DASHBOARD_COMPLETED.md b/.project/objectives/DASHBOARD_COMPLETED.md
index 7338d011..78ebd649 100644
--- a/.project/objectives/DASHBOARD_COMPLETED.md
+++ b/.project/objectives/DASHBOARD_COMPLETED.md
@@ -186,6 +186,7 @@
| [p3-01](p3-01-courier-diplomacy.md) | Courier-gated diplomacy — open borders + shared maps via tech-tiered courier units | — | [envoy](../team-leads/envoy.md) | 2026-04-29 |
| [p3-03](p3-03-courier-route-resolver.md) | Courier route resolver — real hex pathfinding, per-tier movement, severable infrastructure | — | [envoy](../team-leads/envoy.md) | 2026-04-28 |
| [p3-04](p3-04-per-hex-improvement-layer.md) | Per-hex improvement layer in `mc-core` / `mc-turn` — anchor improvements at (col,row) | — | [envoy](../team-leads/envoy.md) | 2026-04-28 |
+| [p3-10c](p3-10c-lair-raid-mode.md) | Lair raid mode — grab-and-exit | — | [combat-dev](../team-leads/combat-dev.md) | 2026-05-07 |
| [p3-12](p3-12-fauna-stat-derivation-from-traits.md) | Fauna combat stat derivation — regenerate from traits | — | [terraformer](../team-leads/terraformer.md) | 2026-05-04 |
| [p3-13a](p3-13a-extend-meteorological-events.md) | Extend meteorological events — drought, flood, dust_storm | — | [unassigned](../team-leads/unassigned.md) | 2026-05-04 |
diff --git a/.project/objectives/README.md b/.project/objectives/README.md
index 9e2d5a07..25198b9b 100644
--- a/.project/objectives/README.md
+++ b/.project/objectives/README.md
@@ -15,10 +15,10 @@
| Priority | 🔵 | 🟡 | 🔴 | ❌ | ⚫ | ✅ | Total |
|---|---|---|---|---|---|---|---|
| **P0** | 0 | 0 | 0 | 0 | 0 | 44 | 44 |
-| **P1** | 1 | 13 | 2 | 5 | 1 | 53 | 75 |
+| **P1** | 1 | 13 | 3 | 5 | 1 | 53 | 76 |
| **P2** | 0 | 9 | 14 | 0 | 6 | 65 | 94 |
-| **P3 (oos)** | 0 | 9 | 8 | 0 | 21 | 5 | 43 |
-| **total** | **1** | **31** | **24** | **5** | **28** | **167** | **256** |
+| **P3 (oos)** | 0 | 8 | 8 | 0 | 21 | 6 | 43 |
+| **total** | **1** | **30** | **25** | **5** | **28** | **168** | **257** |
@@ -27,13 +27,14 @@
| Team Lead | Remaining |
|---|---|
| [unassigned](../team-leads/unassigned.md) | 24 |
-| [combat-dev](../team-leads/combat-dev.md) | 7 |
| [asset-sprite](../team-leads/asset-sprite.md) | 6 |
+| [combat-dev](../team-leads/combat-dev.md) | 6 |
| [shipwright](../team-leads/shipwright.md) | 5 |
| [simulator-infra](../team-leads/simulator-infra.md) | 3 |
| [testwright](../team-leads/testwright.md) | 3 |
| [warcouncil](../team-leads/warcouncil.md) | 2 |
| [asset-audio](../team-leads/asset-audio.md) | 1 |
+| [warmaster](../team-leads/warmaster.md) | 1 |
|
@@ -64,6 +65,7 @@
| [p1-55](p1-55-tech-culture-domain-propagation.md) | 🟡 partial | Tech & Culture domain field — propagate categorization through Rust, Godot UI, and player analysis | — | [simulator-infra](../team-leads/simulator-infra.md) | 2026-05-07 | 🟢 unblocked |
| [p1-56](p1-56-civics-buildings-and-great-works.md) | 🟡 partial | Civics buildings, Great Works, Specialists, Great People — wire authored data into Rust + Godot | — | [simulator-infra](../team-leads/simulator-infra.md) | 2026-05-07 | 🟢 unblocked |
| [p2-22](p2-22-sprite-generation-pipeline.md) | 🟡 partial | Sprite generation pipeline — runnable end-to-end | — | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-25 | 🟢 unblocked |
+| [p1-29b](p1-29b-tier-gap-ai-quality.md) | 🔴 stub | AI tech tier gap — structural research path quality (low-pop AI fails to reach t1+) | ai, balance, tech | [warmaster](../team-leads/warmaster.md) | 2026-05-07 | 🟢 unblocked |
| [p1-43c](p1-43c-chain-ladders-and-ui.md) | 🔴 stub | p1-43 follow-ups — chain ladder authoring, AI stack scoring, city UI upgrade surface, GUT bridge test | — | — | 2026-05-07 | 🟢 unblocked |
| [p1-57](p1-57-diplomacy-tribute-treaties.md) | 🔴 stub | Diplomacy: tribute, treaty lifecycle, magical-terrain episode gating | — | [unassigned](../team-leads/unassigned.md) | 2026-05-03 | 🟢 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 |
diff --git a/.project/objectives/objectives.json b/.project/objectives/objectives.json
index 12a6fd1f..3f03ee75 100644
--- a/.project/objectives/objectives.json
+++ b/.project/objectives/objectives.json
@@ -1,13 +1,13 @@
{
- "generated_at": "2026-05-07T09:03:36Z",
+ "generated_at": "2026-05-07T09:32:24Z",
"totals": {
- "done": 167,
+ "done": 168,
"in_progress": 1,
- "partial": 31,
- "stub": 24,
+ "partial": 30,
+ "stub": 25,
"missing": 5,
"oos": 28,
- "total": 256
+ "total": 257
},
"objectives": [
{
@@ -836,6 +836,17 @@
"blocked_by": [],
"summary": "Filed by p1-29 cycle 5 close-out as the combat-side intervention that should close p1-29's `tier_peak_gap ≤4` gate. Three consecutive cycles of research-side levers (catch-up tech-pick mult, catch-up tech-output mult, loss-tolerance lever) landed durably but failed to move the gate across three batches. The failure is structural: p1 (the losing AI) loses cities faster than research output can unlock era-2+ techs. Research-side levers multiply a tiny base into a tiny base. The gate is a **territory problem**, not a research problem.\n\nThis objective addresses the territory problem by giving the defender (when reduced to their last city) a combat-strength bonus that scales with how many cities they've lost — buying enough turns for the existing research-side levers to finally fire and unlock era-2+ techs."
},
+ {
+ "id": "p1-29b",
+ "title": "AI tech tier gap — structural research path quality (low-pop AI fails to reach t1+)",
+ "priority": "p1",
+ "status": "stub",
+ "scope": "game1",
+ "owner": "warmaster",
+ "updated_at": "2026-05-07",
+ "blocked_by": [],
+ "summary": "Filed by cycle 47 close-out as the structural root cause for the alive-aware gate failure in p1-29a. The `tier_peak_gap ≤ 4` gate (across ALL living players at game end) failed 1/10 seeds in the cycle-45 batch even after p1-29a's last-stand multiplier landed. The failure is structural: the MCTS evaluator, rollout scorer, and tactical production allocator all apply flat weights that make sole-city players deprioritize research relative to defense and expansion. Tuning multipliers on a compound that starts near zero does not close the gap."
+ },
{
"id": "p1-30",
"title": "Optimize `_build_tactical_state` — 8000-tile GDScript dict-build per AI turn blocks p1-22 huge-map gate",
@@ -2746,10 +2757,10 @@
"id": "p3-10c",
"title": "Lair raid mode — grab-and-exit",
"priority": "p3",
- "status": "partial",
+ "status": "done",
"scope": "game1",
"owner": "combat-dev",
- "updated_at": "2026-05-05",
+ "updated_at": "2026-05-07",
"blocked_by": [
"p3-10a"
],
@@ -2978,11 +2989,11 @@
"remaining": 24
},
{
- "owner": "combat-dev",
- "remaining": 7
+ "owner": "asset-sprite",
+ "remaining": 6
},
{
- "owner": "asset-sprite",
+ "owner": "combat-dev",
"remaining": 6
},
{
@@ -3004,6 +3015,10 @@
{
"owner": "asset-audio",
"remaining": 1
+ },
+ {
+ "owner": "warmaster",
+ "remaining": 1
}
]
}
diff --git a/.project/objectives/p3-10c-lair-raid-mode.md b/.project/objectives/p3-10c-lair-raid-mode.md
index c6066379..41285277 100644
--- a/.project/objectives/p3-10c-lair-raid-mode.md
+++ b/.project/objectives/p3-10c-lair-raid-mode.md
@@ -2,16 +2,20 @@
id: p3-10c
title: Lair raid mode — grab-and-exit
priority: p3
-status: partial
+status: done
scope: game1
owner: combat-dev
-updated_at: 2026-05-05
+updated_at: 2026-05-07
evidence:
- "src/simulator/crates/mc-combat/src/lair.rs::resolve_raid"
- src/simulator/crates/mc-combat/tests/lair_raid.rs (4 tests passed)
- "src/simulator/crates/mc-core/src/lair.rs:223 (RaidOutcome enum)"
- public/games/age-of-dwarves/data/balance/lair_combat.json
- public/resources/ecology/fauna/lair_combat_modes.json (raid_loot_fraction added)
+ - "src/simulator/api-gdext/src/lib.rs::GdTurnProcessor::resolve_raid_on_lair (cycle 47)"
+ - "src/simulator/api-gdext/src/lib.rs::GdTurnProcessor::tick_raid_aftermath (cycle 47)"
+ - "src/simulator/crates/mc-turn/src/game_state.rs::GameState.raid_aftermath field (cycle 47)"
+ - "cargo check --workspace: Finished dev profile, 0 errors (cycle 47)"
blocked_by: [p3-10a]
---
## Context
@@ -24,7 +28,7 @@ Per `public/games/age-of-dwarves/docs/combat/LAIRS.md`, Raid mode is a single-tu
- ✓ Loot fraction per lair from `RaidTuning::raid_loot_fraction` (default 0.30), authored in `public/games/age-of-dwarves/data/balance/lair_combat.json::raid.raid_loot_fraction` and `public/resources/ecology/fauna/lair_combat_modes.json::combat_modes[raid].raid_loot_fraction`.
- ✓ Lair retaliation: on `Caught`, `RaidResult.pursuit` returns a `RaidAftermath` with `pursuing_until_turn = current_turn + tuning.pursuit_turns`. Verified by `test_raid_repulsed_when_attacker_loses` in `src/simulator/crates/mc-combat/tests/lair_raid.rs`.
- ✓ `cargo test -p mc-combat --test lair_raid` green: 4 passed (`test_raid_partial_loot_on_success`, `test_raid_lair_persists`, `test_raid_attacker_keeps_movement`, `test_raid_repulsed_when_attacker_loses`).
-- ❌ GDExt: `GdLair::raid(stack_id) -> Dictionary` — Rust core ships, bridge wrapper is the remaining work for a `done` flip (see follow-ups).
+- ✓ GDExt bridge: `GdTurnProcessor::resolve_raid_on_lair(state, lair_col, lair_row, attacker_hp, attacker_attack, lair_tier, lair_counter_attack, attacker_id, turn_seed) -> Dictionary` at `src/simulator/api-gdext/src/lib.rs`. Returns `{outcome, loot_amount, escaped, raider_damage, pursuing_until_turn}`. On `Caught`, writes `RaidAftermath` into `state.raid_aftermath[(col,row)]`. `tick_raid_aftermath(state)` expiry-prunes the map each turn. `mc-turn::GameState.raid_aftermath: BTreeMap<(u16,u16), RaidAftermath>` added with `raid_aftermath_as_pairs` serde adapter (mirrors `siege_pressure` pattern). `cargo check --workspace` clean (cycle 47).
## Source-of-truth rails