yxc20098 commited on
Commit
2b3ad6d
ยท
1 Parent(s): 75de74f

docs: per-scenario closer-look checklist distilled from #1 and #2

Browse files

Reproducible solvency/stability/capability methodology + the defect
patterns already seen, so per-scenario review agents look for and
test the same things.

Files changed (1) hide show
  1. SCENARIO_REVIEW_CHECKLIST.md +138 -0
SCENARIO_REVIEW_CHECKLIST.md ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Per-Scenario Closer-Look Checklist
2
+
3
+ The reproducible methodology applied to #1 (action-multiunit-coordination)
4
+ and #2 (action-sequenced-execution). Every remaining active pack gets
5
+ the same treatment: **closer look โ†’ fix defects โ†’ re-verify โ†’ run each
6
+ difficulty easyโ†’mediumโ†’hard on the model โ†’ inspect the playback โ†’
7
+ commit/push**. Read this fully before touching a pack.
8
+
9
+ Guiding principle: **the benchmark must validly test its stated
10
+ capability; do NOT compensate for model weakness, and do NOT
11
+ over-engineer.** A model failing a correctly-designed scenario is a
12
+ *good* result (real discrimination). Only fix genuine scenario defects.
13
+
14
+ ---
15
+
16
+ ## A. SOLVENCY โ€” can the intended strategy actually win, in budget?
17
+
18
+ 1. **The win predicate must enforce the advertised capability โ€” and
19
+ only that.** The classic defect: the prose claims X but the win
20
+ condition is satisfiable without doing X.
21
+ - #1: "split the force" but `reach_region` (โ‰ฅ1 unit) let one
22
+ touring unit win โ†’ switched to `units_in_region_gte` (โ‰ฅ2 in EACH
23
+ region).
24
+ - #2: "ordered route" but only the *final* region was a predicate โ†’
25
+ a beeline that skipped every waypoint won โ†’ added the stateful
26
+ `waypoint_sequence` latch (Wk+1 only counts after Wk; skip /
27
+ out-of-order / idle โ‡’ never satisfied).
28
+ - Ask: "what is the laziest play that satisfies this win condition?"
29
+ If it isn't the intended capability, the predicate is wrong.
30
+ 2. **Is the optimal/intended play winnable within the tick budget?**
31
+ Estimate path length; engine advances **~90 ticks per decision
32
+ turn** (`tick โ‰ˆ 93 + 90ยท(turn-1)`). The intended strategy must
33
+ finish comfortably under `within_ticks`; the *inefficient* strategy
34
+ must NOT (that gap is the discrimination).
35
+ 3. **Coordinate-blind objectives must be solvable.** If
36
+ `objective_coords: relative`, the model can't cell-count โ€” it needs
37
+ a feedback loop (an `enemy_building_spotted` interrupt revealing the
38
+ marker, a landmark building at each region) and a forgiving enough
39
+ radius. A "search band" beats a bare compass word.
40
+ 4. **Map fits the actors.** Every actor/region coordinate must be
41
+ inside the map's playable bounds. Actors at scout-arena coords on a
42
+ rush-hour map โ†’ engine panic. Confirm `compiled.map_supported` and
43
+ that `base_map` resolves to the *intended* map.
44
+
45
+ ## B. STABILITY โ€” deterministic, no crashes, fail is reachable
46
+
47
+ 5. **Non-win must be a real LOSS, not a draw.** Every level needs a
48
+ `fail_condition`. Idiom: `any_of[ {after_ticks: BUDGET+1},
49
+ {not:{units_lost_lte: N}}, {not:{own_units_gte: 1}} ]`.
50
+ 6. **Tick/turn alignment (critical, easy to get wrong).** A
51
+ `fail after_ticks: K` only bites if K is reachable within
52
+ `max_turns`: require `K โ‰ค 93 + 90ยท(max_turns-1)`. Likewise the
53
+ episode must be able to reach `within_ticks` before `max_turns`
54
+ ends, or a staller draws instead of losing. Re-derive this for
55
+ every level after any budget/turn change.
56
+ 7. **`base_map` override goes INSIDE `overrides:`** โ€” a Level-level
57
+ `base_map` key is silently ignored (it's not a `Level` field).
58
+ 8. **Smoke the engine path before a full run**: compile the level,
59
+ `_scenario_to_tmp_yaml`, `RustEnvPool` reset(seed) + a few
60
+ `env.step`; if the pack enables interrupts, also drive
61
+ `raw_env.step_until_event([...],None,5,[sig])` ~30 steps โ€” catches
62
+ panics/oob without burning a model run.
63
+ 9. **Hard-tier spawn contract**: if the pack is in
64
+ `tests/test_hard_tier.py::UPGRADED`, `hard` must keep โ‰ฅ2
65
+ `spawn_point` groups (seed-varied start). A deliberate exception is
66
+ allowed only with a stated reason added to `NOT_APPLICABLE`.
67
+
68
+ ## C. CAPABILITY โ€” clean difficulty axis, faithful framing
69
+
70
+ 10. **One new controlled variable per tier** so a tier failure
71
+ attributes to a single capability. easy = the bare skill; medium =
72
+ +1 axis (a third region / parallel split / contest / attrition);
73
+ hard = +1 more (relative coords + scouting, larger map, strict
74
+ budget). Avoid stacking uncontrolled variables.
75
+ 11. **Keep established idioms** (e.g. the single-final-region +
76
+ `[after_ticks, within_ticks]` band for "execute, don't stall";
77
+ fact+proc key-building destruction for adversarial). Don't invent a
78
+ new mechanism when one exists โ€” but DO add a reusable predicate /
79
+ engine feature when the capability genuinely can't be expressed
80
+ (`units_in_region_gte`, `waypoint_sequence`, `enemy_building_
81
+ spotted` interrupt, scripted `enemy.bot`).
82
+ 12. **Title/description are plain and self-explanatory**; the
83
+ objective brief the model sees (`game_knowledge.objective_brief`)
84
+ must state the exact machine win/fail in plain language with no
85
+ degenerate lines.
86
+
87
+ ## D. RUN & INSPECT (one difficulty at a time)
88
+
89
+ 13. Run on **`qwen/qwen3.6-flash`** via OpenRouter (key in `./.env`,
90
+ git-ignored): `python3 -m openra_bench.run_eval --packs
91
+ openra_bench/scenarios/packs/<pack>.yaml --levels <lvl> --seeds 1
92
+ --provider openrouter --model qwen/qwen3.6-flash --playback
93
+ playback/run1`. easy, then medium, then hard.
94
+ 14. **Inspect the playback** (`playback/run1/*/<pack>:<lvl>:public/
95
+ seed1/`): `manifest.json` (outcome/turns), `score.json`
96
+ (composite/weakest_link/speed), `turns.jsonl` (per-turn `units`,
97
+ `enemies`, `signals`, `interrupt`, `goal`), `messages.json` (model
98
+ text is in the **`reasoning`** field, not `content`). Reconstruct:
99
+ did the intended mechanism fire? final positions vs win regions?
100
+ `units_lost`? terminal "episode end" frame present?
101
+ 15. **Classify the outcome**: scenario defect (โ†’ fix, re-verify, re-run)
102
+ vs legitimate model failure (โ†’ record as valid discrimination, no
103
+ change). Cite evidence from the playback.
104
+
105
+ ## E. RE-VERIFY & SHIP
106
+
107
+ 16. `python3 -m pytest tests/ -q` fully green (add/extend focused
108
+ tests for any new predicate/knob/scenario behaviour).
109
+ 17. `python3 scripts/gen_scenario_docs.py` (regenerate the HTML
110
+ catalog) when prose/objectives change.
111
+ 18. Commit per fixed scenario, **no Claude co-author line**, using
112
+ `git -c commit.gpgsign=false commit`. Then
113
+ `git fetch -q origin && git rebase -q origin/main && git push -q
114
+ origin HEAD`. Engine changes (OpenRA-Rust) commit+push separately;
115
+ rebuild the wheel (`maturin develop --release`) if the engine
116
+ changed and re-run the affected scenario.
117
+ 19. Record a "Per-scenario closer look โ€” #N" note in
118
+ `SCENARIO_QUALITY.md` (the defect found, the fix, the
119
+ easy/medium/hard outcomes + verdict).
120
+
121
+ ## Reference: defect patterns already seen
122
+
123
+ - Win condition doesn't enforce the stated capability (laziest play
124
+ wins). โ€” #1, #2
125
+ - `reach_region`/single-region where a *split* or *ordered* visit is
126
+ intended. โ€” #1, #2
127
+ - Missing `fail_condition` โ‡’ non-win == draw. โ€” #1, #2
128
+ - `after_ticks` fail unreachable within `max_turns` (tick/turn
129
+ misalignment). โ€” #2
130
+ - Relative objective too literal ("NE corner" โ†’ map extreme), region
131
+ inset & unreachable blind, landmark fogged โ‡’ unfair. โ€” #1 hard
132
+ - `base_map` at Level level silently ignored. โ€” #2 hard
133
+ - Actors placed off the resolved map โ‡’ engine panic in the interrupt
134
+ path. โ€” #2 hard
135
+ - Bench advertises a tool the engine can't execute (1:1 parity). โ€”
136
+ capture_actor / S8
137
+ - Inert deadline (`within_ticks` โ‰ซ optimal) โ‡’ no anti-stall teeth. โ€”
138
+ #1 easy, #2 easy (acceptable for *easy* only).