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 |
|
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)
1. GdUnitActions::legal_actions_for() arity mismatch (5 failures)
- 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.rssignature 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 28643Out of bounds get index '0' (on base: 'Array')
- Root cause: The Rust
mc-aicrate added a requiredmapfield to the state JSON schema, but the GDScript test fixtures that construct state_json were not updated. - Fix: add
mapfield to test fixture construction intest_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-diplomacyadded a requiredagreementsfield 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.gdline 278 - Error:
missing manifest entry building.city_center.complete — used by at least one building - Root cause: The
city_centerbuilding was added to game data but its audio manifest entry for thecompleteevent is absent. - Fix: add
building.city_center.completeto 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.gdline 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: donefor 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.gd—improvement_managerfields missingtest_prologue_driver.gd— GdPrologue extension stub pathtest_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_towerexists as an improvement, not a building. Moved the entry fromunlocks.buildingstounlocks.improvements.foundations.json::tunnel_paths.unlocks.buildings: "tunnel_mouth"—tunnel_mouthdoes not exist anywhere (no JSON authored). Removed the dangling entry; the tech still unlockstunnelimprovement andtunnel_runnerunit 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 frontmatterevidence). - ✓ 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 frontmatterevidence). - ✓ 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 testpending()with reference back to this objective. Satisfies the "no test flipped to pending without a tracking objective" rule. - ✓
bash tools/gut-headless.shexits 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.