diff --git a/.project/objectives/DASHBOARD_CATEGORIES.md b/.project/objectives/DASHBOARD_CATEGORIES.md index 773486a9..7efacf18 100644 --- a/.project/objectives/DASHBOARD_CATEGORIES.md +++ b/.project/objectives/DASHBOARD_CATEGORIES.md @@ -315,7 +315,7 @@ | [p2-55c](p2-55c-freepeople-capture.md) | 🔴 stub | P2 | Freepeople capture mechanics | [combat-dev](../team-leads/combat-dev.md) | 🔒 p2-55 | | [p2-55d](p2-55d-ai-ransom-decision-hook.md) | 🔴 stub | P2 | AI ransom accept/refuse hook in mc-turn start-of-turn | — | 🟢 | | [p2-55e](p2-55e-richer-ransom-events.md) | ✅ done | P2 | UnitRansomAccepted / UnitRansomExpired events on TurnResult | — | 🟢 | -| [p2-55f](p2-55f-ransom-duration-from-json.md) | 🔴 stub | P3 | Read ransom_offer_duration_turns from combat_balance.json | — | 🟢 | +| [p2-55f](p2-55f-ransom-duration-from-json.md) | 🟡 partial | P3 | Read ransom_offer_duration_turns from combat_balance.json | — | 🟢 | | [p2-56](p2-56-worker-categories-and-expertise-tiers.md) | 🟡 partial | P2 | Worker categories (Sustenance/Construction/Wealth) + 5-tier expertise + Master/Grandmaster auras + idle decay | [unassigned](../team-leads/unassigned.md) | 🟢 | | [p2-56a](p2-56a-worker-category-types.md) | ✅ done | P2 | Worker category types — Sustenance / Construction / Wealth taxonomy | [unassigned](../team-leads/unassigned.md) | 🟢 | | [p2-56b](p2-56b-expertise-tier-progression.md) | ✅ done | P2 | Expertise tier progression — 5-tier specialist XP ladder | [simulator-infra](../team-leads/simulator-infra.md) | 🟢 | diff --git a/.project/objectives/README.md b/.project/objectives/README.md index 2c051ac0..58ee33b5 100644 --- a/.project/objectives/README.md +++ b/.project/objectives/README.md @@ -17,8 +17,8 @@ | **P0** | 0 | 0 | 0 | 0 | 0 | 44 | 44 | | **P1** | 1 | 14 | 1 | 5 | 1 | 55 | 77 | | **P2** | 0 | 9 | 10 | 0 | 6 | 70 | 95 | -| **P3 (oos)** | 0 | 7 | 6 | 0 | 21 | 9 | 43 | -| **total** | **1** | **30** | **17** | **5** | **28** | **178** | **259** | +| **P3 (oos)** | 0 | 8 | 5 | 0 | 21 | 9 | 43 | +| **total** | **1** | **31** | **16** | **5** | **28** | **178** | **259** | diff --git a/.project/objectives/objectives.json b/.project/objectives/objectives.json index 599171a5..d48c1a79 100644 --- a/.project/objectives/objectives.json +++ b/.project/objectives/objectives.json @@ -1,10 +1,10 @@ { - "generated_at": "2026-05-09T16:25:09Z", + "generated_at": "2026-05-09T16:33:14Z", "totals": { "done": 178, "in_progress": 1, - "partial": 30, - "stub": 17, + "partial": 31, + "stub": 16, "missing": 5, "oos": 28, "total": 259 @@ -2609,7 +2609,7 @@ "id": "p2-55f", "title": "Read ransom_offer_duration_turns from combat_balance.json", "priority": "p3", - "status": "stub", + "status": "partial", "scope": "game1", "owner": "", "updated_at": "2026-05-03", diff --git a/.project/objectives/p2-55f-ransom-duration-from-json.md b/.project/objectives/p2-55f-ransom-duration-from-json.md index 0fb68e92..ced0c394 100644 --- a/.project/objectives/p2-55f-ransom-duration-from-json.md +++ b/.project/objectives/p2-55f-ransom-duration-from-json.md @@ -2,7 +2,7 @@ id: p2-55f title: "Read ransom_offer_duration_turns from combat_balance.json" priority: p3 -status: stub +status: partial scope: game1 category: combat owner: @@ -19,12 +19,12 @@ parent: p2-55 ## Acceptance criteria -- [ ] `mc-turn::CombatBalance` (or equivalent) struct gains `ransom_offer_duration_turns: u32`. -- [ ] DataLoader / GameState init reads `combat_balance.json` and populates the field at game start. -- [ ] `RansomQueue::push` and `RansomQueue::tick` accept the duration as a runtime parameter rather than reading the constant. +- [x] `mc-turn::CombatBalance` struct gains `ransom_offer_duration_turns: u32`. ✓ Authored at `src/simulator/crates/mc-turn/src/combat_balance.rs` with all 5 fields (`ransom_offer_duration_turns`, `default_ransom_multiplier`, `denial_value_factor`, `capture_civilian_xp_award`, `destroy_civilian_xp_award_multiplier`). Defaults match the prior hardcoded constants (3 / 2.0 / 0.5 / 25 / 0.5). `#[serde(default)]` on every field so save migration is no-op. +- [-] ◐ DataLoader / GameState init reads `combat_balance.json` and populates the field at game start. `load_combat_balance(json: &str) -> Result` parser shipped (`combat_balance.rs:75`); `GameState`-level field + init-time wiring still pending. +- [-] ◐ `RansomQueue::push` and `RansomQueue::tick` accept the duration as a runtime parameter rather than reading the constant. `push_with_duration` already accepts the duration explicitly (`ransom.rs:74`); the default `push` still falls through to the const. Switching `push` callers to thread `CombatBalance.ransom_offer_duration_turns` is the remaining wiring. - [ ] The `RANSOM_OFFER_DURATION_TURNS` const is removed (or kept as a fallback default for tests, with a comment explaining). - [ ] Test in `mc-turn/tests/ransom.rs` exercises a non-3 duration (e.g. 5 turns) to prove the value is plumbed end-to-end. -- [ ] Same treatment for `default_ransom_multiplier` and `denial_value_factor` — currently both are mc-ai constants with TODO markers; bringing all three under one objective keeps the load-config plumbing atomic. +- [x] Same treatment for `default_ransom_multiplier` and `denial_value_factor` — both fields exist on `CombatBalance` with serde defaults matching their pre-p2-55f values. Migration of the mc-ai constants to read from `CombatBalance` is the wiring step (same shape as bullets 2+3). 5/5 inline unit tests pass on apricot (`cargo test -p mc-turn --lib -- combat_balance`). ## Out of scope