@@ -492,3 +492,64 @@ def test_site3_keeps_the_self_pagination_pointer(processor, tool: str) -> None:
492492 # work instead of being handed the path or offset that continues it.
493493 assert "/spill/abc123.md" in out , f"{ tool } : recovery pointer was cut"
494494 assert out == body , f"{ tool } : body was shortened despite fitting gate ①"
495+
496+
497+ _EXEC_TOOLS = ["bash" , "run_python_code" ]
498+
499+
500+ @pytest .mark .parametrize ("processor" , _processors ())
501+ @pytest .mark .parametrize ("tool" , _EXEC_TOOLS )
502+ def test_the_exec_pointer_survives_the_default_gate_1 (
503+ processor , tool : str , spill_store
504+ ) -> None :
505+ """gate ② must not cut below the gate ① preview it is protecting.
506+
507+ ``bash``/``run_python_code`` overflow to disk at gate ①
508+ (``meta.max_result_chars`` = ``tool_exec_result_max_chars``, 8K by default)
509+ and the preview that comes back carries both the recovery pointer and, for
510+ bash, the exit status. Those live at the tail. If gate ②'s budget for these
511+ tools is a hardcoded number below gate ①, it takes the pointer and the
512+ verdict with it and the agent is left with a truncated result it cannot
513+ recover — the exact failure the 10K grep/glob entries above exist to prevent,
514+ one layer down.
515+ """
516+ from frontier_agent .core .loop_types import ToolResult
517+ from frontier_agent .infra .config import get_config
518+ from plugins .tools import _overflow
519+
520+ cap = get_config ().tool_exec_result_max_chars
521+ body = _overflow .maybe_overflow (tool , _pytest_output (cap * 3 ))
522+ assert "saved read-only at" in body , f"{ tool } : fixture did not overflow"
523+
524+ out = processor .process (ToolResult (
525+ name = tool , args = {}, result = body , duration_ms = 0 ,
526+ tool_call_id = "call-1" , is_error = False ,
527+ ))
528+
529+ assert "saved read-only at" in out , f"{ tool } : gate ② cut gate ①'s recovery pointer"
530+ assert "1 failed, 812 passed" in out , f"{ tool } : gate ② cut gate ①'s verdict"
531+
532+
533+ @pytest .mark .parametrize ("processor" , _processors ())
534+ @pytest .mark .parametrize ("tool" , _EXEC_TOOLS )
535+ def test_gate_2_follows_a_raised_exec_cap (processor , tool : str , monkeypatch ) -> None :
536+ """Raising ``TOOL_EXEC_RESULT_MAX_CHARS`` has to widen gate ② too.
537+
538+ The cap is documented as tunable (docs/tool-result-truncation-ab.md runs the
539+ A/B at 8000 vs 50000). A deployment that raises it to give long scans more
540+ room must actually get more room; a fixed gate ② would silently cap the gain
541+ at its own hardcoded number.
542+ """
543+ from frontier_agent .core .loop_types import ToolResult
544+ from frontier_agent .infra .config import get_config
545+
546+ monkeypatch .setattr (get_config (), "tool_exec_result_max_chars" , 50_000 )
547+ body = _pytest_output (40_000 )
548+
549+ out = processor .process (ToolResult (
550+ name = tool , args = {}, result = body , duration_ms = 0 ,
551+ tool_call_id = "call-1" , is_error = False ,
552+ ))
553+
554+ # 50K + 2K headroom covers the 40K body, so nothing is cut.
555+ assert out == body , f"{ tool } : a raised gate ① did not widen gate ②"
0 commit comments