Gap 3 — Equipment/crafting: verified the full craft→equip→combat path runs
headless and Rust-authoritative (orig bullet was stale at [ ]):
- PlayerAction::CraftEquipment → craft_equipment dispatch (materials gate +
consume strategic_ledger + equip), 2 tests
- recipe_phase ("recipe_refine") in END_OF_TURN_PHASES — passive crafting
economy refines raw→quality-tiered product every self-play turn, 1 test
- equip_combat_bonus reads boot-loaded item_combat at every combat site, 2 tests
- boot path: set_item_combat_json FFI ← headless harness _apply_item_combat
- MCTS AI not electing to craft = deliberate 9-kind GPU-rollout constraint,
not a missing system
Verified green: mc-turn + mc-player-api 557/0.
Gap 4 — Per-building queues: recorded verified assessment. Bench single-slot +
per-turn AI reselection is functionally equivalent to a FIFO build queue for the
self-play SIMULATION outcome; the multi-item queue is a live-game UI affordance
belonging to the p3-25/p3-29 projection arc. Owner scope call pending: does p3-26
require simulating a multi-item queue, or reclassify Gap 4 out of the headless bar.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.1 KiB
4.1 KiB
| id | title | priority | status | scope | category | owner | created | updated_at | blocked_by | follow_ups | related | |||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| p3-31 | Replay recording — live games archive a GameHistory (per-turn TurnSnapshot + events) on game-over | p3 | missing | game1-stretch | simulation | shipwright | 2026-06-27 | 2026-06-27 |
|
|
Context
The replay surface (p2-46) and the projected match-chronicle (standings ladder
- event feed via
GameHistory::standings_at, shipped 2026-06-26) both work — but only against synthetic fixtures. No real game is ever archived, so there is nothing real to replay.
Verified state (2026-06-27):
write_gameis called only fromGdReplayArchive::write_fixture(src/simulator/api-gdext/src/replay.rs). No production path archives a game.TurnSnapshotis pushed only inwrite_fixture(replay.rs:637). The live game never records a per-turn snapshot —mc-replay/src/snapshot.rsstill documents itself as "the schema, not the writer."TurnEvents are emitted inmc-turnduring headless turn resolution (p2-46+mc-replay-followup-unit-spawn-events: CityFounded/CityCaptured/ UnitKilled/UnitCreated/WonderBuilt/TechResearched land inTurnResult), but the live Godot turn loop (turn_manager.gd→AiTurnBridge) is a separate path; whether it populates aTurnEventCollectorand where those events would be archived is unwired.
The goal: a finished game — including an OBSERVER cast or an AI_ARENA match —
produces a GameHistory on disk so it can be re-watched and compared.
Source-of-truth rails
- Rust (
mc-replay+ the recorder) owns:TurnSnapshotderivation per clan per turn (population / cities / army_strength / gold / tech_count / land_area / score — already computed by the score + economy crates), event collection intoGameHistory.events, andwrite_gameon game-over. No stats math or archive I/O in GDScript (Rail-1). - GDScript only: fires the game-over trigger, hands the recorder the
per-turn hook, and reads the archive back via the existing
GdReplayArchive/GdReplayPlayerbridges (Rail-3). - JSON: archive-root path stays in
EnvConfig; retention policy in Settings (both already exist fromp2-46). No new tunables hardcoded (Rail-2).
Acceptance bullets
- A
GdGameRecorder(or equivalent) holds a sessionGameHistoryand, on a per-turn hook, appends oneTurnSnapshotper clan with the live stat values. After an N-turn headlessOBSERVER/AI_ARENAgame,history.snapshotshasN × clansrows. Projection (standings_at) is unchanged. - The live turn path collects the same
TurnEvents the headlessmc-turnpath already emits (CityFounded/CityCaptured/UnitKilled/UnitCreated/WonderBuilt/ TechResearched/WarDeclared/…) into the sessionGameHistory.events. (First determine whetherturn_manager.gd's path already drains a collector; wire it if not.) write_gameis invoked on game-over (victory and turn-limit), writingmeta.json+history.binunder<archive_root>/<pack>/<game-id>/.GdReplayArchive.list()returns the just-finished game;GameState'slast_archived_game_idis set for the end-game summary.end_game_summary.gd"Watch Replay" andpast_games.gdopen the freshly-recorded game and the chronicle (standings ladder + event feed) renders the real match, not a fixture. Proof screenshot reviewed in conversation.- Rust round-trip test: build a multi-turn
GameHistoryvia the recorder,write_game→read_game, assert snapshots + events survive andstandings_atreturns the recorded ladder.cargo test -p mc-replaygreen. - Determinism: same seed + same controller ids → byte-identical recorded snapshots + event-count vectors across two runs.
Out of scope
- Visual map/unit replay rendering — tracked by
p3-32(this objective only makes real games recordable; that one makes them watchable on a map). - Cloud sync of archives; replay branching / mid-game forking.
- Per-turn delta compression beyond bincode (the
p2-4610 MB cap assert still governs; optimise only if it fires).