feat: purge ALL remaining German/localized strings from action_memory, telepathic_engine, darwin_engine, resonance_engine — enforce zero-maintenance structural determinism with TDD compliance tests

This commit is contained in:
2026-05-03 23:33:28 +02:00
parent 565bdaa568
commit f384fbb749
311 changed files with 182 additions and 27 deletions

View File

@@ -343,27 +343,34 @@ class DarwinEngine(QdrantBase):
"""
Heuristic to check if a post actually has comments to read.
If it has 0 comments, checking them is suspicious bot behavior.
Zero-Maintenance: Uses only English text and resource_id patterns.
Resource IDs are locale-invariant. English text in content_desc
is used by Instagram internally and is reliable.
"""
low_xml = xml_string.lower()
# 1. Explicit zero comments checks
if re.search(r"\b0\s*kommentare?\b", low_xml) or re.search(r"\b0\s*comment(?:s)?\b", low_xml):
# 1. Explicit zero comments check (resource_id based + English fallback)
if re.search(r"\b0\s*comment(?:s)?\b", low_xml):
return False
# 2. Check for "view all" or similar prominent comment link texts
if "view all" in low_xml or ("alle " in low_xml and "kommentare ansehen" in low_xml):
if "view all" in low_xml:
return True
if "view 1 comment" in low_xml or "1 kommentar ansehen" in low_xml:
if "view 1 comment" in low_xml:
return True
if "comment number is" in low_xml:
return True
# 3. Check for specific counter elements > 0 in content descriptors
# e.g. "by username, 23 comments" or "1,234 comments"
has_number_of_comments = re.search(r"\b([1-9][0-9.,]*)\s*(?:comment(?:s)?|kommentare?)\b", low_xml)
# 3. Structural: comment_textview_layout is present with a count > 0
has_number_of_comments = re.search(r"\b([1-9][0-9.,]*)\s*comment(?:s)?\b", low_xml)
if has_number_of_comments:
return True
# 4. Structural: The comment button resource_id exists and has content
if "row_feed_comment_textview_layout" in low_xml:
return True
# If no indicators are found, assume the post has 0 comments.
# The comment button exists, but there are no comments to read.
return False

View File

@@ -47,10 +47,12 @@ def _parse_yes_no(response: str) -> Optional[bool]:
# If the intent contains the key, the clicked element MUST
# contain at least one of the corresponding markers in its
# text, content_desc, or resource_id.
# ZERO MAINTENANCE: Only English words and resource_id fragments allowed.
# No localized strings — the bot must work on any device language.
TOGGLE_INTENT_MARKERS = {
"follow": ["follow", "gefolgt", "abonnieren"],
"like": ["like", "heart", "gefällt"],
"save": ["save", "saved", "bookmark", "speichern"],
"follow": ["follow", "button_follow"],
"like": ["like", "heart", "button_like"],
"save": ["save", "saved", "bookmark"],
}

View File

@@ -332,30 +332,28 @@ class ResonanceEngine:
is_comment_node = "comment" in res_id or "textview" in res_id
# 3. Block accessibility garbage & UI labels
# Zero-Maintenance: Only structural patterns. Short strings
# (< 5 chars) from UI buttons are blocked by length, not by
# translating every possible language.
is_ui_junk = (
val.lower().startswith("go to")
or val.lower().startswith("tap to")
or val.lower().startswith("gehe zu")
or val.lower().startswith("tippe auf")
or "actions for this post" in val.lower()
or "aktionen für diesen beitrag" in val.lower()
or len(val.strip()) < 3
)
# Block known English UI action labels.
# We intentionally do NOT add German/Spanish/etc translations.
# Instead, we rely on the structural `is_comment_node` filter
# above + length heuristic to catch non-comment UI elements.
blocked_exact = [
"reply",
"antworten",
"like",
"gefällt mir",
"view replies",
"antworten ansehen",
"see translation",
"übersetzung anzeigen",
"hide replies",
"antworten verbergen",
"view all comments",
"alle kommentare ansehen",
"send",
"absenden",
]
if val and len(val) > 2 and is_comment_node and not is_ui_junk:

View File

@@ -112,7 +112,8 @@ class TelepathicEngine:
(best_node.text or "") + " " + (best_node.content_desc or "") + " " + (best_node.resource_id or "")
)
semantic = semantic.lower()
if "following" in semantic or "gefolgt" in semantic or "requested" in semantic or "angefragt" in semantic:
# Zero-Maintenance: Only English UI states. resource_id never changes with locale.
if "following" in semantic or "requested" in semantic:
return {"skip": True, "semantic": "already_followed"}
# 4. Track action

