From 830c1605b6f92ecdf74290113ac037326878777d Mon Sep 17 00:00:00 2001 From: autocommit Date: Tue, 19 May 2026 00:27:21 -0700 Subject: [PATCH] =?UTF-8?q?docs(core):=20=F0=9F=93=9D=20document=20technic?= =?UTF-8?q?al=20audit=20findings=20and=20AI=20milestones=20in=20CHANGELOG.?= =?UTF-8?q?md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- .project/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.project/CHANGELOG.md b/.project/CHANGELOG.md index ad0b4b2f..111fdda4 100644 --- a/.project/CHANGELOG.md +++ b/.project/CHANGELOG.md @@ -192,6 +192,8 @@ The specific bullets citing canopy fields + weather_event records in `turn_stats 2026-04-18 p0-01 TECH-TREE AUDIT COMPLETE + p0-39 FILED (shipwright): warcouncil's session-close handoff asked for tech_web.json + research-cost audit to explain universal `peak_unit_tier=1` in T300 games. Audit finding: **tech tree is fine** (73 base techs, balanced cost curve T1 avg 20.7 → T10 322, 1500-sci budget reaches tier-3 comfortably). Empirical spot-check in seed from `apricot-20260418_062941`: `bronze_working` researched turn 72 (unlocks pikeman, tier-2), 53 techs by T300, zero pikemen built. Root cause isolated to `src/simulator/crates/mc-ai/src/tactical/production.rs:72-80` — the `ids` module hardcodes only tier-1 unit IDs (WARRIOR/WORKER/FOUNDER/WALLS/FORGE/CASTLE/MARKETPLACE/GRANARY), and `decide_production()` pulls exclusively from that list. Same gap blocks berserker / cavalry / ironwarden / forge_titan / mithril_vanguard. Telemetry is honest — it reports 1 because tier-1 is all that exists in live gameplay. Filed `p0-39-ai-tier-progression-unit-selection.md` as warcouncil-owned P0 stub with two candidate fix approaches (dynamic candidate generation vs. extend hardcoded list), acceptance bullets targeting median `peak_unit_tier ≥ 2` across 10-seed T300, regression test name locked. Blocks p0-01 / p0-22 / p0-08 per warcouncil's own gating. No code changes this session — the fix lives in warcouncil's mc-ai crate per Rail-1 scope boundaries; Shipwright's audit discharged the information need. [ref: p0-01, p0-39] +2026-05-18 p1-60 GUT VALIDATION + CLOSURE (simulator-infra): ran workstreams C and G headless via flatpak Godot 4.6.2 on EDIT host. C `test_vision_parity.gd` 5/5, G `test_fog_renderer_consumes_vision.gd` 8/8. Both required fixing a Godot 4.6 inner-class scoping bug — `class StubUnit extends UnitScript` fails to resolve outer-scope preloaded consts at parse time. Patched both my new C test and the pre-existing `test_fog_of_war_vision.gd` (which had the same bug latent, blocking 6 tests) to use `extends "res://engine/src/entities/unit.gd"` instead. Repaired test_fog_of_war_vision.gd now runs 6/6. Combined GUT fog suite: 32/32 across 4 files (test_fog_of_war 13, test_fog_of_war_vision 6, test_vision_parity 5, test_fog_renderer_consumes_vision 8). Objective p1-60 status flips `partial → done`; every acceptance bullet now ✓. **`./run verify` not green** but aborts at step 1 on pre-existing JSON-schema drift (`flora_cover_blends.json` missing `id`/`name`; `substrate.json` missing `movement_cost`/`defense_bonus`; `great_sculptor.json` gender enum) — unrelated to p1-60, filed for data owner. [ref: p1-60] + 2026-05-18 p1-60 FOLLOW-UPS H + I + J landed (simulator-infra): the wrap-mode, elevation-peak, and allied-vision follow-ups from p1-60's plan all landed in a single session against the producer crate. **H wrap-mode**: `WrapMode { None, Horizontal }` enum added to `GridState` (`mc-core/src/grid/mod.rs:418-449`, `#[serde(default)]` for back-compat); new `wrap_coord` helper in `mc-vision/src/lib.rs` normalises col modulo width when `Horizontal`; `tile_in_bounds` / `tile_at` route through it; `accumulate_visible_from` stores wrapped canonical coords in the visible set; LoS uses the raw goal coord so cube-line interpolation crosses the seam intact. **I elevation peak**: `VisionCatalog` gained `peak_elevation_threshold: f32 = 0.7`, `peak_sight_bonus: i32 = 0`, `peak_pierce_blockers: u32 = 0` (all `#[serde(default)]`, all default-off). When a unit stands on a tile with `elevation >= threshold`, vision uses `base + bonus` AND new `has_line_of_sight_with_pierce` ignores up to `pierce` intermediate blockers (see over the ridge). Default zero values preserve byte-equal pre-existing test behaviour. **J allied vision**: `GameState.alliances: BTreeSet<(u8, u8)>` (canonical `(min, max)` keying, mirrors `relations`), `#[serde(default)]`. New `apply_allied_vision` step in `compute_vision` unions `visible` and `explored` between every allied pair after individual refresh; `last_seen` is NOT shared (info-decay stays per-player). **Tests**: +9 in `mc-vision` (`wrap_horizontal_disk_crosses_seam`, `wrap_los_through_seam_respects_blockers`, `bounded_mode_unchanged_after_wrap_field_added`; `unit_on_peak_sees_over_one_mountain_ring`, `unit_on_plains_does_not_see_over_mountain`, `elevation_threshold_data_driven`; `allied_pair_shares_visible_set`, `non_allied_pair_does_not_share`, `breaking_alliance_drops_shared_vision_next_turn`). Final tally: mc-vision 29/29 (1 ignored Phase 2), mc-player-api 138/138 across 11 binaries, mc-save 10/10 + doctest, mc-turn 222/222 + 3/3 (one pre-existing `abstract_projection::five_players_overflow_truncates_to_max_players` failure from 2026-05-04 is orthogonal — doesn't touch alliances/wrap/vision). Workspace `cargo build --workspace` clean. **Pre-existing breakage repaired in passing**: `mc-turn/tests/event_collector_wiring.rs:222` exhaustive `match` over `TurnEvent` was missing the new `PlayerDiscovered` / `CitySpotted` / `UnitSpotted` Communications WIP variants — added them as labelled arms. With H+I+J merged, the p1-60 plan's "in-scope follow-ups" section is fully discharged; only "truly out of scope" (spell-revealed gates, Game 3 magic schools) remains. p1-60 objective stays `partial` until C and G GUT tests are run on RUN host (`./run gut` flips them ⏳ → ✓). [ref: p1-60] 2026-05-18 p1-60 FOG-OF-WAR FAIRNESS + COVERAGE landed (simulator-infra): closed a load-bearing gap where the headless AI consumed the raw `GameState` through `project_tactical(state, player)` and saw enemy units / cities / unexplored resources its human counterpart never would — invalidating any AI-vs-AI tournament for balance purposes. Workstreams A–G landed; H/I/J (wrap-mode, elevation peaks, allied vision) tracked as follow-ups. **Code**: new `project_tactical_with_vision(state, player, Option<&PlayerVision>)` in `mc-player-api/src/projection.rs:917-949` threads a vision arg through `_map` (resources stripped outside `explored`) and `_player` (enemy units/cities outside `visible` omitted; own slot always full). Production call sites switched: `dispatch.rs:540` (`drive_ai_slot`) and `api-gdext/src/ai.rs:260` (`decide_strategic_kind`) now compute `compute_vision` once per turn and pass the active player's `PlayerVision` to the new variant. `CP_OMNISCIENT` retained as debug-only escape hatch. The legacy 2-arg `project_tactical` stays as an omniscient compat wrapper so 12+ existing test fixtures don't churn. **Tests**: +23 across 4 crates — `mc-vision` 4 gap-fill tests (multi-unit unions, stale-snapshot freezing, two-blocker LoS, bounded-clip), `mc-player-api/tests/ai_fairness.rs` 6 tests (hidden-warrior-behind-mountain, scout-reveals, omniscient compat, enemy-city redaction, resources-on-unexplored stripped), `mc-player-api/tests/projection_redaction.rs` 6 tests (enemy unit/city/tile omission, stale tile semantics, omniscient flag preserved, default-path parity), `mc-save/tests/round_trip.rs` 2 tests (byte-equal vision JSON, back-compat default). Final tallies: mc-vision 21/21 (1 ignored Phase 2), mc-player-api 109/109, mc-save 10/10 + 1 doctest. **Save format**: `SaveFile.vision_state: Option` with `#[serde(default)]` — opaque JSON keeps `mc-save` decoupled from `mc-vision`'s dep graph. **Bench**: criterion bench at `mc-vision/benches/compute_vision.rs`, small_map 60×60×4p×8u measured at ~90 µs (~55× headroom on 5 ms target). **GUT**: `test_vision_parity.gd` (5 tests) + `test_fog_renderer_consumes_vision.gd` (8 tests, exercises real `fog_renderer.gd` headlessly) — files landed but require `./run gut` on RUN host to validate. **Side effect**: my workstream A `stale_snapshot_is_frozen_until_reobserved` test initially failed because `refresh_for_player` re-sampled the grid at the transition turn instead of preserving the last-visible snapshot — a real fog-of-war soundness bug. The Communications Phase 1 author landed a `PlayerVision.visible_snapshots` fix in parallel during this session and the test now passes. **Docs**: `docs/modding/ai-controller.md` gained a "Fog of war" section so mod authors know `TacticalState` arrives pre-filtered. **Pre-existing breakage repaired in passing**: Communications Phase 1 WIP had left `dispatch.rs:389` with a non-exhaustive `match ev` over the new `PlayerDiscovered` / `CitySpotted` / `UnitSpotted` `TurnEvent` variants — added them as drop-in no-ops at the existing "no wire counterpart" branch so the workspace builds. [ref: p1-60, p2-70, p0-13]