4.4 KiB
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
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==0tip_matches_unit- tip asset equals focus unit asset; if tip starts withNew species:, label species must matchno_bad- StateProbe bad counter is zeroenabled/show_watch_reasons- mod settingscurrent_tier/would_accept_curiosity- director schedulingfocus_same- afterremember_focus(ghost guard)presented/pending_discovery- species discovery buffercaption_layout_ok- dossier panel size/layout bounds (catches off-panel text)screenshotharness action writes.harness/<name>.pngvia Unity ScreenCapture
Screenshots
Harness screenshot writes:
IdleSpectator/.harness/<name>.png- full frameIdleSpectator/.harness/<name>-dossier.png- exact dossier rect (ReadPixels)
For HUD work:
- Prefer reading
*-dossier.png(wait ~1s after the run). - Validate with
./scripts/crop-dossier-hud.py --strict IdleSpectator/.harness/hud-*-dossier.png - Do not judge layout from raw 2880p frames.
- Player-pasted shots override harness crops.
Extending scenarios
Edit IdleSpectator/HarnessScenarios.cs:
- Add steps with
Step(id, action, ...). - Register name in
Build. - Add the name to
REGRESSION_SCENARIOSinscripts/harness-run.shwhen it should gate merges. - Run
./scripts/harness-run.sh <name>then--repeat 3 critical_smoke(andregressionfor suite-level changes).
Keep critical_smoke deterministic:
dismiss_windows/wait_worldfirstpick_unitbefore tip/focus assertsreset_countersafter spectator+focus before health (StateProbe only; usevalue=allonly at batch start)simulate_inputlast for manual-exit coverage