|
| 1 | +Repair-Tactic Policy for Failed / No-Effect Actions |
| 2 | +=================================================== |
| 3 | + |
| 4 | +When an action does nothing or lands wrong, the agent needs a *policy* for what to try next — |
| 5 | +re-locate and retry, nudge the coordinate, scroll the target into view, wait and retry, or give |
| 6 | +up and escalate. ``self_healing`` / ``locator_repair`` only repair a locator that *did not |
| 7 | +resolve* (element not found); they do nothing when the element was found and clicked but had no |
| 8 | +effect. ``loop_guard`` only *detects* a stuck loop — it has no tactic selection or backoff. |
| 9 | +``step_repair`` is that missing controller: it consumes an effect verdict (e.g. from |
| 10 | +``action_effect``) and drives a bounded retry loop, choosing the next untried tactic each round. |
| 11 | + |
| 12 | +Pure-stdlib state machine; every side effect — performing the action, verifying it, applying a |
| 13 | +tactic, sleeping — is an injected callable, so the loop is fully deterministic and |
| 14 | +unit-testable with no device. Imports no ``PySide6``. |
| 15 | + |
| 16 | +Headless API |
| 17 | +------------ |
| 18 | + |
| 19 | +.. code-block:: python |
| 20 | +
|
| 21 | + from je_auto_control import (plan_repair, run_with_repair, RepairPolicy, |
| 22 | + classify_effect) |
| 23 | +
|
| 24 | + # just the plan |
| 25 | + plan_repair("no_op") # ['wait_retry', 'relocate', 'nudge'] |
| 26 | + plan_repair("changed_elsewhere") # ['escalate'] |
| 27 | +
|
| 28 | + # drive the loop with injected seams |
| 29 | + outcome = run_with_repair( |
| 30 | + act=lambda: click(*target), |
| 31 | + verify=lambda: not is_no_op(before(), after()), |
| 32 | + apply_tactic=apply, # e.g. relocate / nudge the target |
| 33 | + verdict_for=lambda: classify_effect(before(), after(), action).effect, |
| 34 | + policy=RepairPolicy(max_attempts=3)) |
| 35 | + print(outcome.ok, outcome.tactics_used) |
| 36 | +
|
| 37 | +``plan_repair`` returns the ordered tactics for a verdict (a string like ``no_op`` / |
| 38 | +``changed_elsewhere`` or an ``EffectVerdict`` dict), capped at ``max_attempts``; |
| 39 | +``next_tactic`` returns the next untried one. ``run_with_repair`` runs ``act`` then ``verify``; |
| 40 | +on failure it applies tactics until success or exhaustion, returning a ``RepairOutcome`` |
| 41 | +(``ok`` / ``attempts`` / ``tactics_used`` / ``detail``). ``RepairPolicy`` caps attempts and |
| 42 | +lists the allowed tactics. |
| 43 | + |
| 44 | +Executor command |
| 45 | +---------------- |
| 46 | + |
| 47 | +``AC_plan_repair`` (``verdict`` / ``max_attempts`` → ``{count, tactics}``) is exposed as the |
| 48 | +MCP tool ``ac_plan_repair`` (read-only) and as the Script Builder command **Plan Repair |
| 49 | +Tactics** under **Native UI**. (The live ``run_with_repair`` loop is driven from Python, since |
| 50 | +it takes injected callables.) |
0 commit comments