fix(vm): faithful headless — halt at wait-for-input instead of plowing

The headless divergence that sent us chasing a phantom 'sleep' spin: op 0x72
wait-for-input was a no-op headless, so a run plowed past all 166 of a scene's
prompts into code no real playthrough reaches (SC0000 -> the name-entry poll
loop, spinning sleep 1 493k x to STEP-LIMIT). That path is a fiction.

Fix: VmOptions.HaltAtWaitForInput -> the VM halts (reason 'wait-for-input') at
0x72. run/play default to faithful (SC0000 now halts at ~402 steps, 0 sleeps,
matching the real run's path to the first prompt); --plow opts into the old
walk-every-page coverage. sweep stays plow by default (dialogue oracle, 284/13
unchanged); --halt-at-wait makes all 297 scenes halt cleanly at their first
prompt (0 STEP-LIMIT). Godot unaffected (really blocks on input; flag false).

Engine 58/58 (2 new); sweep default 284/13 unchanged; Godot selftest OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-07-08 10:45:32 -04:00
parent 9ccfc43959
commit 791ea2ca0f
5 changed files with 77 additions and 6 deletions

View File

@@ -86,6 +86,16 @@ subsystem oracles. Test scenes are **synthesized** via `Age.Engine/Sys4/ScriptAs
| `play [--boot] [--state <f>] [--save-state <f>] <SCENE.BIN…> [0xADDR=VAL…]` | ★ Cross-scene **state runner**: run a scene sequence carrying persistent globals. `--boot` first runs the 9 `*INIT` data scripts (real skill/item/unit/map/stage state). `--state`/`--save-state` load/persist a JSON snapshot. | `GameSession`; **executes call-script**. |
| `sweep [--boot] [0xADDR=VAL…]` | Corpus-scale run. **With call-script execution on: 284/297 exit, 13 STEP-LIMIT** (input/state-gated ADV scenes spin headless once subroutine global-writes drive their loops — state divergence, not a bug; 0 depth-cap/unresolved). **With seeds = a story-state explorer**: reports which scenes' dialogue changes ±seed (e.g. form flag `0xa57=1` → 34/297 scenes). | |
**Faithful headless vs plow (`HaltAtWaitForInput`)** — headless has no player, so op `0x72 wait-for-input`
either **halts** ("the scene is waiting; with no input, stop here") or is ignored (**plow** — walk every page).
Plow is a *fiction*: it runs past every prompt into code no real playthrough reaches — e.g. a plowed
`SC0000` fell through 166 prompts into the name-entry poll loop and spun `sleep 1` **493k×** to STEP-LIMIT.
So: **`run`/`play` HALT at the first `wait-for-input` by default** (faithful; `SC0000` stops at ~402 steps,
0 sleeps — matching the real run's path to the first prompt), with **`--plow`** to opt into full-page
coverage. **`sweep` PLOWS by default** (it *is* the dialogue-coverage oracle: 284 exit / 13 STEP-LIMIT),
with **`--halt-at-wait`** to opt into faithful mode (then all 297 scenes halt cleanly at their first prompt
— 0 STEP-LIMIT). Interactive Godot is unaffected (it really blocks on input; flag stays false there).
**`--trace [--trace-file <path>] [--trace-steps]`** (on `run`/`play`/`sweep`): stream the engine's own
diagnostic events over the `Age.Engine.Diagnostics.ITraceSink` seam — scene/subroutine frame enter+exit
(indented by call depth), call-script dispatch with resolved name, and the final halt+step count — to