waitForRenderStable now runs two phases (still entirely in-page, still just the
canvas hash — no CDP screenshots):
1. Fast per-rAF convergence — hash every animation frame until stableFrames are
identical (~48ms). Catches high-frequency motion, cheap.
2. Wide confirmation — then re-hash confirmFrames times, each `interval` ms apart
(default 2 × 250ms ≈ 500ms). A slow async repaint (e.g. a file list arriving
after an asyncify readdir) that a few 16ms frames would sail past now forces a
reset back to phase 1.
This closes the hole where three ~16ms samples could declare "settled" ~200ms
before a delayed paint lands. Genuinely-animating states still never converge and
are captured at the timeout, as before.
Cost (local, 4 workers): e2e 62s->103s, kicad 155s->212s — pure waiting, no extra
CPU. Both suites green (e2e 340, kicad 92). Tunable via confirmFrames/interval.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVX1pHMvRPYHdp6ZfEawrk