From 7761d716b1f57a5b5672bc9a31e9b0d4abdcb9ae Mon Sep 17 00:00:00 2001 From: ciregenz Date: Fri, 31 Jul 2026 17:16:50 -0700 Subject: [PATCH] [eric] browser: a read-only directive can never be a publish task, so probes stop reporting failure --- backend/apps/agents/browser/browser_loop.py | 10 ++++++++++ backend/tests/test_browser_stagnation.py | 22 +++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/backend/apps/agents/browser/browser_loop.py b/backend/apps/agents/browser/browser_loop.py index 6f3b084f..33d8e063 100644 --- a/backend/apps/agents/browser/browser_loop.py +++ b/backend/apps/agents/browser/browser_loop.py @@ -10,6 +10,8 @@ prevents the model from burning the entire turn budget on a failing approach. import json import re +from backend.apps.agents.browser import browser_send_parse + # Tools that are read-only / idempotent and should NOT count toward loop detection. Repeating these is normal (scrolling through a feed, taking successive screenshots, polling for an element to appear). LOOP_DETECTION_EXCLUDED_TOOLS = { "BrowserScreenshot", @@ -322,6 +324,14 @@ def is_publish_task(task: str) -> bool: call a perfectly good read a failure.""" if not P_PUBLISH_INTENT_RE.search(task or ""): return False + # An explicit read-only directive settles it: the user said "do NOT submit anything", so nothing + # was ever supposed to leave, and demanding a send receipt turns a correct read into a reported + # failure. Measured live on reddit's own discovery probe ("Do NOT type or submit anything. Is + # the post title/body compose form present?"), which the informational heuristic below scored as + # a publish and this gate then failed. Reuses the SAME authority the send script declines on, + # rather than growing a second opinion that can drift away from it. + if browser_send_parse.is_readonly(task or ""): + return False return not deliverable_is_informational("", task) diff --git a/backend/tests/test_browser_stagnation.py b/backend/tests/test_browser_stagnation.py index e1f6b0ad..d71459eb 100644 --- a/backend/tests/test_browser_stagnation.py +++ b/backend/tests/test_browser_stagnation.py @@ -244,3 +244,25 @@ def test_publish_verbs_are_also_ordinary_nouns(): 'click the Search button', ): assert is_publish_task(task) is False, task + + +def test_an_explicit_read_only_directive_is_never_a_publish(): + """The user saying "do NOT submit anything" settles it, whatever verbs the rest of the sentence + carries. Measured live 2026-07-31: the reddit canary's own discovery probe, which is read-only + by construction, scored as a publish and this gate reported "the send was never confirmed" on a + probe that was never supposed to send. The informational heuristic alone missed it, because the + task is stuffed with publish nouns ("post title/body compose form", "/r/test/submit"). + + Uses the same is_readonly authority the send script declines on, rather than a second opinion + that can drift away from it. Direction is safe: this can only ever REMOVE a publish + classification, so it can never newly flag a run that genuinely sent something.""" + for task in ( + 'Go to reddit.com/r/test/submit. Do NOT type or submit anything. Is the post title/body ' + 'compose form present? Answer with exactly one word: YES or NO.', + 'Go to x.com/me. Do NOT post, delete or change anything. Is there a post containing "abc"?', + 'Go to my LinkedIn activity. Do NOT post, delete or change anything. Is there a post ' + 'titled "canary"? Answer GONE or PRESENT.', + ): + assert is_publish_task(task) is False, task + # ...and a real write with no read-only directive still gates. + assert is_publish_task('Go to reddit and submit a text post titled "x" with body "y"') is True