Repair-Tactic Policy for Failed / No-Effect Actions

When an action does nothing or lands wrong, the agent needs a policy for what to try next — re-locate and retry, nudge the coordinate, scroll the target into view, wait and retry, or give up and escalate. self_healing / locator_repair only repair a locator that did not resolve (element not found); they do nothing when the element was found and clicked but had no effect. loop_guard only detects a stuck loop — it has no tactic selection or backoff. step_repair is that missing controller: it consumes an effect verdict (e.g. from action_effect) and drives a bounded retry loop, choosing the next untried tactic each round.

Pure-stdlib state machine; every side effect — performing the action, verifying it, applying a tactic, sleeping — is an injected callable, so the loop is fully deterministic and unit-testable with no device. Imports no PySide6.

Headless API

from je_auto_control import (plan_repair, run_with_repair, RepairPolicy,
                             classify_effect)

# just the plan
plan_repair("no_op")              # ['wait_retry', 'relocate', 'nudge']
plan_repair("changed_elsewhere")  # ['escalate']

# drive the loop with injected seams
outcome = run_with_repair(
    act=lambda: click(*target),
    verify=lambda: not is_no_op(before(), after()),
    apply_tactic=apply,                 # e.g. relocate / nudge the target
    verdict_for=lambda: classify_effect(before(), after(), action).effect,
    policy=RepairPolicy(max_attempts=3))
print(outcome.ok, outcome.tactics_used)

plan_repair returns the ordered tactics for a verdict (a string like no_op / changed_elsewhere or an EffectVerdict dict), capped at max_attempts; next_tactic returns the next untried one. run_with_repair runs act then verify; on failure it applies tactics until success or exhaustion, returning a RepairOutcome (ok / attempts / tactics_used / detail). RepairPolicy caps attempts and lists the allowed tactics.

Executor command

AC_plan_repair (verdict / max_attempts{count, tactics}) is exposed as the MCP tool ac_plan_repair (read-only) and as the Script Builder command Plan Repair Tactics under Native UI. (The live run_with_repair loop is driven from Python, since it takes injected callables.)