chore: harden verify_success fallbacks to Fail-Fast and apply aggressive GOAP penalty

This commit is contained in:
2026-05-03 15:42:09 +02:00
parent 5fbbe3d273
commit f46b0b7bcb
4 changed files with 24 additions and 11 deletions

View File

@@ -496,8 +496,12 @@ class GoalExecutor:
return False
else:
# action_success is None (INCONCLUSIVE)
# We decay the memory so it unlearns if it repeatedly fails to produce a definitive success.
logger.warning(f"⚠️ [GOAP Execute] Applying AGGRESSIVE PENALTY for inconclusive action '{action}'.")
engine.decay_click(action)
# Double penalty to burn ambiguous paths faster (outer loop adds +1, so total +2 = instantly hits MAX_RETRIES)
self.action_failures[(pre_action_screen_type, action)] = (
self.action_failures.get((pre_action_screen_type, action), 0) + 1
)
return False
def _execute_recalled_path(self, steps: List[Dict], goal: str) -> bool:

View File

@@ -167,7 +167,8 @@ class ActionMemory:
and "row_feed_button_like" not in post_xml_lower
and "clips_viewer" not in post_xml_lower
):
return None # Still on grid, inconclusive
logger.warning(f"⚠️ [ActionMemory] Still on grid after trying to '{intent}'. Verification FAIL.")
return False # Still on grid, definitely failed
state_toggles = ["like", "save", "follow", "heart"]
is_toggle = any(t in intent_lower for t in state_toggles)
@@ -269,7 +270,10 @@ class ActionMemory:
if diff > 0:
logger.debug(f"🧠 [ActionMemory] Structural delta detected for toggle '{intent}'. Verification PASS.")
return True
else:
logger.warning(
f"⚠️ [ActionMemory] Zero structural shift (diff={diff}) for state-toggle '{intent}'. Verification FAIL."
)
return False
# If the intent is an abstract goal (like "find customers"), diff > 50 is NOT enough.
# We must force visual VLM confirmation because clicking the wrong thing (like "Create highlight")
# also produces a large diff but achieves the wrong goal.
@@ -317,6 +321,12 @@ class ActionMemory:
logger.warning(f"⚠️ [ActionMemory] Cannot visually verify abstract intent '{intent}'. Failing safe.")
return False
# If diff <= 50 for non-toggle
logger.warning(
f"⚠️ [ActionMemory] Insufficient structural change (diff={diff}) for non-toggle '{intent}'. Verification FAIL."
)
return False
def _intent_matches_node(intent: str, semantic_string: str) -> bool:
"""Checks if the clicked element semantically matches the toggle intent.

View File

@@ -54,9 +54,9 @@ def test_goap_planner_avoids_infinite_loop_on_masked_edge():
action_failures=action_failures,
)
# The HD Map should fail, and because the planner is trapped, it forces a restart
# The HD Map should fail, and because the planner is trapped, it falls back to back-tracking
assert (
action_avoided == "force start instagram"
action_avoided == "press back" or action_avoided == "force start instagram"
), "Planner routed BLIND into the dead end despite the edge being masked!"

View File

@@ -855,14 +855,13 @@ class TestVerifySuccessStructuralDelta:
result = memory.verify_success("like", pre_click_xml=pre_xml, post_click_xml=post_xml)
assert result is True
def test_toggle_zero_diff_is_none(self):
"""Zero XML change for a toggle = inconclusive (not confirmed)."""
def test_toggle_zero_diff_is_false(self):
"""Zero XML change for a toggle = fail."""
memory, _ = self._make_memory()
same_xml = '<node text="Like" />'
result = memory.verify_success("like", pre_click_xml=same_xml, post_click_xml=same_xml)
# Zero diff, no markers → None (inconclusive) not True
assert result is None or result is False
assert result is False
def test_follow_success_with_requested_marker(self):
"""
@@ -888,7 +887,7 @@ class TestVerifySuccessStructuralDelta:
result = memory.verify_success("view a post", pre_click_xml="<node/>", post_click_xml=post_xml)
assert result is True
def test_view_post_still_on_grid_is_inconclusive(self):
def test_view_post_still_on_grid_is_false(self):
"""
If after 'view a post' the XML still shows explore_action_bar
WITHOUT post detail markers, navigation failed.
@@ -896,7 +895,7 @@ class TestVerifySuccessStructuralDelta:
memory, _ = self._make_memory()
post_xml = '<node resource-id="com.instagram.android:id/explore_action_bar" />'
result = memory.verify_success("grid item", pre_click_xml="<node/>", post_click_xml=post_xml)
assert result is None # Inconclusive
assert result is False
def test_semantic_gate_blocks_wrong_toggle_element(self):
"""