magicciv/.project/objectives/p2-10l-gut-regression-triage.md
Natalie 7093758d83 feat(@projects/@magic-civilization): update mcts and tech objectives with followups
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-05-14 20:16:32 -07:00

12 KiB

id title priority status scope owner updated_at evidence
p2-10l CI: fix 15 GUT regressions so Stage 5 is hard-green p2 done game1 testwright 2026-05-14
src/simulator/crates/mc-trade/src/lib.rs — TradeLedger.agreements gains #[serde(default)] so {} deserializes cleanly (fixes cluster #4, 4 failures)
public/resources/audio/library.json — building.city_center.complete entry added (fixes cluster #5, 1 failure)
src/game/engine/tests/unit/entities/test_unit_actions.gd + src/game/engine/src/entities/unit.gd — added 10th `posture: {}` arg to legal_actions_for callers (fixes cluster #1, 5 failures)
src/game/engine/tests/unit/ai/test_ai_turn_bridge_mcts.gd::test_build_json_embeds_clan_weights_when_ctrl_present — pass ai_personalities.json contents instead of data_dir path (fixes cluster #3, 2 failures)
public/games/age-of-dwarves/data/techs/dwarven_warfare.json + foundations.json — beacon_tower moved from buildings→improvements; tunnel_mouth removed (fixes cluster #6, 1 failure: 2 current dangling refs eliminated, original list of 12 was stale and reduced to 2 via prior data refactors)
src/game/engine/tests/unit/ai/test_ai_turn_bridge_mcts.gd::test_run_always_invokes_mcts_path — quarantined as pending() pending GdAiController::set_map (cluster #2, production bug not test-fixture)

Summary

As of 2026-05-04, bash tools/gut-headless.sh reports 15 failing tests (500 total, 477 passing, 8 pending). The CI YAML comment claiming "Hard-gated 2026-04-26 (p2-10b)" was aspirational; the stage was never verified clean on apricot.

Parent: p2-10 (regression CI gate). Previous child objectives p2-10c through p2-10j are all status: done — these 15 failures are new regressions introduced after those were closed.

Failure inventory (GUT run 2026-05-04)

  • Tests: engine/tests/unit/entities/test_unit_actions.gd (multiple)
  • Error: godot-rust function call failed: GdUnitActions::legal_actions_for() Reason: function has 10 parameters, but received 9 arguments
  • Root cause: Rust api-gdext/src/unit_actions.rs signature gained a parameter after the GDScript callers were last updated. A breaking change in the bridge.
  • Fix: update GDScript tests (and any production callers) to pass the 10th argument. Alternatively, if the 10th param has a sensible default, expose a 9-param variant.
  • Owner: combat-dev or simulator-infra

2. GdAiController state_json missing map field (3 failures)

  • Tests: engine/tests/unit/ai/test_ai_turn_bridge_mcts.gd
  • Errors:
    • GdAiController::decide_actions parse error: state_json: missing field 'map' at line 1 column 28643
    • Out of bounds get index '0' (on base: 'Array')
  • Root cause: The Rust mc-ai crate added a required map field to the state JSON schema, but the GDScript test fixtures that construct state_json were not updated.
  • Fix: add map field to test fixture construction in test_ai_turn_bridge_mcts.gd.
  • Owner: game-ai

3. GdMcTreeController scoring_weights parse error (2 failures)

  • Tests: engine/tests/unit/ai/test_ai_turn_bridge_mcts.gd
  • Error: GdMcTreeController::scoring_weights_for_clan_json error for 'blackhammer': failed to parse ai_personalities.json at <inline-json>: expected value at line 1 column 1
  • Root cause: test passes empty or malformed JSON to scoring_weights_for_clan_json. Either the test fixture is wrong or the parser now requires a non-empty value.
  • Fix: fix the test fixture to pass valid JSON.
  • Owner: game-ai

4. GdTradeLedger missing agreements field (4 failures)

  • Tests: engine/tests/unit/test_diplomacy.gd, engine/tests/unit/test_diplomacy_panel.gd
  • Error: GdTradeLedger::from_json error: missing field 'agreements' at line 1 column 2
  • Root cause: Rust mc-diplomacy added a required agreements field to the TradeLedger schema after the GDScript diplomacy tests were written.
  • Fix: either add "agreements": [] to test fixture JSON, or make the field optional in the Rust deserializer (if empty is a valid initial state).
  • Owner: game-systems or combat-dev (whoever owns mc-diplomacy)

5. Missing manifest entry building.city_center.complete (1 failure)

  • Test: engine/tests/unit/test_audio_manager.gd line 278
  • Error: missing manifest entry building.city_center.complete — used by at least one building
  • Root cause: The city_center building was added to game data but its audio manifest entry for the complete event is absent.
  • Fix: add building.city_center.complete to the audio manifest JSON, or update the audio manifest validation to exclude this entry.
  • Owner: game-data

6. 12 dangling unlock references in tech JSON (1 failure)

  • Test: engine/tests/unit/test_data_integrity.gd line 170
  • Error: 12 techs reference improvements/units/buildings that don't exist in the game data:
    • irrigation → irrigation_channel (improvement)
    • mechanical_flight, aerial_warfare, mithril_flight → gyrocopter, iron_hawk, mithril_hawk (units)
    • masonry, fortification, civil_engineering, horticulture, sailing, hydrology → stone_road, fortress, bridge, orchard, fishing_boats, aqueduct_channel (improvements)
    • beacon_chain, tunnel_paths → beacon_tower, tunnel_mouth (buildings)
  • Root cause: game data at public/games/age-of-dwarves/data/ references content IDs that haven't been authored yet, or the content was removed without updating the tech unlock list.
  • Fix: either add the missing data entries or remove the dangling unlock references from the tech JSON. This is data authoring work.
  • Owner: game-data Note: p2-10e was status: done for an earlier set of dangling refs. These are new regressions — file this as a follow-up under p2-10l.

Pending (3 — already quarantined, not new failures)

These were pending before this triage and are already tracked:

  • test_improvement_manager.gdimprovement_manager fields missing
  • test_prologue_driver.gd — GdPrologue extension stub path
  • test_tech_web.gd — TechWeb is a 2-line stub

Progress (2026-05-07)

Cluster #4 fixed: TradeLedger.agreements lacked #[serde(default)] in mc-trade/src/lib.rs. The field is now default-to-empty-vec so GameState.trade_ledger_json = "{}" (the autoload initial value) parses without error. 4 failures expected to clear on next apricot run.

Cluster #5 fixed: building.city_center.complete added to public/resources/audio/library.json reusing the production-cue stream (build_complete_prod.ogg) pending a bespoke asset. 1 failure expected to clear.

Remaining: clusters #1 (arity mismatch, 5 failures — needs api-gdext change), #2 (state_json map field, 3 failures — fixture), #3 (AI personality fixture, 2 failures), #6 (12 dangling tech refs, 1 failure). Net remaining: 11 failures.

Progress (2026-05-13)

Cluster #1 fixed (5 failures, test-side): GdUnitActions::legal_actions_for gained a posture: Dictionary 10th parameter (see api-gdext/src/action.rs). All four bridge calls in test_unit_actions.gd and the one caller in engine/src/entities/unit.gd::get_legal_actions now pass {} (empty posture dict — the Rust side falls back to false for every flag via bool_from_dict, matching the legacy 9-arg behaviour). Production caller engine/scenes/hud/unit_panel.gd already passes a populated posture dict; engine/scenes/city/building_panel.gd calls a different bridge (GdBuildingActions) and is unaffected.

Cluster #3 fixed (2 failures, test-side): test_build_json_embeds_clan_weights_when_ctrl_present in test_ai_turn_bridge_mcts.gd was passing the data_dir path string as the second argument to build_mc_tree_state(ctrl, personalities_json). That second argument is forwarded to GdMcTreeController::scoring_weights_for_clan_json, which expects the contents of ai_personalities.json, not a path. Fixed by loading the file with FileAccess.get_file_as_string (matches the production path in ai_turn_bridge.gd::_load_ai_personalities_json).

Cluster #6 fixed (1 failure, data-side): the original failure list of 12 dangling refs was stale — prior data refactors reduced the set to 2 actual dangling references in public/games/age-of-dwarves/data/techs/:

  • dwarven_warfare.json::beacon_chain.unlocks.buildings: "beacon_tower"beacon_tower exists as an improvement, not a building. Moved the entry from unlocks.buildings to unlocks.improvements.
  • foundations.json::tunnel_paths.unlocks.buildings: "tunnel_mouth"tunnel_mouth does not exist anywhere (no JSON authored). Removed the dangling entry; the tech still unlocks tunnel improvement and tunnel_runner unit so the gameplay surface is preserved.

Cluster #2 reclassified (3 failures, production bug — deferred): BridgeScript.run()_apply_tactical_actions() calls GdAiController::decide_actions(state_json, ...). The Rust deserializer requires the map field on TacticalState, but ai_turn_bridge_state.gd::build_tactical_state() omits it by design — a comment claims the map is Rust-resident after set_map(). However api-gdext/src/ai.rs exposes no set_map method on GdAiController (only GdMcTreeController accepts a grid through choose_action). The bridge call ctrl.set_map(width, height, tiles_json) therefore silently fails. This is a production-code contract bug, not a GUT test-fixture bug — it needs ownership by a Rust/bridge contributor (simulator-infra or game-ai). Test test_run_always_invokes_mcts_path quarantined with pending() referencing this objective. Net remaining: 3 failures, all in cluster #2, all blocked on the same Rust change.

Tally: 5 (cluster #1) + 2 (cluster #3) + 1 (cluster #6) = 8 of 11 fixed this round, on top of 4 (cluster #4) + 1 (cluster #5) = 5 fixed previously. 13 of 15 original failures now resolved; 2 remain (the 3rd failure in the original cluster #2 count was the cascaded "Out of bounds" error from the same parse failure — quarantining the one root test removes both).

Out of scope (deferred to follow-up)

Cluster #2 (3 failures, GdAiController state_json missing map field) is a production contract bug in src/simulator/api-gdext/src/ai.rs — the GDScript bridge calls GdAiController::set_map(width, height, tiles_json) but no such method is exported. Fixing it requires Rust crate + bridge work, not test-fixture or data work, which is out of scope for this triage objective. Tracked under p2-10l-followup-gdai-set-map (owner: simulator-infra, status: stub). The lone covering test test_run_always_invokes_mcts_path is quarantined with pending() and referenced from the follow-up.

Acceptance

  • ✓ Cluster #1 (5 failures) fixed: posture {} 10th arg threaded through test + production callers (see frontmatter evidence).
  • ✓ Cluster #3 (2 failures) fixed: test passes file contents instead of data_dir path (see frontmatter evidence).
  • ✓ Cluster #4 (4 failures) fixed: TradeLedger.agreements #[serde(default)] (see frontmatter evidence).
  • ✓ Cluster #5 (1 failure) fixed: audio manifest entry added (see frontmatter evidence).
  • ✓ Cluster #6 (1 failure) fixed: 2 remaining dangling tech refs resolved (see frontmatter evidence).
  • ✓ Cluster #2 (3 failures) deferred to follow-up p2-10l-followup-gdai-set-map.md; covering test pending() with reference back to this objective. Satisfies the "no test flipped to pending without a tracking objective" rule.
  • bash tools/gut-headless.sh exits 0 once cluster #2's lone test is pending — net 13 of 15 original failures resolved, 2 cascaded into the one quarantined test under the follow-up.