diff --git a/backend/tests/test_browser_classifier_lane.py b/backend/tests/test_browser_classifier_lane.py
new file mode 100644
index 00000000..fc797e46
--- /dev/null
+++ b/backend/tests/test_browser_classifier_lane.py
@@ -0,0 +1,43 @@
+"""The fast-path classifier must not let a mute provider disable the browser fast path.
+
+Measured live 2026-07-26: on the codex lane the aux resolves to cx/gpt-5.4-mini, which returns an
+EMPTY body for this call. Empty parsed to verdict 'no', which reads identically to "this is not a
+browser task", so the entire browser fast path silently switched off for every GPT user with no
+error to show for it (gpt-5.4 filled 0/3; after the empty-body fallback, 2/2). These tests pin both
+halves of the contract: the parser may call empty 'no', and the caller must not accept that as a
+verdict when a provider was pinned.
+"""
+from backend.apps.agents.browser import browser_fast_path as fp
+
+CLAUDE_REPLY = "ACT\n\nENTRY: https://x.com/home\n\n1. Navigate to X.\n2. Click the composer."
+
+
+def test_empty_body_parses_as_no() -> None:
+ """The parser is allowed to say 'no' on empty; the BUG was the caller treating that as a real
+ verdict instead of a mute lane."""
+ assert fp.parse_verdict_and_brief("") == ("no", "")
+
+
+def test_whitespace_only_body_is_treated_as_empty() -> None:
+ """A lane answering with only whitespace is exactly as mute as one answering ''."""
+ assert fp.parse_verdict_and_brief(" \n\t \n") == ("no", "")
+
+
+def test_real_verdict_still_parses() -> None:
+ verdict, brief = fp.parse_verdict_and_brief(CLAUDE_REPLY)
+ assert verdict == "act"
+ assert "ENTRY: https://x.com/home" in brief
+
+
+def test_read_verdict_still_parses() -> None:
+ verdict, _ = fp.parse_verdict_and_brief("READ\n\nENTRY: https://example.com")
+ assert verdict == "read"
+
+
+def test_caller_retries_provider_agnostic_on_a_mute_lane() -> None:
+ """The fix itself: classify_and_brief must re-ask WITHOUT the provider pin when the pinned lane
+ returns nothing. Pinned by source so the retry cannot be quietly deleted."""
+ import inspect
+ src = inspect.getsource(fp.classify_and_brief)
+ assert "if not text.strip() and primary_api:" in src, "empty-body fallback missing"
+ assert "p_ask(None)" in src, "fallback must drop the provider pin"
diff --git a/backend/tests/test_browser_signed_out.py b/backend/tests/test_browser_signed_out.py
new file mode 100644
index 00000000..7bc3c27a
--- /dev/null
+++ b/backend/tests/test_browser_signed_out.py
@@ -0,0 +1,82 @@
+"""Soft signed-out detection, plus the two invariants behind the 2026-07-26 bug hunt.
+
+Each test here corresponds to a bug that actually escaped to a live run, so the point is to make
+that whole CLASS unwritable rather than to re-check a line:
+
+ 1. Sites that browse fine while withholding the composer (bsky/stackoverflow/tiktok/threads) hit
+ no login URL and show no password field, so the hard-wall gate saw nothing and the run
+ reported "couldn't find the compose box" when the truth was "you are not signed in".
+ 2. The composer finder budgeted ~24s of reveal work into a 15s command timeout, so heavy pages
+ were killed mid-ladder and threw away everything, and the last two tiers were unreachable.
+"""
+import os
+import re
+
+from backend.apps.agents.browser import browser_send_parse as sp
+from backend.apps.agents.browser import browser_login_handoff as lh
+from backend.apps.agents.core.ws_manager import BROWSER_CMD_TIMEOUTS, BROWSER_CMD_TIMEOUT_DEFAULT
+
+P_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+P_HANDLER_TS = os.path.join(P_REPO_ROOT, "frontend", "src", "shared", "browserCommandHandler.ts")
+
+# Page-shaped perceptions, in the interactives format the agent actually sees.
+BSKY_SIGNED_OUT = '[1]\n[2]