fix: Tab Height Guard false-positive on author intent with 'tab' substring
Production bug 2026-05-02 22:19:
Intent 'post author username text (exclude bottom tabs)' contained the
word 'tab' in its parenthetical qualifier. The old check:
is_tab_intent = 'tab' in intent_lower
matched this intent as a TAB navigation intent, activating the Tab
Height Guard which excluded ALL nodes with center_y < 85% of screen.
This nuked the actual clips_author_username node at y=2012, leaving
only 4 irrelevant items for the VLM → bot stuck in a loop unable to
find the author username.
Fix: Replace substring match with precise regex patterns that only
match real tab navigation intents:
- 'tap profile tab', 'tap home tab' (tap + word + tab)
- 'profile tab', 'explore tab' (word + tab)
- 'tab ...' at start of intent
TDD: Red test added first, reproducing exact production state.
81 perception tests + 172 total tests pass.
This commit is contained in:
@@ -61,10 +61,22 @@ class IntentResolver:
|
||||
"""
|
||||
intent_lower = intent_description.lower()
|
||||
|
||||
# Only apply for tab-related intents OR generic interaction intents
|
||||
# We want to aggressively protect against clicking 'Create' / 'Camera' / 'Back' by accident
|
||||
# Only apply for REAL tab navigation intents.
|
||||
# REGRESSION FIX 2026-05-02: "tab" as a substring was too broad.
|
||||
# Intent "post author username text (exclude bottom tabs)" matched
|
||||
# because it contained "tab" → Tab Height Guard nuked the author node.
|
||||
# Now we require specific tab navigation patterns:
|
||||
# - "tap profile tab", "tap home tab", "explore tab"
|
||||
# - NOT "exclude bottom tabs", "tabbar", random mentions
|
||||
import re
|
||||
_TAB_PATTERN = re.compile(
|
||||
r"\btap\s+\w+\s+tab\b" # "tap profile tab", "tap home tab"
|
||||
r"|\b\w+\s+tab\b" # "profile tab", "explore tab"
|
||||
r"|^tab\b", # "tab" at start of intent
|
||||
re.IGNORECASE,
|
||||
)
|
||||
filtered = []
|
||||
is_tab_intent = "tab" in intent_lower and "back" not in intent_lower
|
||||
is_tab_intent = bool(_TAB_PATTERN.search(intent_lower)) and "back" not in intent_lower
|
||||
is_create_intent = "create" in intent_lower or "camera" in intent_lower or "story" in intent_lower
|
||||
# REGRESSION FIX 2026-05-01: Author/username intents must never pick nav tabs
|
||||
is_author_intent = any(kw in intent_lower for kw in ["author", "username", "post media"])
|
||||
|
||||
@@ -295,6 +295,44 @@ class TestStructuralGuards:
|
||||
author_nodes = [n for n in filtered if n.text == "photographer_jane"]
|
||||
assert len(author_nodes) == 1
|
||||
|
||||
def test_author_intent_with_exclude_tabs_suffix_preserves_author(self):
|
||||
"""
|
||||
Production bug 2026-05-02 22:19: Intent 'post author username text
|
||||
(exclude bottom tabs)' contains the word 'tab', which falsely
|
||||
triggers the Tab Height Guard. This nukes the actual author node
|
||||
(clips_author_username at y=2012) because 2012 < 85% of 2400.
|
||||
|
||||
The fix: is_tab_intent must only match REAL tab navigation intents,
|
||||
not intents that mention 'tab' in a parenthetical exclusion note.
|
||||
"""
|
||||
candidates = [
|
||||
_make_node(
|
||||
resource_id="com.instagram.android:id/clips_author_username",
|
||||
content_desc="trenny_m",
|
||||
center_y=2012,
|
||||
),
|
||||
_make_node(
|
||||
resource_id="com.instagram.android:id/profile_tab",
|
||||
content_desc="Profile",
|
||||
center_y=2350,
|
||||
),
|
||||
_make_node(
|
||||
resource_id="com.instagram.android:id/like_button",
|
||||
content_desc="Like",
|
||||
center_y=1353,
|
||||
),
|
||||
]
|
||||
filtered = self.resolver.filter_navigation_conflicts(
|
||||
candidates, "post author username text (exclude bottom tabs)",
|
||||
screen_height=2400,
|
||||
)
|
||||
# The author username at y=2012 MUST survive
|
||||
author_nodes = [n for n in filtered if n.content_desc == "trenny_m"]
|
||||
assert len(author_nodes) == 1, (
|
||||
f"Author node 'trenny_m' was incorrectly filtered out! "
|
||||
f"Remaining: {[(n.resource_id, n.content_desc) for n in filtered]}"
|
||||
)
|
||||
|
||||
# ── Structural Fast-Paths ──
|
||||
|
||||
def test_fast_path_message_input(self):
|
||||
|
||||
Reference in New Issue
Block a user