docs(@projects/@magic-civilization): 🐺 p3-30 — record owner ruling (bridge) + bridge landed

Owner ruled the GdWildAiController bridge over an in-step substrate. Mark
acceptance #2 [~] (Rust bridge done 8696a48aa; GDScript rewire render-gated)
and spell out the remaining live-game rewire steps + the render/live-run host
needed for the parity proof.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Natalie 2026-06-27 07:35:17 -04:00
parent 8696a48aa0
commit acf57fd05f

View file

@ -54,14 +54,16 @@ GDScript decision system.
target-select, step-toward (chase), attack (iff adjacent, per the player-tactical convention),
leash-return, city-drift, and leashed roam, reusing the existing `Action` taxonomy + `mc_core`
hex helpers + `XorShift64`. Combat resolution stays in `mc_combat::wilds` (no formula drift).
- [ ] Driven from inside `mc_turn::TurnProcessor::step` as a turn phase (so the p3-29 swap needs
no wild carve-out), OR via a `GdWildAiController` bridge if it must stay engine-driven —
infra decides; default is in-`step`. **BLOCKED ON FORK DECISION (see Notes 2026-06-27):** the
headless `GameState` has NO roaming wild-unit substrate (units are implicit-owned per
`PlayerState::units`; no `owner == -1`; wilds are modeled as `npc_buildings` lairs +
`process_fauna_encounters`). "Drive in `step`" therefore requires first BUILDING a headless
roaming-wild substrate (spawn at lairs, store, persist) that partly duplicates the encounter
system — a real scope/architecture fork vs. the allowed `GdWildAiController` bridge path.
- [~] Driven via a **`GdWildAiController` bridge** — **OWNER RULED 2026-06-27: bridge, not the
in-`step` substrate** (the headless `GameState` has no roaming wild-unit substrate — units are
implicit-owned per `PlayerState::units`, no `owner == -1`; wilds are `npc_buildings` lairs +
`process_fauna_encounters` — so an in-`step` path would build a second wild model that duplicates
encounters). **Rust side DONE (3964e55b4):** `mc_ai::wild::decide_wild_actions_json` (JSON
`WildContextDto` contract) + `GdWildAiController` GDExtension shim (`set_rng_seed` +
`decide_actions → PackedStringArray`, the `GdAiController` envelope); cdylib links with the class
auto-registered. **Remaining (render-gated):** GDScript builds the DTO from the live game_map +
primary-layer units in `_process_wild_creatures` and dispatches the returned Actions via the
existing AI-action path.
- [ ] `wild_creature_ai.gd` DELETED (or reduced to a thin bridge with zero decision logic);
`_process_wild_creatures` becomes a no-op or bridge call. **(render-gated — live turn swap.)**
- [x] Determinism preserved — same seed + state → same wild actions (XorShift64). Covered by
@ -108,3 +110,19 @@ without building a second wild model headless. "Everything inside `step()`" is t
but a headless roamer substrate duplicating encounters is the kind of disabled/parallel system the
rails warn against. Surfaced to owner; integration (A vs B) + the `.gd` deletion are the remaining
work and are render/decision-gated.
**OWNER RULED B (2026-06-27).** Bridge built + verified headless (3964e55b4): the JSON contract
(`WildContextDto`) + `decide_wild_actions_json` helper in `mc_ai::wild` (16 tests, mc-ai 305/0) +
the `GdWildAiController` shim in `api-gdext/src/ai.rs` (cdylib links, class auto-registered). The
only remaining p3-30 work is the live-game GDScript rewire, which is render-gated:
1. `turn_processor.gd::_process_wild_creatures` builds the `WildContextDto` JSON from the live
`game_map` (passable = in-bounds ∧ ¬water ∧ ¬player-occupied) + the primary-layer `owner == -1`
units + lairs (`npc_buildings_all` minus village/ruin) + city positions + the `wilds` config.
2. Calls `GdWildAiController.decide_actions(json)`; applies the returned `MoveUnit`/`AttackTarget`
records via the existing AI-action dispatch (move along path / `CombatResolver.resolve`).
3. Deletes `wild_creature_ai.gd`'s decision logic (`_act`/`_find_attack_target`/`_roam`/etc.);
`spawn_initial_creatures` stays (start/worldgen path, not a per-turn decision).
4. Render-proof: a headless/live run showing wilds still guard lairs, attack units in radius, and
roam within leash (parity vs the pre-port baseline), + GUT green.
**Deferred: needs a render/live-run host** — shipping the live-turn rewire without the parity
proof would violate the Rail UI rule + phase gate, so it is NOT done blind.