diff --git a/.project/objectives/DASHBOARD_CATEGORIES.md b/.project/objectives/DASHBOARD_CATEGORIES.md index 77fb8084..0408dbf7 100644 --- a/.project/objectives/DASHBOARD_CATEGORIES.md +++ b/.project/objectives/DASHBOARD_CATEGORIES.md @@ -269,6 +269,7 @@ | [p2-38](p2-38-unit-audio-cues-stubs.md) | ✅ done | P2 | Unit audio_cues stub strings — selection/move/attack lines for the dwarven roster | [asset-audio](../team-leads/asset-audio.md) | 🟢 | | [p2-39](p2-39-chronicle-hall-phantom-unlock.md) | ✅ done | P2 | Resolve `chronicle_hall` phantom unlock in `chronicle_keeping` culture tech | — | 🟢 | | [p2-43](p2-43-culture-research-completion-event.md) | 🟡 partial | P2 | Culture research live-game pipeline — per-turn GDExt bridge + `culture_researched` emit | — | 🟢 | +| [p2-43a](p2-43a-rust-port-culture-pick.md) | 🔴 stub | P3 | Rail-1 port — `_pick_culture_tradition` → mc-ai::tactical::culture_pick | — | 🟢 | | [p2-44](p2-44-ai-promotion-selection.md) | 🟡 partial | P2 | AI promotion selection — auto-pick + emit unit_promoted for AI units | — | 🟢 | | [p2-45](p2-45-elimination-reconciliation.md) | ✅ done | P2 | Player elimination reconciliation — emit `player_eliminated` on every transition | — | 🟢 | | [p2-46](p2-46-past-games-archive-replay-viewer.md) | 🟡 partial | P2 | Past-games archive & replay viewer — `mc-replay` crate, on-disk archive, projection-based playback | [shipwright](../team-leads/shipwright.md) | 🟢 | @@ -311,6 +312,7 @@ | [p2-61](p2-61-observation-recording-gates-from-tech.md) | 🔴 stub | P2 | Bind mc-observation gate_bits to player tech state — recording gates per-field | [unassigned](../team-leads/unassigned.md) | 🟢 | | [p2-62](p2-62-procedural-unit-and-building-renderer.md) | ✅ done | P2 | Procedural unit/building renderer — alpha-only visual substitute | [asset-sprite](../team-leads/asset-sprite.md) | 🟢 | | [p2-63](p2-63-mc-flora-biome-substrate-migration.md) | 🔴 stub | P2 | mc-flora generation: migrate biome filter to substrate_climate-aware path | [unassigned](../team-leads/unassigned.md) | 🟢 | +| [p2-64](p2-64-apricot-async-batch-protocol.md) | 🟡 partial | P2 | Apricot async batch protocol — launch / status / fetch decoupling | [simulator-infra](../team-leads/simulator-infra.md) | 🟢 | | [p3-01](p3-01-courier-diplomacy.md) | ✅ done | P3 | Courier-gated diplomacy — open borders + shared maps via tech-tiered courier units | [envoy](../team-leads/envoy.md) | 🟢 | | [p3-03](p3-03-courier-route-resolver.md) | ✅ done | P3 | Courier route resolver — real hex pathfinding, per-tier movement, severable infrastructure | [envoy](../team-leads/envoy.md) | 🟢 | | [p3-04](p3-04-per-hex-improvement-layer.md) | ✅ done | P3 | Per-hex improvement layer in `mc-core` / `mc-turn` — anchor improvements at (col,row) | [envoy](../team-leads/envoy.md) | 🟢 | @@ -322,7 +324,7 @@ | [p3-06](p3-06-civic-anarchy-and-axis-switching.md) | 🟡 partial | P3 | Civic anarchy — 5-turn anarchy on axis switch | [unassigned](../team-leads/unassigned.md) | 🟢 | | [p3-07a](p3-07a-cv-wealth-and-authority-amplifier.md) | 🔴 stub | P3 | CV-of-wealth + Authority amplifier → inequality stat | [unassigned](../team-leads/unassigned.md) | 🔒 p3-05b | | [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) | 🔴 stub | P3 | Lair assault mode — enter-and-clear | [unassigned](../team-leads/unassigned.md) | 🟢 | +| [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) | 🔴 stub | P3 | Lair siege mode — multi-turn pressure from adjacent | [unassigned](../team-leads/unassigned.md) | 🔒 p3-10a | | [p3-10c](p3-10c-lair-raid-mode.md) | 🔴 stub | P3 | Lair raid mode — grab-and-exit | [unassigned](../team-leads/unassigned.md) | 🔒 p3-10a | | [p3-11](p3-11-pioneer-engineer-action-points.md) | 🟡 partial | P3 | Pioneer & Engineer action-point pool | [unassigned](../team-leads/unassigned.md) | 🟢 | diff --git a/.project/objectives/README.md b/.project/objectives/README.md index 44c739ad..6a04a28d 100644 --- a/.project/objectives/README.md +++ b/.project/objectives/README.md @@ -16,9 +16,9 @@ |---|---|---|---|---|---|---|---| | **P0** | 0 | 0 | 0 | 0 | 0 | 44 | 44 | | **P1** | 1 | 13 | 2 | 6 | 1 | 50 | 73 | -| **P2** | 0 | 11 | 11 | 0 | 6 | 58 | 86 | -| **P3 (oos)** | 0 | 6 | 10 | 0 | 21 | 5 | 42 | -| **total** | **1** | **30** | **23** | **6** | **28** | **157** | **245** | +| **P2** | 0 | 12 | 11 | 0 | 6 | 58 | 87 | +| **P3 (oos)** | 0 | 7 | 10 | 0 | 21 | 5 | 43 | +| **total** | **1** | **32** | **23** | **6** | **28** | **157** | **247** | @@ -29,7 +29,7 @@ | [unassigned](../team-leads/unassigned.md) | 26 | | [asset-sprite](../team-leads/asset-sprite.md) | 6 | | [shipwright](../team-leads/shipwright.md) | 5 | -| [simulator-infra](../team-leads/simulator-infra.md) | 3 | +| [simulator-infra](../team-leads/simulator-infra.md) | 4 | | [testwright](../team-leads/testwright.md) | 3 | | [combat-dev](../team-leads/combat-dev.md) | 2 | | [warcouncil](../team-leads/warcouncil.md) | 2 | @@ -79,7 +79,7 @@ |---|---|---|---|---|---|---| | [p2-10](p2-10-regression-ci-gate.md) | 🟡 partial | Automated regression CI gate on every push to main | — | [testwright](../team-leads/testwright.md) | 2026-05-04 | 🟢 unblocked | | [p2-18](p2-18-guide-public-deployment.md) | 🟡 partial | Guide web app — public hosting + deploy pipeline | — | — | 2026-04-17 | 🟢 unblocked | -| [p2-43](p2-43-culture-research-completion-event.md) | 🟡 partial | Culture research live-game pipeline — per-turn GDExt bridge + `culture_researched` emit | — | — | 2026-05-04 | 🟢 unblocked | +| [p2-43](p2-43-culture-research-completion-event.md) | 🟡 partial | Culture research live-game pipeline — per-turn GDExt bridge + `culture_researched` emit | — | — | 2026-05-05 | 🟢 unblocked | | [p2-44](p2-44-ai-promotion-selection.md) | 🟡 partial | AI promotion selection — auto-pick + emit unit_promoted for AI units | — | — | 2026-05-04 | 🟢 unblocked | | [p2-46](p2-46-past-games-archive-replay-viewer.md) | 🟡 partial | Past-games archive & replay viewer — `mc-replay` crate, on-disk archive, projection-based playback | — | [shipwright](../team-leads/shipwright.md) | 2026-05-05 | 🟢 unblocked | | [p2-47](p2-47-in-game-statistics-screens.md) | 🟡 partial | In-game statistics screens — Civ-style 5-tab modal (Demographics / Graphs / Rankings / Replay / Histories) | — | [shipwright](../team-leads/shipwright.md) | 2026-05-03 | 🟢 unblocked | @@ -88,6 +88,7 @@ | [p2-56c](p2-56c-master-grandmaster-auras.md) | 🟡 partial | Master / Grandmaster auras — adjacent-slot yield propagation | — | [unassigned](../team-leads/unassigned.md) | 2026-05-04 | 🟢 unblocked | | [p2-57a](p2-57a-typed-resource-stockpile.md) | 🟡 partial | Typed resource stockpile — raw vs processed taxonomy | — | [unassigned](../team-leads/unassigned.md) | 2026-05-04 | 🟢 unblocked | | [p2-58](p2-58-ambient-encounter-rolls.md) | 🟡 partial | Ambient encounter rolls per tile moved — fauna_density × ecology_tier | — | [unassigned](../team-leads/unassigned.md) | 2026-05-05 | 🟢 unblocked | +| [p2-64](p2-64-apricot-async-batch-protocol.md) | 🟡 partial | Apricot async batch protocol — launch / status / fetch decoupling | — | [simulator-infra](../team-leads/simulator-infra.md) | 2026-05-05 | 🟢 unblocked | | [p2-10k](p2-10k-gdlint-cleanup.md) | 🔴 stub | CI: fix 51 gdlint violations so Stage 3 is hard-green | — | [testwright](../team-leads/testwright.md) | 2026-05-04 | 🟢 unblocked | | [p2-10l](p2-10l-gut-regression-triage.md) | 🔴 stub | CI: fix 15 GUT regressions so Stage 5 is hard-green | — | [testwright](../team-leads/testwright.md) | 2026-05-04 | 🟢 unblocked | | [p2-55d](p2-55d-ai-ransom-decision-hook.md) | 🔴 stub | AI ransom accept/refuse hook in mc-turn start-of-turn | — | — | 2026-05-03 | 🟢 unblocked | diff --git a/.project/objectives/objectives.json b/.project/objectives/objectives.json index 5a4949ea..4a83a1b7 100644 --- a/.project/objectives/objectives.json +++ b/.project/objectives/objectives.json @@ -1,13 +1,13 @@ { - "generated_at": "2026-05-05T16:06:46Z", + "generated_at": "2026-05-05T18:13:24Z", "totals": { "done": 157, "in_progress": 1, - "partial": 30, + "partial": 32, "stub": 23, "missing": 6, "oos": 28, - "total": 245 + "total": 247 }, "objectives": [ { @@ -1760,7 +1760,7 @@ "priority": "p2", "status": "partial", "scope": "game1", - "updated_at": "2026-05-04", + "updated_at": "2026-05-05", "blocked_by": [], "summary": "`EventBus.culture_researched(tradition_id, player_index)` is defined and\n**every downstream consumer is wired** (AudioManager handler, manifest\nentry `culture_researched`, the asset shipped at\n`public/resources/audio/sfx/ui/culture_researched.ogg`). What's missing\nturned out to be deeper than the original framing of this objective: the\n**entire per-turn culture-research path doesn't run in the live game**.\n\n### Trace\n\n- `turn_manager.gd:246` calls `_process_culture(player, game_map)`\n- `turn_processor.gd:360 _process_culture` only handles **border\n expansion** via `city.process_culture_with_modifier()` — no\n tradition-research accumulator\n- `processor.rs:604 process_culture_research` (Rust mc-turn) **does**\n drive tradition completion via `mc_culture::CultureResearchResult`,\n but it lives in the bench / legacy-headless path, not in the\n GDScript-driven live-game per-turn\n- Tech has a Rust GDExt method `tech_web.process_research(player_dict,\n yields, mult) → {new_progress, new_researching, completed_tech}` that\n GDScript calls in `turn_processor.gd::_process_research` — **no\n equivalent exists for culture**\n\nSo in the playable game today: `culture_research_progress` never\nincrements, `researched_traditions` never grows, no completion event\never fires. `p1-28` shipped the UI and the data graph but not the\nruntime accumulator." }, @@ -2229,6 +2229,17 @@ "blocked_by": [], "summary": "Authored flora JSON files have migrated from a top-level `biomes: [...]`\narray to the `substrate_climate` ontology\n(see `p2-52-substrate-flora-cover-ontology-split.md`). The biome-filter\nloop in `mc-flora/src/generation.rs` was not updated, so the\n`AuthoredSpeciesFile.biomes` field is now empty for every authored file\nand the candidate-pool query returns nothing.\n\nTwo pre-existing regression tests in `mc-flora` document the gap:\n\n- `load_authored_returns_species_for_known_biome` (generation.rs:645)\n- `generate_flora_for_biome_more_species_with_authored_files`\n (generation.rs:695)\n\nBoth fail because the biome filter at `generation.rs:508-510` still\nchecks `raw.biomes.iter().any(|b| b == biome_id)` while the JSON now\nencodes biome eligibility through `substrate_climate` blocks the loader\ndoes not currently inspect." }, + { + "id": "p2-64", + "title": "Apricot async batch protocol — launch / status / fetch decoupling", + "priority": "p2", + "status": "partial", + "scope": "game1", + "owner": "simulator-infra", + "updated_at": "2026-05-05", + "blocked_by": [], + "summary": "" + }, { "id": "g2-01", "title": "Ley lines — Game 2 (Age of Kzzykt)", @@ -2446,6 +2457,16 @@ "blocked_by": [], "summary": "Persistent trade-route units (caravans, traders) that travel between owned cities OR between own-city and foreign-city, generate per-turn gold/resource yields tied to distance and city-pair characteristics, and can be plundered by enemy units. Distinct from p1-01's instantaneous luxury-for-gold trade modal." }, + { + "id": "p2-43a", + "title": "Rail-1 port — `_pick_culture_tradition` → mc-ai::tactical::culture_pick", + "priority": "p3", + "status": "stub", + "scope": "game1", + "updated_at": "2026-05-03", + "blocked_by": [], + "summary": "Phase A of `p2-43` landed the AI culture-tradition picker as GDScript in\n`auto_play.gd::_pick_culture_tradition`. This violates Rail-1 (Rust is\nthe simulation source of truth) and is filed here as the explicit\nport-back follow-up.\n\nMirror the shape of `mc-ai::tactical::pick_promotion`:\n\n- New module `src/simulator/crates/mc-ai/src/tactical/culture_pick.rs`\n with `pub fn pick_culture_tradition(state: &PlayerState, available: &[TraditionId], priors: &PersonalityPriors) -> Option`.\n- Extend `PersonalityPriors` (in `policy.rs`) with `culture_pillar_weights: BTreeMap` and a single\n `culture_cost_bias: f32` knob — no parallel structs, no stringly maps.\n- Bridge through `GdAiController::pick_culture_tradition(player_dict, available_array)` in\n `api-gdext/src/ai.rs` (alongside the existing promotion bridge).\n- Replace the `_pick_culture_tradition` body in `auto_play.gd` with a\n one-liner delegating to the bridge. Delete the local scoring code —\n Zero-Tech-Debt rail forbids leaving the GDScript shadow.\n- GUT test asserts the bridge returns the same id sequence the Phase A\n GDScript would have, using a fixed personality + tradition graph.\n- `cargo test -p mc-ai test_culture_pick_personality_weighting` green." + }, { "id": "p2-55f", "title": "Read ransom_offer_duration_turns from combat_balance.json", @@ -2596,10 +2617,10 @@ "id": "p3-10a", "title": "Lair assault mode — enter-and-clear", "priority": "p3", - "status": "stub", + "status": "partial", "scope": "game1", "owner": "unassigned", - "updated_at": "2026-05-03", + "updated_at": "2026-05-05", "blocked_by": [ "p0-17" ], @@ -2845,7 +2866,7 @@ }, { "owner": "simulator-infra", - "remaining": 3 + "remaining": 4 }, { "owner": "testwright", diff --git a/.project/objectives/p2-43a-rust-port-culture-pick.md b/.project/objectives/p2-43a-rust-port-culture-pick.md index e9996595..8b097463 100644 --- a/.project/objectives/p2-43a-rust-port-culture-pick.md +++ b/.project/objectives/p2-43a-rust-port-culture-pick.md @@ -2,7 +2,7 @@ id: p2-43a title: "Rail-1 port — `_pick_culture_tradition` → mc-ai::tactical::culture_pick" priority: p3 -status: open +status: stub scope: game1 updated_at: 2026-05-03 evidence: diff --git a/.project/objectives/p2-64-apricot-async-batch-protocol.md b/.project/objectives/p2-64-apricot-async-batch-protocol.md index 86a19ef6..e276d10c 100644 --- a/.project/objectives/p2-64-apricot-async-batch-protocol.md +++ b/.project/objectives/p2-64-apricot-async-batch-protocol.md @@ -2,15 +2,28 @@ id: p2-64 title: Apricot async batch protocol — launch / status / fetch decoupling priority: p2 -status: stub +status: partial scope: game1 -category: infra owner: simulator-infra -created: 2026-05-05 updated_at: 2026-05-05 +evidence: + - scripts/apricot-run.sh launch/status/fetch sub-modes + - scripts/apricot-async-smoke.sh + - tooling/claude/dot-claude/instructions/canonical-commands.md blocked_by: [] -follow_ups: [] --- +## p2-64 close-out (2026-05-05) + +Three new sub-modes added to `scripts/apricot-run.sh`: +- `launch ` — writes a per-stamp launcher.sh into `~/.cache/mc-batches//`, starts it under `systemd-run --user --collect --unit=mc-batch-`. Returns immediately with `STAMP=` on stdout. +- `status ` — single ssh `ConnectTimeout=5` probe with three lightweight `ls | wc -l`-style checks; emits one-line JSON (`state` ∈ `running|complete|failed|unreachable`). +- `fetch ` — `rsync -a --partial`; resumable across drops; exit 1 if not yet complete. +- Documentation in script header + canonical-commands.md (already committed `5a57a6ac6`). +- Smoke test at `scripts/apricot-async-smoke.sh`. + +**Status: partial.** Implementation landed; needs at least one real-batch validation run (the smoke test exists but a true intermittent-connectivity scenario hasn't been exercised yet). Close to done. + +Existing synchronous modes (`smoke`, `huge-map-5clan`, `ai-quality-baseline-pre-c`, etc.) keep working — `launch` is a wrapper, not a replacement. ## Context diff --git a/.project/objectives/p3-10a-lair-assault-mode.md b/.project/objectives/p3-10a-lair-assault-mode.md index a58a1c80..e58d16f3 100644 --- a/.project/objectives/p3-10a-lair-assault-mode.md +++ b/.project/objectives/p3-10a-lair-assault-mode.md @@ -1,16 +1,22 @@ --- id: p3-10a -title: "Lair assault mode — enter-and-clear" +title: Lair assault mode — enter-and-clear priority: p3 -status: stub +status: partial scope: game1 -category: combat owner: unassigned -created: 2026-05-03 -updated_at: 2026-05-03 +updated_at: 2026-05-05 +evidence: + - src/simulator/crates/mc-core/src/lair.rs + - src/simulator/crates/mc-combat/src/lair.rs + - mc-core lair.rs serde + ord + default tests blocked_by: [p0-17] -follow_ups: [] --- +## p3-10a close-out (2026-05-05) + +`mc_core::lair::LairCombatMode` typed enum landed (variants `Assault | Siege | Raid`, snake_case serde, derive `Default` = Assault for backward compat with existing lair-clear callers). `mc_combat::lair` accepts the mode parameter at 7 existing call sites; passing `Default::default()` preserves p0-17 behavior. Serde round-trip + Ord-consistency tests + default-mode test land in `mc-core::lair::tests`. + +**Status: partial.** Assault mode is the existing-behavior typed-default — no new combat resolution logic shipped (the existing path IS the assault). Siege (p3-10b) and Raid (p3-10c) implementations are the structural follow-ups; both would extend `mc_combat::lair::resolve_lair_combat` with mode-specific branches. ## Context