Spaces:
Running
Running
| # Engine follow-ups discovered during Wave 12+13 per-scenario map redesign | |
| Issues that fall outside the per-scenario-map work but were surfaced by | |
| the agents while doing it. Each item is a discrepancy between what the | |
| bench can EXPRESS (the scenario spec / capability description) and what | |
| the engine actually DOES. Most have working pack-side workarounds; a few | |
| genuinely block a capability from being tested cleanly. | |
| Filed 2026-05-23 from Wave 12 + Wave 13 agent reports. | |
| --- | |
| ## 1. Tick-rate discrepancy (medium severity) โ RESOLVED (doc fix) | |
| **Finding** (def-position-revealed-direction agent, Wave 13) | |
| CLAUDE.md said "engine advances ~90 ticks per decision turn". | |
| Empirically measured under interrupt mode: **~64 ticks/turn**, not 90. | |
| **Resolution (2026-05-23, post-merge):** The engine constant is | |
| `DEFAULT_TICKS_PER_STEP = 30` (`OpenRA-Rust/openra-train/src/env.rs:33`). | |
| Non-interrupt mode advances exactly 30 ticks per `env.step()`; | |
| interrupt mode (`step_until_event`) advances 1โ`max_ticks` ticks per | |
| turn, variable per call. `OpenRA-Bench/CLAUDE.md` updated in two | |
| places โ the "~90 ticks/turn" estimate is gone; replaced with the | |
| actual constant + a note to read `info["ticks_advanced"]` instead of | |
| calculating arithmetically. See `docs/ENGINE_FOLLOWUPS_TRIAGE.md` | |
| finding #1. | |
| **Impact (resolved)**: per-pack `within_ticks` / `after_ticks` deadlines | |
| tuned via the bogus `tick โค 93 + 90ยท(max_turns โ 1)` formula may bite | |
| LATER than intended (silent draw-degeneracy) or NOT AT ALL inside | |
| `max_turns`. The bench has manual tightening evidence in | |
| `combat-attack-from-behind-fog.yaml:105`, | |
| `econ-protect-harvester-route.yaml:159`, | |
| `combat-protect-vip-escort.yaml:144`, `tp-survive-n-turns.yaml:151`, | |
| `rob-deadline-shortened-midway.yaml:107` already manually compensating | |
| โ these are correct (the deadlines were tightened to bite under 30 | |
| ticks/step) and stay. | |
| --- | |
| ## 2. Ore density clamps at 12 cells (medium severity) | |
| **Finding** (econ-mine-and-grow agent, Wave 12) | |
| `ore_patches: {x, y, amount, radius}` density formula is | |
| `clamp(amount / cells, 1, 12)`. The cap of 12 ore/cell means a small | |
| patch with a big `amount` is wasted โ the original | |
| `amount: 2000, radius: 2` (~13 cells) yielded only ~156 total ore = | |
| ~3900 cash ceiling, insufficient for the medium/hard cash bars. | |
| **Workaround**: bump `amount` to 10000-20000 and use `radius: 4` so the | |
| denser disc actually fills. Documented in CLAUDE.md as a footgun, not | |
| yet a permanent fix. | |
| **Action options** | |
| - (a) Document the clamp prominently in `openra_bench/scenarios/schema.py` | |
| docstring for `ore_patches:`. | |
| - (b) Raise the cap (e.g. 24/cell) so smaller patches behave intuitively. | |
| - (c) Have `ore_patches:` validation warn when `amount > cells * 12`. | |
| --- | |
| ## 3. Nuke AoE kills not credited to `kills_per_player` (medium severity) | |
| **Finding** (spec-nuke-strike agent, Wave 12) | |
| `openra-sim/src/world.rs::detonate_nuke` (around line 2133) removes | |
| actors via `self.actors.remove(id)` without bumping | |
| `self.kills_per_player[owner]`. Result: `units_killed` stays 0 for | |
| nuke AoE kills. | |
| **Impact**: any pack that wants `units_killed_gte: N` for a nuke | |
| scenario is unwinnable. The original `spec-nuke-strike` pack used | |
| `units_killed_gte: 5` and was unwinnable on every seed. | |
| **Workaround**: pivoted the pack to destroy BUILDINGS (`silo` clusters) | |
| and use `enemy_buildings_destroyed_gte` instead. Works but limits the | |
| nuke-targeting test to building targets. | |
| **Fix**: mirror the data-driven attack path's credit-on-removal line | |
| (`world.rs:4237`) inside `detonate_nuke`. Small Rust PR. | |
| --- | |
| ## 4. Explicit `harvest(unit_id, x, y)` orders silently ignored (high severity) | |
| **Finding** (econ-multi-patch-allocation agent, Wave 12) | |
| The bench's `harvest` command issues an order, but the engine's | |
| auto-route system (`World::auto_route_idle_harvesters`) re-overrides | |
| any explicit assignment the moment the harvester's owner has a `proc`. | |
| Result: stall, all-to-FAR, all-to-MID, uniform-split, and intended | |
| 2N+1M policies ALL yield the same `ev=14250` on | |
| `econ-multi-patch-allocation medium`. | |
| **Impact**: ANY pack measuring harvester-allocation decision quality | |
| (econ-multi-patch-allocation, econ-far-patch-vs-near-patch, | |
| econ-protect-harvester-route, econ-harvester-pathing-optimization) | |
| cannot discriminate stall from intended; the no-cheat bar can't be | |
| enforced on those families. | |
| **Fix**: respect an explicit `harvest` order โ disable | |
| `auto_route_idle_harvesters` for any harvester that has a | |
| user-issued `Harvest` activity within the last N ticks. Small Rust | |
| PR but load-bearing for ~4 packs and the whole "harvester allocation" | |
| capability. | |
| --- | |
| ## 5. ReturnFire-stance auto-fire breaks kite-and-pull discrimination (high severity) | |
| **Finding** (combat-kite-and-pull agent, Wave 13) | |
| The canonical kiting capability is: ranged unit retreat-fires while a | |
| slower-but-stronger melee unit chases. The intended policy WINS by | |
| keeping range; a stall policy (just stand still on ReturnFire) should | |
| LOSE because the kiter eats hits without dealing enough damage back. | |
| **Empirical reality**: 1 medium tank cannot kill 1 heavy via the kite | |
| cycle within `within_ticks` regardless of HP tuning. **The cell where | |
| the kite WINS is also the cell where stall WINS** โ the heavy walks | |
| into the passive ReturnFire kiter, which auto-fires and kills it | |
| without any kite needed. | |
| **Impact**: the canonical 1v1 kite-and-pull test cannot be expressed. | |
| The combat-kite-and-pull pack worked around it by using a 3-raider | |
| composition (focus-fire micro instead of true kite-and-pull). This | |
| LOSES the bench's ability to test kite-and-pull as a distinct | |
| capability. | |
| **Fix options** | |
| - (a) ReturnFire balance: reduce auto-fire DPS so a passive kiter | |
| actually loses to a heavy in 1v1. | |
| - (b) New stance: "Pure ReturnFire" that fires ONLY after taking hits | |
| (already partially specified in CLAUDE.md's stance section โ verify | |
| it's actually working as specified). | |
| - (c) Engine `attack_unit_kite` order that explicitly retreats-fires | |
| with kite cadence (most invasive, but cleanest semantic). | |
| This is the highest-leverage of the 7 findings โ it removes a whole | |
| capability from the bench. | |
| --- | |
| ## 6. Arena generator's default 4-corner mpspawns rotate per-seed (medium severity) | |
| **Finding** (perception-target-vs-fog agent, Wave 13). **CORRECTED | |
| 2026-05-23 โ original wording was misleading; see triage doc finding | |
| #6 for the post-merge re-read.** | |
| `openra_bench/mapgen.py::_arena` emits 4 default corner mpspawns. The | |
| engine does NOT offset pre-placed actor coordinates โ `position: | |
| [6, 5]` lands at exactly (6, 5) every seed (`build_scenario_actor`, | |
| `openra-train/src/env.rs:2338+`, places actors at literal `sa.position`). | |
| What DOES vary per seed is the **auto-spawned MCV**: | |
| `assign_spawn_points` (`world.rs:4684+`) picks one mpspawn cell per | |
| playable slot, deterministic on `seed`. So if a pack relies on the | |
| auto-MCV being at a specific corner (rather than its absolute | |
| position), the corner DOES rotate across seeds. | |
| **Impact**: per-scenario maps using the `arena` generator with default | |
| spawns and depending on a fixed agent-base location see the auto-MCV | |
| land at a different corner per seed โ breaking position invariants | |
| that assume "the agent's MCV is always at (6,5)". | |
| **Workaround**: declare exactly one mpspawn per generator spec: | |
| `spawns: [[6, 5]]`. Documented inline in the affected pack | |
| (`perception-target-vs-fog.yaml:62-68, 157, 200, 252`). | |
| **Action options** | |
| - (a) Default `arena` generator to a SINGLE centred mpspawn so the | |
| auto-MCV's corner is fixed by default. | |
| - (b) Add a runtime warning when a generator emits multiple mpspawns | |
| AND the scenario has `position:`-anchored agent actors AND no | |
| `spawn_point:` axis is declared. | |
| - (c) Add `mapgen.py` docstring warning. | |
| --- | |
| ## 7. Pre-placing `proc` inside an `ore_patches:` disk silently empties patch (low severity, easy workaround) | |
| **Finding** (mcv-deploy-near-resource agent, Wave 13) | |
| If you pre-place a `proc` actor at a cell that lies INSIDE an | |
| `ore_patches:` disc, the patch silently renders zero ore. The harv | |
| "harvests" at 0 cr/turn. | |
| **Workaround**: place `proc` just OUTSIDE the patch radius. Trivial | |
| once known. | |
| **Fix**: validation-time error or warning when a pre-placed proc/silo | |
| overlaps an `ore_patches:` disc. | |
| --- | |
| ## Summary | |
| | # | Issue | Severity | Workaround? | Cap effort to fix | | |
| |---|---|---|---|---| | |
| | 1 | Tick-rate ~64 not ~90 | M | Tighten deadlines | Re-measure + doc update | | |
| | 2 | Ore density clamps at 12 | M | Bump amount | Raise cap or doc | | |
| | 3 | Nuke kills not credited | M | Use building-destroy predicate | Small Rust PR | | |
| | 4 | Explicit harvest ignored | **H** | None โ kills capability | Small Rust PR | | |
| | 5 | ReturnFire breaks kite | **H** | Use focus-fire comp instead | Engine balance change | | |
| | 6 | Arena default mpspawns rotate | M | Declare single spawn | Default change | | |
| | 7 | Proc-in-patch empties patch | L | Place outside | Validation warning | | |
| **The two HIGH-severity findings (#4 + #5) each remove an entire | |
| capability axis from the bench: harvester-allocation and kite-and-pull | |
| micro.** Worth a focused engine PR pair before the next data-collection | |
| campaign. | |
| The medium findings either have clean workarounds (already applied) or | |
| are doc/spec polish. | |
| --- | |
| ## Suggested next actions | |
| 1. **Top priority**: schedule an engine PR to address #4 (harvester | |
| allocation) โ unblocks an entire econ family. | |
| 2. **Second priority**: address #5 (ReturnFire balance) โ restores | |
| kite-and-pull as a real capability. | |
| 3. **Bench follow-ups**: re-measure tick rate (#1), add validation | |
| warnings for #2/#7, update mapgen default (#6) โ bundleable into | |
| one bench-side PR. | |
| 4. **Nuke kill credit** (#3) is independent and small; ship whenever. | |