Pre-Match Settle Gating + Match Persistence
Matching mid-animation is a top flakiness source: the spinner is still spinning, a transition
is half-done, and the matcher fires on a transient. smart_waits.wait_until_screen_stable
gates a live polling loop and returns a boolean — it cannot score stability on an injectable
sequence of frames, nor tell you whether a match held steady across them. match_stability
adds both, on injected frames so it is unit-testable: region_stability scores how settled a
frame sequence is (consecutive-frame SSIM), and match_persistence checks that a template’s
hit holds at the same place across the frames (low jitter), not just in one lucky frame.
It reuses ssim.ssim_compare, visual_match.match_template and
grounding_consensus.consensus_point — no new matching code. The frames are injectable
(ndarray / path / PIL). Imports no PySide6.
Headless API
from je_auto_control import region_stability, match_persistence
frames = [grab(), grab(), grab()] # a few snapshots of the same region
if region_stability(frames, settle_threshold=0.99)["stable"]:
do_the_match()
result = match_persistence("button.png", frames, min_score=0.8, agree_px=8)
if result["persisted"]:
print("steady hit, jitter", result["jitter"])
region_stability returns {stable, mean_ssim, min_ssim} — stable when the lowest
consecutive-frame SSIM clears settle_threshold. match_persistence returns
{persisted, n_hits, jitter} — persisted when the template is found in every frame and
the hit centres agree within agree_px (jitter is their spread; 0 = rock steady).
Executor commands
AC_region_stability (frames / settle_threshold → {stable, mean_ssim, min_ssim})
and AC_match_persistence (template / frames / min_score / agree_px →
{persisted, n_hits, jitter}). They are exposed as the MCP tools ac_region_stability /
ac_match_persistence (read-only) and as the Script Builder commands Region Stability
(frames) / Match Persistence (frames) under Image.