diff --git a/.claude/skills/jgengine-verify/SKILL.md b/.claude/skills/jgengine-verify/SKILL.md index 5b92935b6..4db5b741d 100644 --- a/.claude/skills/jgengine-verify/SKILL.md +++ b/.claude/skills/jgengine-verify/SKILL.md @@ -41,6 +41,7 @@ Classify each acceptance claim before scheduling proof. A plan that uses screens - `GameHost` publishes declared `capture.probe` as `window.__jgProbe` after game initialization and staging. Reads use the live context; replacement/unmount retires the hook. An absent or throwing probe supplies no progress evidence. - Placeholder-vs-authored: `debug_snapshot().probes.fallbacks` reports which render seams resolved to fallbacks (green ground, primitive actors, proxy scatter) and why — a non-empty count proves content is unauthored/misconfigured rather than an intended placeholder. `probes.textureErrors` (`{ url, count }[]`) is its twin for the failure `fallbacks` cannot see: a model that resolves but whose GLB textures 404 — a non-empty list is a visibly-broken (untextured) scene, treat it exactly like a model fallback. - Portable `drive --click-at x,y` sends trusted native left-click input to a canvas or control in CSS viewport pixels. Steps preserve argument order; coordinates must be finite, nonnegative and strictly inside the selected device or overridden viewport (mobile DPR does not multiply them). Use the game’s visible world target; coordinate clicking does not project an entity id, run a game command directly, or advance fixed simulation time. Older project-owned `scripts/drive.mjs` keep their installed behavior; update the scaffold harness to use this option. +- Aiming in a step file: `{ "moveMouse": [x, y] }` hovers the pointer at viewport pixels, `{ "look": [dx, dy] }` turns a pointer-locked view by that mouse movement (the step takes pointer lock on the play canvas inside a CDP user gesture, so it never fires the weapon), and `{ "aimAt": "" }` turns until the target's `{x,y}` projection sits on the crosshair within `tolerancePx` (default 6), measuring the page's own response instead of assuming a sensitivity. A shooter proof asserts the arrival every time — a shot before the aim that leaves `capture.probe` health flat, then a shot after it that drops it — because "fired six taps" proves only that the trigger works. - A route a proof needs once belongs in `/proofs/flows/.json`, not copied into every proof: `bun run drive --flow ` runs it, `--flows` lists each name with its step count and asserts, and `{ "flow": "" }` includes it (4 levels deep, a repeated name is a cycle). A flow asserts arrival — the match started, N units present — because a flow that only clicks proves nothing. Every step prints one line and `--log ` writes JSONL, so a failed run names the step, the expected and actual value, and stops before the next screenshot. - UI-flow logic (a command/intent handler, not its pixels): drive it canvas-free with `createHeadlessRunner(...).ui.invoke("intent.name", input)`, then assert the resulting reactive state off `ctx` (store, stats, scene). No renderer, no pointer simulation — leave pixels to `shoot`; prove the logic headlessly. - Play itself, as a script: `bun run simulate