[eric] browser: a comment meant for a post must not land on a story

This commit is contained in:
ciregenz
2026-08-04 15:57:30 -07:00
parent 57d5a6fed7
commit b62a29357e
3 changed files with 105 additions and 0 deletions
@@ -191,6 +191,46 @@ def surface_mismatch(task: str, composer_name: str) -> bool:
return bool(P_COMMENT_SURFACE_RE.search(name))
# What a URL path declares the page to BE. Sites disagree about almost everything, but these path
# segments are near-universal, and each one is the site telling us the content type in its own words.
P_URL_CONTENT_TYPES = (
("story", re.compile(r"/stories?/", re.I)),
("message", re.compile(r"/(direct|messages?|dm)/", re.I)),
("video", re.compile(r"/(watch|shorts)\b|/video/", re.I)),
("post", re.compile(r"/(p|posts?|status|submit)/|/submit\b", re.I)),
)
# The same types as the TASK names them. Deliberately narrow: a word has to be unambiguous here or
# the guard starts refusing pages that were right all along.
P_TASK_CONTENT_TYPES = (
("story", re.compile(r"\bstor(?:y|ies)\b", re.I)),
("message", re.compile(r"\b(dm|direct message)\b", re.I)),
("video", re.compile(r"\bvideos?\b", re.I)),
("post", re.compile(r"\bposts?\b", re.I)),
)
def content_type_mismatch(task: str, url: str) -> str:
""""asked for a <x>, landed on a <y>", or "" when they agree or either is unclear.
surface_mismatch below asks whether the COMPOSER contradicts the task. This asks the same thing
one level up, about the page, because a page can hand you a perfectly good composer that belongs
to the wrong thing entirely. Measured on instagram 2026-08-04: "write a comment on the first
post" opened a STORY (`/stories/<user>/`), whose reply box is a real composer, so every gate
downstream was satisfied and the comment would have gone to a story nobody asked about.
Both sides must be unambiguous. A task that names no type, or a URL that declares none, returns
"" and changes nothing, so the cost of being wrong here is a page we decline to fill fast and
hand to the model instead.
"""
if not task or not url:
return ""
want = [name for name, rx in P_TASK_CONTENT_TYPES if rx.search(task)]
got = [name for name, rx in P_URL_CONTENT_TYPES if rx.search(url)]
if len(want) != 1 or len(got) != 1 or want[0] == got[0]:
return ""
return f"asked for a {want[0]}, landed on a {got[0]}"
def is_readonly(text: str) -> bool:
"""A read-only directive ('verify whether', 'do not send') that must decline the scripted
send even with a quoted payload in hand. Keeps the regex private to this file."""
@@ -263,6 +263,15 @@ async def run_send_script(
if not payload:
logger.info("[browser-sendscript] decline: no unambiguous quoted payload")
return None
# The page itself says what it is, and a composer on the wrong thing is still the wrong thing.
# Live on instagram: a "comment on the first post" task opened a STORY, whose reply box is a
# perfectly real composer, so every gate below passed and the comment would have gone somewhere
# nobody asked for. Declining costs the fast path and hands the page to the model, which can
# navigate; filling would have been silent and wrong.
p_ctype = browser_send_parse.content_type_mismatch(task_sans_brief, current_url or "")
if p_ctype:
logger.info(f"[browser-sendscript] decline: {p_ctype} ({(current_url or '')[:60]})")
return None
log: list[dict] = []
composer = browser_send_parse.composer_index_in_state(state_text)
@@ -0,0 +1,56 @@
"""A composer on the wrong THING is still the wrong thing.
`surface_mismatch` already asks whether the composer contradicts the task (a post ask must not fill
a DM box). This is the same question one level up, about the page, because a page can hand you a
perfectly real composer that belongs to something nobody asked about.
Measured live on instagram 2026-08-04 at N=5: "write a comment on the first post" opened a STORY
(`/stories/<user>/`). A story's reply box IS a composer, so the DM guard, the structural finder and
the fill receipt were all satisfied, and the comment would have gone to a story silently. The URL is
the page's own declaration of what it is, and it was the only signal that disagreed.
Both sides must be unambiguous before this fires. The cost of a false positive is one declined fast
path (the model still gets the page); the cost of a false negative is a write to the wrong surface.
"""
from backend.apps.agents.browser import browser_send_parse as sp
def test_the_instagram_story_bug_verbatim():
why = sp.content_type_mismatch("write a comment on the first post",
"https://www.instagram.com/stories/miy.jpg/")
assert why == "asked for a post, landed on a story", why
def test_the_same_task_on_a_real_post_is_fine():
"""The guard must not cost us the case that works: instagram reached /p/<id> on other runs."""
assert sp.content_type_mismatch("write a comment on the first post",
"https://www.instagram.com/p/DbRdbkgCZgk/") == ""
def test_asking_for_a_story_and_getting_one_is_fine():
"""The user is allowed to want a story. The guard is about disagreement, not about stories."""
assert sp.content_type_mismatch("reply to the first story",
"https://www.instagram.com/stories/miy.jpg/") == ""
def test_the_working_sites_are_untouched():
"""Every site at 5/5 or 4/4 in the N=5 sweep must keep scoring exactly as it did."""
for task, url in (
("comment on the first video", "https://www.youtube.com/watch?v=SAjrSUNCQbc"),
("post this tweet", "https://x.com/compose/post"),
("create a text post", "https://www.reddit.com/r/test/submit/?type=TEXT"),
("create a post", "https://www.linkedin.com/feed/?shareActive=true"),
("write in chat", "https://www.twitch.tv/jynxzi"),
):
assert sp.content_type_mismatch(task, url) == "", (task, url)
def test_it_stays_silent_when_either_side_is_unclear():
"""An ambiguous task names two types and a plain URL declares none. Guessing between them would
refuse pages that were right all along, which costs more than the bug it would catch."""
assert sp.content_type_mismatch("post a comment on the story",
"https://www.instagram.com/stories/x/") == ""
assert sp.content_type_mismatch("say hi to tyler", "https://example.com/anything") == ""
assert sp.content_type_mismatch("", "https://www.instagram.com/stories/x/") == ""
assert sp.content_type_mismatch("comment on the first post", "") == ""