fix(@projects/@magic-civilization): 🐛 update objective deadlines
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
93e53a7ddf
commit
d9e287d6b6
5 changed files with 38 additions and 27 deletions
|
|
@ -57,7 +57,7 @@
|
|||
| [p1-29](p1-29.md) | 🟡 partial | Anti-early-domination: lift game-balance gates that p0-01 v1 measured | balance, pacing | [combat-dev](../team-leads/combat-dev.md) | 2026-05-03 | 🟢 unblocked |
|
||||
| [p1-29a](p1-29a-last-stand-defense.md) | 🟡 partial | Last-stand defense — combat-strength multiplier when defender is at last city | balance, combat, pacing | [combat-dev](../team-leads/combat-dev.md) | 2026-05-04 | 🟢 unblocked |
|
||||
| [p1-38](p1-38-biome-economy-coupling.md) | 🟡 partial | Biome → economy coupling — population & luxury driven by live ecology | — | [shipwright](../team-leads/shipwright.md) | 2026-05-04 | 🟢 unblocked |
|
||||
| [p1-42](p1-42-ai-full-building-catalog.md) | 🟡 partial | AI must consider the full 155-building catalog, not the hardcoded 8-id ladder | — | — | 2026-05-04 | 🟢 unblocked |
|
||||
| [p1-42](p1-42-ai-full-building-catalog.md) | 🟡 partial | AI must consider the full 155-building catalog, not the hardcoded 8-id ladder | — | — | 2026-05-07 | 🟢 unblocked |
|
||||
| [p1-43](p1-43-building-stacking-upgrade.md) | 🟡 partial | Building stacking — per-category upgrade chains (military / science / culture / production / etc.) | — | — | 2026-05-07 | 🟢 unblocked |
|
||||
| [p1-44](p1-44-buildings-as-producers.md) | 🟡 partial | Buildings produce units, not the city center — per-building production queues | — | — | 2026-05-05 | 🟢 unblocked |
|
||||
| [p1-44c](p1-44c-buildings-as-producers-followups.md) | 🟡 partial | p1-44 follow-ups — UI, AI per-building emission, themed roster, GUT, batch | — | — | 2026-05-07 | 🟢 unblocked |
|
||||
|
|
@ -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-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-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-07 | 🟢 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 |
|
||||
| [p2-48](p2-48-end-of-game-summary-screen.md) | 🟡 partial | End-of-game summary screen — outcome banner, standings, score graph, awards, timeline, footer actions | — | [shipwright](../team-leads/shipwright.md) | 2026-05-03 | 🟢 unblocked |
|
||||
| [p2-55](p2-55-civilian-capture-system.md) | 🟡 partial | Civilian Capture / Destroy / Ransom | — | — | 2026-05-03 | 🟢 unblocked |
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"generated_at": "2026-05-07T06:35:10Z",
|
||||
"generated_at": "2026-05-07T06:57:45Z",
|
||||
"totals": {
|
||||
"done": 166,
|
||||
"in_progress": 1,
|
||||
|
|
@ -969,7 +969,7 @@
|
|||
"priority": "p1",
|
||||
"status": "partial",
|
||||
"scope": "game1",
|
||||
"updated_at": "2026-05-04",
|
||||
"updated_at": "2026-05-07",
|
||||
"blocked_by": [],
|
||||
"summary": "`mc-ai/tactical/production.rs::ids` hardcodes 8 building/unit IDs (`WARRIOR`, `WORKER`, `FOUNDER`, `WALLS`, `FORGE`, `CASTLE`, `MARKETPLACE`, `GRANARY`) and the priority ladder picks among them. The human player sees all 155 buildings in the city UI (`city_buildable_helper.gd` iterates `DataLoader.get_all_buildings()` and gates by `city.can_build()`); the AI's mental model is a ~5% slice of that catalog.\n\nResult: AI never builds `library`, `temple`, `colosseum`, `barracks`, `siege_workshop`, `harbor`, `aqueduct`, any of the 62 wonders, or any of the 32 military buildings beyond walls. Production decisions are silently flat across most of the tech tree.\n\nThe fix is to evaluate the full catalog the same way `pick_best_melee` evaluates units: filter by tech / race / resource gates, score by category × personality × city-state, return best. Catalog comes from the subscription manifest (`p1-41`) so the AI honors the same scope as the human."
|
||||
},
|
||||
|
|
@ -1833,7 +1833,7 @@
|
|||
"status": "partial",
|
||||
"scope": "game1-stretch",
|
||||
"owner": "shipwright",
|
||||
"updated_at": "2026-05-05",
|
||||
"updated_at": "2026-05-07",
|
||||
"blocked_by": [],
|
||||
"summary": "Persistent local archive of finished games, accessible from the main menu, with three surfaces:\n\n1. **Past Games index** — card grid (newest first), filters (outcome / map / version / date), per-card actions (Open Summary · Watch Replay · Rename · Export · Delete).\n2. **Replay viewer** — turn-by-turn playback against the live renderer, **projection-based not re-simulated** (reads pre-recorded snapshots + events), scrubber, speed selector, event ticker, optional stats overlay.\n3. **Compare view** — multi-select 2–4 games → overlapping score-graph + final-standings delta.\n\nFoundational for `p3-06` (statistics screens) and `p3-07` (end-of-game summary), both of which read the same `GameHistory` artefact this objective owns. **Ships first** of the three.\n\nDesign doc: [.project/designs/past-games-replays.md](../designs/past-games-replays.md)."
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ title: "AI must consider the full 155-building catalog, not the hardcoded 8-id l
|
|||
priority: p1
|
||||
status: partial
|
||||
scope: game1
|
||||
updated_at: 2026-05-04
|
||||
updated_at: 2026-05-07
|
||||
evidence:
|
||||
- "src/simulator/crates/mc-ai/src/tactical/state.rs:43-49 (building_catalog field)"
|
||||
- "src/simulator/crates/mc-ai/src/tactical/state.rs:368-487 (TacticalBuildingSpec + is_buildable)"
|
||||
|
|
@ -12,6 +12,7 @@ evidence:
|
|||
- "src/simulator/crates/mc-ai/src/tactical/production.rs:265-345 (catalog-driven Defense/BuildUp/Steady branches)"
|
||||
- "src/simulator/crates/mc-ai/src/tactical/production.rs:374-484 (pick_building_from_catalog + score_building)"
|
||||
- "src/simulator/crates/mc-ai/src/tactical/production.rs:1789-1933 (4 new catalog tests, all passing)"
|
||||
- "src/simulator/crates/mc-ai/src/tactical/production.rs::production_full_catalog (acceptance gate alias, cycle 42 — passes cargo test -p mc-ai -- production_full_catalog)"
|
||||
- "src/game/engine/src/modules/ai/ai_turn_bridge_state.gd:188-272 (build_building_catalog GDScript bridge)"
|
||||
---
|
||||
## Summary
|
||||
|
|
|
|||
|
|
@ -5,8 +5,11 @@ priority: p2
|
|||
status: partial
|
||||
scope: game1-stretch
|
||||
owner: shipwright
|
||||
updated_at: 2026-05-05
|
||||
updated_at: 2026-05-07
|
||||
evidence:
|
||||
- "src/simulator/crates/mc-replay/src/archive.rs:214 (write_game — bincode + meta.json, cycle 42)"
|
||||
- "src/simulator/crates/mc-replay/src/archive.rs:270 (read_game — schema + pack check, cycle 42)"
|
||||
- "src/simulator/crates/mc-replay/tests/archive_roundtrip.rs — 3 integration tests: round_trip, schema_mismatch, two_games_same_pack (cycle 42)"
|
||||
- "src/simulator/crates/mc-turn/Cargo.toml:18 (mc-replay dep)"
|
||||
- "src/simulator/crates/mc-turn/src/lib.rs:74 (re-export TurnEvent + TurnEventCollector)"
|
||||
- "src/simulator/crates/mc-replay/src/history.rs:122 (append_many helper)"
|
||||
|
|
@ -42,7 +45,7 @@ No tunable values are hardcoded. Retention policy (max archived games) lives in
|
|||
|
||||
- [x] **`mc-replay` crate scaffolded** with `GameHistory`, `TurnSnapshot`, `TurnEvent` (full enum from the design doc — `CityFounded`, `CityCaptured`, `UnitKilled`, `WonderBuilt`, `WarDeclared`, `PeaceSigned`, `EraEntered`, `LeaderChanged`, `ClanEliminated`, `TechResearched`), `TurnEventCollector`, and `GameOutcome`. Compiles cleanly with `cargo check -p mc-replay` and `cargo check --target wasm32-unknown-unknown -p mc-replay`.
|
||||
- [x] **`TurnEventCollector` resource** exposed from `mc-turn` so emitter crates (`mc-economy`, `mc-combat`, `mc-tech`) can append events during turn resolution; flushes into `GameHistory.events` at turn-end alongside the per-clan `TurnSnapshot` append. Per-turn append is wired into the existing turn-end pipeline. **(✓ 2026-05-05.)** `mc-turn` now depends on `mc-replay` (Cargo.toml:18) and re-exports `pub use mc_replay::{TurnEvent, TurnEventCollector}` (mc-turn/src/lib.rs:74). Per-step emission lands on `TurnResult::events_emitted` (mc-turn/src/combat_event.rs:165-194); the collector exposes `append_many` (mc-replay/src/history.rs:122-130) so callers drain `events_emitted` into the collector and then `flush_to_history`. Wired sites: `try_found_city` → `CityFounded` (mc-turn/src/processor.rs:1041-1051); `process_city_production` → `WonderBuilt` (mc-turn/src/processor.rs:957-965); `process_siege` capture loop → `CityCaptured` (mc-turn/src/processor.rs:2530-2540); `process_pvp_combat` kill loop → `UnitKilled` (mc-turn/src/processor.rs:2391-2422); `process_science` → `TechResearched` (mc-turn/src/processor.rs:797-805). Test gate: `mc-turn/tests/event_collector_wiring.rs` runs a 10-turn fixture and asserts every wired variant lands in `GameHistory.events` after `flush_to_history` (`cargo test -p mc-turn --test event_collector_wiring` passes 2/2). Deferred variants (no natural emission site in the headless bench yet — will land alongside the underlying mechanic): `WarDeclared` (only fires through the external `action_declare_war` diplomacy API, not `step()`), `PeaceSigned`, `LeaderChanged`, `ClanEliminated`, `EraEntered`. Documented in the `events_emitted` doc-comment.
|
||||
- [ ] **Bincode archive I/O** in `mc-replay/src/archive.rs`: `save(history: &GameHistory, path: &Path) -> Result<()>` writes `meta.json` + `history.bin` + `thumbnail.png`; `load(path: &Path) -> Result<GameHistory>` reads back. Round-trip test asserts `history == load(save(history))` for a non-trivial 50-turn fixture (≥3 clans, ≥1 of every `TurnEvent` variant, non-empty `final_state`).
|
||||
- [x] **Bincode archive I/O** in `mc-replay/src/archive.rs`: `write_game(root, history, title, written_at)` writes `meta.json` + `history.bin` (bincode); `read_game(root, expected_pack, game_id)` reads back with schema + pack validation. Round-trip test `archive_round_trip` in `tests/archive_roundtrip.rs` asserts `loaded == hist` for a 3-clan fixture with `TurnEvent`s; `schema_mismatch_returns_typed_error` pins `ArchiveError::SchemaMismatch`; `two_games_same_pack_list_correctly` verifies pack-subtree isolation. All 3 pass (`cargo test -p mc-replay --test archive_roundtrip`). Inline tests also cover pack mismatch and outcome JSON round-trip. (cycle 42)
|
||||
- [ ] **Per-pack archive subtree** — files land at `$XDG_DATA_HOME/magic-civilization/archive/<pack>/<game-id>/` where `<pack>` is `age-of-dwarves`. Test exercises that two games from the same pack land under the same subtree and listing returns both.
|
||||
- [ ] **Schema versioning** — `history_schema: u32` in `meta.json`. `load()` checks the constant `mc_replay::SCHEMA_VERSION`; mismatch returns `LoadError::SchemaMismatch { found, expected }` rather than panicking. Test exercises the mismatch path with a hand-written `meta.json`.
|
||||
- [ ] **Pack-version compat refusal** — replay surface checks `meta.pack_version` against `replay_compat.json`; incompatible majors return `LoadError::PackIncompatible` and the scene shows the "this replay is from a different version" warning with replay disabled (summary still works because it's projected from `final_state`). Test covers compat + incompat.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use mc_replay::archive::{
|
|||
MapDescriptor, HISTORY_SCHEMA_VERSION,
|
||||
};
|
||||
use mc_replay::history::{ClanDescriptor, GameHistory, TurnEventCollector};
|
||||
use mc_replay::ids::{ClanId, EraId, GameId, LeaderId, PackId, PackVersion, TechId};
|
||||
use mc_replay::ids::{ClanId, GameId, LeaderId, PackId, PackVersion, TechId};
|
||||
use mc_replay::event::TurnEvent;
|
||||
|
||||
/// Build a non-trivial `GameHistory` with ≥3 clans and representative events.
|
||||
|
|
@ -108,34 +108,41 @@ fn archive_round_trip() {
|
|||
}
|
||||
|
||||
/// Schema mismatch must return a typed `SchemaMismatch` error, not panic.
|
||||
///
|
||||
/// Strategy: write a valid archive, then overwrite `meta.json` with a version
|
||||
/// that has a bumped `history_schema`, so `read_game` trips the version check
|
||||
/// before it even touches `history.bin`.
|
||||
#[test]
|
||||
fn schema_mismatch_returns_typed_error() {
|
||||
use mc_replay::archive::ArchiveMeta;
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let pack = PackId("age-of-dwarves".into());
|
||||
let game_id = GameId::new_v4();
|
||||
let hist = make_history("age-of-dwarves", 42, 50);
|
||||
let game_id = hist.game_id;
|
||||
|
||||
// Forge a future-version meta.json by hand; leave history.bin with the
|
||||
// bumped schema so the double-check inside read_game also fires.
|
||||
let dir = game_dir(tmp.path(), &pack, game_id);
|
||||
fs::create_dir_all(&dir).unwrap();
|
||||
// Write a valid archive first so the directory and history.bin exist.
|
||||
write_game(tmp.path(), &hist, "Future Archive".into(), "2099-01-01T00:00:00Z".into())
|
||||
.expect("initial write must succeed");
|
||||
|
||||
// Overwrite meta.json with a bumped schema version.
|
||||
let future_schema = HISTORY_SCHEMA_VERSION + 1;
|
||||
let meta_json = serde_json::json!({
|
||||
"history_schema": future_schema,
|
||||
"game_id": game_id,
|
||||
"pack": { "0": "age-of-dwarves" },
|
||||
"pack_version": { "0": "99.0.0" },
|
||||
"title": "future archive",
|
||||
"final_turn": 0,
|
||||
"outcome": "InProgress",
|
||||
"written_at": "2099-01-01T00:00:00Z",
|
||||
});
|
||||
fs::write(dir.join("meta.json"), serde_json::to_vec(&meta_json).unwrap()).unwrap();
|
||||
let dir = game_dir(tmp.path(), &pack, game_id);
|
||||
let bad_meta = ArchiveMeta {
|
||||
history_schema: future_schema,
|
||||
game_id,
|
||||
pack: pack.clone(),
|
||||
pack_version: hist.pack_version.clone(),
|
||||
title: "Future Archive".into(),
|
||||
final_turn: hist.final_turn,
|
||||
outcome: hist.outcome.clone(),
|
||||
written_at: "2099-01-01T00:00:00Z".into(),
|
||||
};
|
||||
fs::write(dir.join("meta.json"), serde_json::to_vec_pretty(&bad_meta).unwrap()).unwrap();
|
||||
|
||||
match read_game(tmp.path(), &pack, game_id) {
|
||||
Err(ArchiveError::SchemaMismatch { on_disk, expected }) => {
|
||||
assert_eq!(on_disk, future_schema);
|
||||
assert_eq!(expected, HISTORY_SCHEMA_VERSION);
|
||||
assert_eq!(on_disk, future_schema, "on_disk schema must match what we wrote");
|
||||
assert_eq!(expected, HISTORY_SCHEMA_VERSION, "expected must be the current constant");
|
||||
}
|
||||
Err(other) => panic!("expected SchemaMismatch, got: {other}"),
|
||||
Ok(_) => panic!("expected error for schema mismatch, got Ok"),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue