worldbox-observer-mod/.cursor/skills/idle-spectator-e2e/reference.md

93 lines
4.4 KiB
Markdown

# IdleSpectator harness reference
## WorldBox wiki
Use https://the-official-worldbox-wiki.fandom.com/wiki/The_Official_Worldbox_Wiki when designing Spectator priorities or Chronicle events (Units lovers/friends, World History, kings, traits, war, disasters).
Chronicle records **life/world events**, not spectator watch notes.
Watch caption covers live focus context.
Vanilla tooltips (`Tooltip`, `TooltipLibrary`, `tooltips/tooltip_actor`) are candidates for future UI polish.
## Architecture
```text
scripts/harness-run.sh
-> starts WorldBox if needed
-> writes IdleSpectator/.harness/commands.jsonl
-> touches reset + auto-dismiss
-> regression: loops named scenarios sequentially
AgentHarness.Update() (in-game)
-> executes commands on Unity main thread
-> FreezeDirector unless director_run is active
-> writes results.jsonl + last-result.json
-> logs [HARNESS]/[ASSERT]/[SNAP]
```
## Why not OS input?
Wayland makes `xdotool`/`ydotool` unreliable.
Truth lives in engine state (`MoveCamera`, tips, settings), so drive the mod API directly.
## Timing
InterestDirector and SpeciesDiscovery use `Time.unscaledTime`.
WorldBox 5x game speed does not advance those clocks.
Use harness `fast_timing` + `director_run` / `age_current` for scheduling tests.
## Hard-won failure modes
| Symptom | Cause | Fix |
|---------|-------|-----|
| Welcome closes, idle never starts | commands.jsonl rewritten same length; offset at EOF | runner writes `offset=0` + `reset` |
| Steps run before world ready | `wait_world` requeued to back | `RequeueFront` |
| Tip says crab, focus is angle | watch fell back to nearest unit | require matching asset; use `pick_unit`/`asset=auto` |
| Spawn then "no unit for asset" | miracle spawns die on bad tiles | prefer `pick_unit` for critical paths |
| Flaky health/no_bad mid-scenario | ambient retarget while harness busy | `FreezeDirector` unless `director_run` |
| Steam hang after tests | `Application.Quit` | leave game running |
| Director tests take forever | production dwell/rotate | `fast_timing` (not game 5x) |
| Game never opens / `Cannot run as root user` | Shell sandboxed; Steam rejects root-like env | re-run launch/`harness-run.sh` with Shell `required_permissions: ["all"]` |
| Old mod still loaded after edit | NML caches until restart | kill `.../common/worldbox/worldbox`, bump `mod.json`, relaunch with `all` perms |
| Harness says ready but mod broken | Stale `Harmony patches applied` / missed `error CS` | runner fails boot on IdleSpectator `error CS`; ready requires Harmony in current (truncated) Player.log |
| Harness stuck on `o...` forever | Launch marker written into Player.log then wiped by Unity | do not put boot markers in Player.log; wait for process + Harmony |
## Useful asserts
- `health` - idle + focus + power bar hidden + `bad==0`
- `tip_matches_unit` - tip asset equals focus unit asset; if tip starts with `New species:`, label species must match
- `no_bad` - StateProbe bad counter is zero
- `enabled` / `show_watch_reasons` - mod settings
- `current_tier` / `would_accept_curiosity` - director scheduling
- `focus_same` - after `remember_focus` (ghost guard)
- `presented` / `pending_discovery` - species discovery buffer
- `caption_layout_ok` - dossier panel size/layout bounds (catches off-panel text)
- `screenshot` harness action writes `.harness/<name>.png` via Unity ScreenCapture
## Screenshots
Harness `screenshot` writes:
- `IdleSpectator/.harness/<name>.png` - full frame
- `IdleSpectator/.harness/<name>-dossier.png` - **exact dossier rect** (`ReadPixels`)
For HUD work:
1. Prefer reading `*-dossier.png` (wait ~1s after the run).
2. Validate with `./scripts/crop-dossier-hud.py --strict IdleSpectator/.harness/hud-*-dossier.png`
3. Do not judge layout from raw 2880p frames.
4. Player-pasted shots override harness crops.
## Extending scenarios
Edit `IdleSpectator/HarnessScenarios.cs`:
1. Add steps with `Step(id, action, ...)`.
2. Register name in `Build`.
3. Add the name to `REGRESSION_SCENARIOS` in `scripts/harness-run.sh` when it should gate merges.
4. Run `./scripts/harness-run.sh <name>` then `--repeat 3 critical_smoke` (and `regression` for suite-level changes).
Keep critical_smoke deterministic:
- `dismiss_windows` / `wait_world` first
- `pick_unit` before tip/focus asserts
- `reset_counters` after spectator+focus before health (StateProbe only; use `value=all` only at batch start)
- `simulate_input` last for manual-exit coverage