The effects→yield aggregation existed in two places: GDScript
(ai_turn_bridge_state.gd::build_building_catalog) and Rust
(mc_ai::tactical::parse_building_catalog). Both were byte-equivalent but a
duplicated transform that could drift. Per Rail 1 (simulation logic in Rust),
the GDScript copy is now retired.
- api-gdext: GdItemSystem gains `aggregate_building_catalog_json(raw)` — a thin
#[func] over parse_building_catalog that takes the raw authored building docs
and returns the aggregated Vec<TacticalBuildingSpec> JSON (reuses the existing
lightweight stateless bridge class — no new registered class, so no plum
class-cache churn).
- ai_turn_bridge_state.gd: build_building_catalog now marshals
DataLoader.get_data("buildings").values() to that method instead of summing the
effects[] array in GDScript. The ~80-line aggregation loop is deleted.
- parse_building_catalog: made resilient (skips malformed entries instead of
failing the whole catalog) to match the GDScript builder's has("id") filter.
Validation: cargo mc-ai building_catalog 4/4; rebuilt the aarch64 dylib; full
headless GUT 728 passing / 0 failing / 13 pending, including 2 new tests that
exercise the GDScript→Rust→GDScript round-trip (forge production→yield_production,
research→science, trade→gold) and the malformed-input empty-catalog path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>