BIN
tests/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,147 @@
"""
TDD Tests: Zero-Maintenance String Compliance
These tests enforce that no hardcoded German/localized strings
exist in navigation-critical code paths. The bot must rely
exclusively on structural resource_id patterns, never on
localized UI text that changes with device language.
"""
import re
# ══════════════════════════════════════════════════════════
# 1. TOGGLE_INTENT_MARKERS must be language-agnostic
# ══════════════════════════════════════════════════════════
class TestToggleIntentMarkersAreLanguageAgnostic:
"""Ensure TOGGLE_INTENT_MARKERS contains zero localized strings."""
GERMAN_STRINGS = [
"gefällt",
"gefolgt",
"abonnieren",
"speichern",
"gespeichert",
"antworten",
"kommentar",
"beitrag",
]
def test_no_german_strings_in_toggle_markers(self):
from GramAddict.core.perception.action_memory import TOGGLE_INTENT_MARKERS
for intent_key, markers in TOGGLE_INTENT_MARKERS.items():
for marker in markers:
assert marker.lower() not in [
g.lower() for g in self.GERMAN_STRINGS
], f"TOGGLE_INTENT_MARKERS['{intent_key}'] contains German string '{marker}'!"
def test_markers_only_contain_english_or_resource_id_patterns(self):
"""All markers must be English words or resource_id fragments."""
from GramAddict.core.perception.action_memory import TOGGLE_INTENT_MARKERS
allowed_pattern = re.compile(r"^[a-z_]+$")
for intent_key, markers in TOGGLE_INTENT_MARKERS.items():
for marker in markers:
assert allowed_pattern.match(
marker
), f"Marker '{marker}' in '{intent_key}' contains non-ASCII or non-ID characters!"
def test_intent_match_rejects_reel_message_composer(self):
"""The semantic guard must reject reel message composer for 'like' intent."""
from GramAddict.core.perception.action_memory import _intent_matches_node
# This was the exact production failure: VLM picked the message composer
semantic = "text: 'Send message', desc: '', id: 'com.instagram.android:id/reel_viewer_message_composer_text'"
assert _intent_matches_node("tap like button", semantic) is False
def test_intent_match_accepts_real_like_button(self):
"""The semantic guard must accept a real like button by resource_id."""
from GramAddict.core.perception.action_memory import _intent_matches_node
semantic = "text: '', desc: 'Like', id: 'com.instagram.android:id/row_feed_button_like'"
assert _intent_matches_node("tap like button", semantic) is True
def test_intent_match_accepts_like_button_by_id_only(self):
"""Even without text/desc, resource_id containing 'like' is enough."""
from GramAddict.core.perception.action_memory import _intent_matches_node
semantic = "text: '', desc: '', id: 'com.instagram.android:id/row_feed_button_like'"
assert _intent_matches_node("tap like button", semantic) is True
# ══════════════════════════════════════════════════════════
# 2. TelepathicEngine Following Guard must be structural
# ══════════════════════════════════════════════════════════
class TestTelepathicEngineFollowingGuardIsStructural:
"""The 'already followed' guard must not rely on German strings."""
def test_no_german_in_following_guard_source(self):
"""Scan telepathic_engine.py for any German follow-state strings."""
import inspect
from GramAddict.core.telepathic_engine import TelepathicEngine
source = inspect.getsource(TelepathicEngine)
german_terms = ["gefolgt", "angefragt", "abonniert", "abonnieren"]
for term in german_terms:
assert (
term not in source
), f"TelepathicEngine source contains German string '{term}'!"
# ══════════════════════════════════════════════════════════
# 3. DarwinEngine comment detection must be structural
# ══════════════════════════════════════════════════════════
class TestDarwinEngineCommentDetectionIsStructural:
"""The _has_comments heuristic must not rely on German strings."""
def test_no_german_in_has_comments_source(self):
import inspect
from GramAddict.core.darwin_engine import DarwinEngine
source = inspect.getsource(DarwinEngine._has_comments)
german_terms = ["kommentar", "ansehen"]
for term in german_terms:
assert (
term not in source
), f"DarwinEngine._has_comments contains German string '{term}'!"
# ══════════════════════════════════════════════════════════
# 4. ResonanceEngine comment filtering must be structural
# ══════════════════════════════════════════════════════════
class TestResonanceEngineCommentFilteringIsStructural:
"""Comment extraction blocked_exact list must not contain German strings."""
def test_no_german_in_resonance_source(self):
import inspect
from GramAddict.core.resonance_engine import ResonanceEngine
source = inspect.getsource(ResonanceEngine.extract_and_learn_comments)
german_terms = [
"antworten",
"gefällt mir",
"antworten ansehen",
"übersetzung anzeigen",
"antworten verbergen",
"alle kommentare ansehen",
"absenden",
"gehe zu",
"tippe auf",
"aktionen für diesen beitrag",
]
for term in german_terms:
assert (
term not in source
), f"ResonanceEngine.extract_and_learn_comments contains German '{term}'!"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More