ROOT CAUSE:
The bot would permanently softlock itself on UNKNOWN screens (like Trending Audio/Reels)
because 'is_trap' had NO time-decay, and UNKNOWN screen traps persisted forever.
Once 'press back' was trapped on an UNKNOWN screen, the bot was permanently blocked
from escaping any future UNKNOWN screen, causing endless restart loops.
FIXES:
1. Trap Time-Decay: 'is_trap' now forgives Qdrant-persisted traps older than 30 minutes.
This mirrors ContextMemory and prevents permanent dead-ends.
2. UNKNOWN Guard: 'learn_trap' now REFUSES to persist traps for UNKNOWN screens to Qdrant.
They only live in-memory to prevent breaking global navigation.
3. In-Memory Purge: 'force start instagram' now properly calls 'clear_traps()' to wipe
the planner's in-memory traps. Previously they survived restarts, immediately
re-trapping the bot.
Purged 6 ancient traps from Qdrant (one was 8 days old!).
TDD: 3 new tests, full suite 113/114 (1 infra flake)
FOLLOW PLUGIN SAFETY GUARD:
- Added pre-click XML guard: checks button text before clicking
- If button says 'Following'/'Requested'/'Message' → SKIP
(prevents opening dangerous Unfollow/Add-to-Favorites bottom sheet)
- Changed intent from 'tap Follow or Following button' to 'tap follow button'
- This was the root cause of adding users to Close Friends/Favorites
VLM VERIFICATION RESILIENCE:
- VLM false no longer shortcircuits to return False
- VLM verdict is now a SOFT SIGNAL that falls through to structural
delta verification as ground-truth tiebreaker
- Small local VLMs (7B llava) systematically return false for everything
- This was THE root cause of memory poisoning: every action got penalized
because VLM always said 'failed', regardless of actual screen state
- 22 total poisoned Qdrant entries purged across two sessions
ROOT CAUSE CHAIN:
VLM always says false → penalties on every action → confidence < 0.2 →
circuit breaker blocks ALL core actions → bot can't like/follow/comment
→ bot clicks random things → adds to favorites, opens bottom sheets
TDD: 4 new tests, full suite 110/111 (1 infra flake)
CLOSE FRIENDS GUARD:
- Replaced broken text-only detection ('enge freunde'/'close friend')
with STRUCTURAL resource-id marker detection:
* friendly_bubbles_component (feed posts)
* profile_header_close_friend (profile view)
* close_friends_badge (Reels)
* close_friends_star (green star)
- Kept legacy text fallback for edge cases
- TDD: 5 tests including meta-test enforcing structural detection
CONTEXT MEMORY CIRCUIT BREAKER:
- Old system: single failure permanently blocks action (confidence < 0.2 = dead)
- New system: time-based decay — failures older than 1 hour are auto-forgiven
and the poisoned entry is DELETED, allowing re-exploration
- This prevents catastrophic self-sabotage where the bot blocks its own
core actions (tap like, tap follow, tap comment) permanently
- Purged 14 poisoned entries from live Qdrant that were blocking ALL
fundamental interactions
- TDD: 2 meta-tests enforcing time-decay and no permanent blocking
Full suite: 106/107 passed (1 infra flake)
- Removed hardcoded (540,150) fallback in _plan_escape_via_llm
- Added _find_structural_dismiss_target: scans raw XML for clickable
dismiss/cancel/close buttons and extracts coords from bounds attrs
- LLM repeat detection now falls back to structural scan (not magic numbers)
- Temperature increases progressively when LLM is stubborn (0.1 → 0.7)
- Failure history injected as CRITICAL block into LLM system prompt
- Extended pre-commit test discovery to include tests/tdd/
- TDD: 7 new tests including meta-tests proving zero hardcoded coords
- Full suite: 100/100 passed
1. Add long_clickable and class_name to SpatialNode and box_legend.
2. Inject strict guardrails into the SoM prompt to prevent EditText, Recents gallery, and long_clickable hallucinations.
3. Update text-based resolver with equivalent extreme fallback rules.
4. Ensures VLM explicitly rejects non-deterministic edge cases (e.g., Select Album).
- Updated e2e_workflow_ctx in conftest to structurally parse screen_type
- Fixed StoryViewPlugin to gracefully handle pre-existing story screens
- Resolved 'LIE DETECTED' failure in story test suite
- Validated structural path transitions for story ring and story exit
P0-2: Kill blank_start: true in config — the nuclear option that wipes ALL learned
knowledge on every run. Replaced with memory_hygiene() selective amnesia that
prunes low-confidence entries while preserving high-confidence learned patterns.
P0-3: Purge all root-level garbage scripts (scratch.py, test_run.py, profile_dump.xml,
resp_dump.json, pytest_output.log, test_output.log, coverage.xml, coverage_e2e.json).
Updated .gitignore to prevent reaccumulation.
P2-2: Replace piecemeal BANNED_SCREENS/REQUIRED_MARKERS with complete Action
Compatibility Matrix (VALID_SCREENS whitelist). Every interaction intent now has
an explicit set of valid screens. Covers like, comment, follow, unfollow, save,
repost across all 14 screen types.
P2-3: Remove non-deterministic 'press back' transitions from HD Map topology for
OTHER_PROFILE, POST_DETAIL, and SEARCH_RESULTS. Add tab transitions to POST_DETAIL.
P2-4: Replace ScreenMemoryDB nuclear 200-entry wipe with LRU eviction.
store_screen() now accepts confidence parameter.
TDD: 22 new tests (all GREEN), 240 unit/tdd/integration passed, 0 regressions.
E2E: 288 passed (+2 fixed), 6 pre-existing LIE DETECTED failures.
- Raised ContentMemoryDB cache threshold from 0.95 to 0.98 to prevent cross-post poisoning.
- Implemented continuous resonance scoring by storing and retrieving 'resonance_score'
in ContentMemoryDB.
- ResonanceEngine now prioritizes high-fidelity raw scores from cache over
discrete 'high/medium/low' mapping.
TDD: 3 tests verifying threshold and scoring logic integrity.
- Raised similarity_threshold from 0.90 to 0.95 in get_screen_type() to eliminate
POST_DETAIL false-positives caused by embedding saturation.
- Implemented purge_stale_screens() with 24h TTL to remove layout drift.
- Added collection size cap (200 points) to store_screen() with automatic wipe
when saturated to clear overfitting bias.
- Triggered purge_stale_screens() on ScreenIdentity initialization.
TDD: 3 new tests for threshold defaults and purge method integrity.
1. ScreenIdentity: Restructured priority cascade to prevent Qdrant semantic
cache from overriding deterministic structural heuristics. Cached types
now resolve AFTER structural checks (Priority 3) instead of before.
Story/Reels hallucinations from both cache and VLM are rejected if
structural markers are absent.
2. bot_flow: Added error handling for story ring avatar tap and curiosity
HomeFeed navigation failures — prevents silent continuation into
undefined states.
3. GOAP: Extended is_navigation detection to include ScreenTopology
structural actions, ensuring HD Map routes are correctly classified.
4. action_memory: Tightened _parse_yes_no to prevent JSON fall-through
into ambiguous text matching. Structured responses with 'success'
boolean are now handled directly.
The SAE perceive() had no structural fast-path for Instagram-internal
modal overlays (surveys, rating prompts, interstitials). These modals
live inside com.instagram.android and were invisible to the foreign-app
detector. The LLM fallback frequently misclassified them as NORMAL,
causing the bot to get trapped in survey dialogs.
Added two O(1) structural detection layers:
1. Resource-ID markers: survey_overlay_container, interstitial_container,
mystery_interstitial, nux_overlay, rating_prompt, feedback_dialog
2. Dismiss-button heuristic: cross-validates dismiss text with overlay
container structure to prevent caption false positives
Also removed dead code: xml_dump.lower() no-op.
Fixes: test_perceive_instagram_survey_modal
Zero regressions: 464 passed, 6 pre-existing failures
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.
Root cause: bot_flow.py injected configs.args.global_start_time = datetime.now()
which polluted the args namespace. SessionStateEncoder blindly serialized
args.__dict__ via json.dump, which crashed mid-write on the datetime object,
leaving sessions.json truncated/corrupt. Every subsequent restart failed.
Fixes:
- Remove global_start_time from configs.args (only lives on engine class vars)
- Harden SessionStateEncoder with _sanitize_value() to convert any
non-JSON-serializable type (datetime, timedelta, arbitrary objects) to strings
- Add test_system_session_persistence.py with 4 tests covering the exact
production crash scenario (datetime injection → json.dumps → round-trip)
- Fix test_engine_timeout.py broken GoalExecutor module reference
Bug 1 — VLM Profile Tab Hallucination:
- VLM confused 'Profile' nav tab with post author username
- Added Author Tab Guard to filter_navigation_conflicts()
- Fixed mock LLM to use structural row_feed_photo_profile matching
instead of hardcoded username (was the test lie)
Bug 2 — Like Button 1-Byte Delta Kill:
- Toggle actions (like/save) produce 1-byte XML deltas
- GOAP's MIN_UI_CHANGE_BYTES=50 threshold killed them as 'no change'
- Split interaction gate: interactions use ANY xml diff, navigations
keep the 50-byte threshold
Bug 3 — Empty Username Silently Accepted:
- PostDataExtraction returned 'Post by @' with no warning
- Added 'username_missing' reliability flag to result dict
- Downstream consumers can now detect degraded data quality
Cleanup:
- Removed all debug print() statements from production code
- Replaced with structured logger.debug() calls
PRODUCTION BUG 2026-04-30 11:50:
VLM returned action_bar_button_back (desc='Back') for 'tap profile tab'
intent on OTHER_PROFILE screen. This caused account_switcher to navigate
AWAY from the profile instead of opening the account selector bottom
sheet, halting the session.
Root cause: _visual_discovery pre-filter did not exclude navigation
buttons (Back, Close) when the intent was for a tab element.
Fix:
- Added filter_navigation_conflicts() method to IntentResolver
- Guards tab intents by excluding nodes with 'back' in resource_id
or content_desc == 'Back'
- Does NOT apply for 'tap back button' or 'press back' intents
TDD:
- RED: test_intent_resolver_back_guard.py — 2 tests that call
filter_navigation_conflicts on real OTHER_PROFILE XML
- GREEN: filter_navigation_conflicts method + wired into _visual_discovery
- E2E: test_workflow_account_switch.py, test_workflow_vlm_tab_confusion.py
31/31 tests passing.