Files
openswarm/backend/tests/test_delegated_hard_ceiling.py
T

79 lines
3.9 KiB
Python

"""A delegated run must not be killed by a deadline meant for a millisecond tool.
Field, 2026-09-01: browser runs on a HEALTHY, heartbeating sidecar were killed at 450s and 600s
outstanding, each ending the turn mute in front of the user. The exemption for delegated tools was
real but only governed the 25s arming; `wedge_verdict` never received the tool name, so the single
300s ceiling killed them anyway. That is a deadline on the wrong unit of work: a quick tool answers
in milliseconds, a delegated run takes as long as the browser it handed off to.
The protection that actually matters is untouched: a stale heartbeat is a wedged PROCESS and still
dies in seconds, at any age. This ceiling is only the backstop for "alive but never returns".
"""
import pytest
from backend.apps.agents.manager.streaming.unwedge_sidecar import (
DELEGATED_HARD_WEDGE_SECONDS, HARD_WEDGE_SECONDS, HEARTBEAT_FRESH_S,
hard_ceiling_for, wedge_verdict,
)
BROWSER = "mcp__openswarm-core__BrowserAgent"
CREATE = "mcp__openswarm-core__CreateBrowserAgent"
QUICK = "mcp__openswarm-core__MemoryWrite"
@pytest.mark.parametrize("outstanding", [450.0, 600.0])
def test_the_two_kills_from_the_field_now_survive(outstanding):
assert wedge_verdict(outstanding, 1.0, BROWSER) == "extend"
def test_a_quick_tool_keeps_the_old_ceiling_exactly():
assert hard_ceiling_for(QUICK) == HARD_WEDGE_SECONDS
assert wedge_verdict(HARD_WEDGE_SECONDS, 1.0, QUICK) == "kill"
def test_delegated_tools_get_the_longer_one():
for t in (BROWSER, CREATE, "mcp__openswarm-core__AppAgent", "mcp__openswarm-core__BrowserAgents"):
assert hard_ceiling_for(t) == DELEGATED_HARD_WEDGE_SECONDS, t
def test_a_dead_sidecar_still_dies_in_seconds_even_for_a_delegated_tool():
"""The half that keeps this safe: a stale heartbeat means the PROCESS is gone, and no exemption
may protect that. Without this the change would trade a false kill for a real hang."""
assert wedge_verdict(30.0, HEARTBEAT_FRESH_S + 1, BROWSER) == "kill"
assert wedge_verdict(1.0, 999.0, BROWSER) == "kill"
def test_a_delegated_run_is_still_bounded_eventually():
"""Not unbounded: a hung call must not hang the session forever, it just gets measured in the
delegated run's own units."""
assert wedge_verdict(DELEGATED_HARD_WEDGE_SECONDS, 1.0, BROWSER) == "kill"
assert DELEGATED_HARD_WEDGE_SECONDS > HARD_WEDGE_SECONDS
def test_only_a_DECLARED_delegation_tool_earns_the_longer_ceiling():
"""Fail safe toward today's behaviour: an unknown name, an empty one, or a non-core tool keeps
the strict 300s, so a caller that forgets to pass the name cannot silently buy 30 minutes.
The protection against the BrowserAgents-class bug (a real delegation tool left out of the
exemption and killed at the ceiling) is NOT a lenient default, it is the single shared list."""
assert hard_ceiling_for("mcp__openswarm-core__SomethingAddedLater") == HARD_WEDGE_SECONDS
assert hard_ceiling_for("") == HARD_WEDGE_SECONDS
assert hard_ceiling_for("Bash") == HARD_WEDGE_SECONDS
def test_the_exemption_list_is_the_one_shared_list_not_a_second_copy():
"""Two lists of names that must agree will not; that drift is what cost weeks of browser runs."""
from backend.apps.agents.manager.streaming import unwedge_sidecar as mod
from backend.apps.agents.manager.delegation_tool_names import BLOCKING_TOOLS
assert mod.P_BLOCKING_TOOLS is BLOCKING_TOOLS, "it must be imported, never restated"
for t in ("BrowserAgent", "BrowserAgents", "CreateBrowserAgent", "AppAgent"):
assert t in BLOCKING_TOOLS
def test_the_call_site_passes_the_tool_name():
"""The whole bug was that it did not. A test on wedge_verdict alone would still pass."""
import inspect
from backend.apps.agents.manager.streaming import unwedge_sidecar as mod
src = inspect.getsource(mod)
assert "wedge_verdict(outstanding, heartbeat_age(session_id), tool_name)" in src