Skip to content

Commit fefaab7

Browse files
authored
Update test_headless.py
1 parent 4c0347c commit fefaab7

1 file changed

Lines changed: 127 additions & 9 deletions

File tree

‎tests/entry/test_headless.py‎

Lines changed: 127 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
run_headless_jsonl,
2626
signal_canceller,
2727
)
28+
from python_agent_harness.entry.view import FilteredDeltaStream
29+
from python_agent_harness.io.text_filter import strip_final_check
2830
from tests.support import (
2931
plan_cleanup, # noqa: F401,E402 (side-effect: auto-remove /tmp plan dirs)
3032
session_sandbox, # noqa: F401,E402 (side-effect: redirect SESSION_DIR)
@@ -478,19 +480,24 @@ def test_events_emitted_as_json_lines(self):
478480
self.assertEqual(
479481
lines[0], {"seq": 1, "type": "start", "prompt": "do it", "warnings": ["w1"]}
480482
)
481-
self.assertEqual(lines[1], {"seq": 2, "type": "delta", "text": "hello "})
483+
# "hello " streams as "hello": the delta filter holds trailing
484+
# whitespace (strip_final_check would rstrip it away if a
485+
# [FINAL CHECK] block followed) and releases it at the message
486+
# boundary below.
487+
self.assertEqual(lines[1], {"seq": 2, "type": "delta", "text": "hello"})
488+
self.assertEqual(lines[2], {"seq": 3, "type": "delta", "text": " "})
482489
self.assertEqual(
483-
lines[2], {"seq": 3, "type": "notify", "kind": "tool_start", "data": ["Read"]}
490+
lines[3], {"seq": 4, "type": "notify", "kind": "tool_start", "data": ["Read"]}
484491
)
485492
self.assertEqual(
486-
lines[3], {"seq": 4, "type": "notify", "kind": "error", "data": "no quota"}
493+
lines[4], {"seq": 5, "type": "notify", "kind": "error", "data": "no quota"}
487494
)
488-
self.assertEqual(lines[4], {"seq": 5, "type": "log", "message": "warming up"})
495+
self.assertEqual(lines[5], {"seq": 6, "type": "log", "message": "warming up"})
489496
# the error seen during the run is reported on the result line
490497
self.assertEqual(
491-
lines[5],
498+
lines[6],
492499
{
493-
"seq": 6,
500+
"seq": 7,
494501
"type": "result",
495502
"answer": "Done.",
496503
"errors": ["no quota"],
@@ -500,6 +507,113 @@ def test_events_emitted_as_json_lines(self):
500507
# each line is compact single-line JSON: no embedded newlines
501508
self.assertTrue(all("\n" not in line for line in out.getvalue().splitlines()))
502509

510+
def test_delta_stream_filters_final_check_block(self):
511+
"""The [FINAL CHECK] block never reaches the delta stream, not even
512+
as a half-streamed prefix: the tail is withheld until the block is
513+
ruled out, so concatenated deltas equal the filtered answer."""
514+
out = io.StringIO()
515+
view = JsonlView(out=out)
516+
view.emit_start("p", [])
517+
view.on_delta("Done. ")
518+
view.on_delta("\n\n[FINAL")
519+
view.on_delta(" CHECK]\n- Goal: g\n- Status: SUCCESS\n- Evidence: e")
520+
view.emit_result("Done.")
521+
lines = self._lines(out)
522+
deltas = [line["text"] for line in lines if line["type"] == "delta"]
523+
# exactly what the TUI shows -- no "[FINAL" fragment leaks out
524+
self.assertEqual("".join(deltas), "Done.")
525+
full = "Done. \n\n[FINAL CHECK]\n- Goal: g\n- Status: SUCCESS\n- Evidence: e"
526+
self.assertEqual("".join(deltas), strip_final_check(full))
527+
528+
def test_delta_stream_releases_tail_when_block_ruled_out(self):
529+
"""A header that never completes into a block is real content and
530+
must still be streamed (released when the message ends)."""
531+
out = io.StringIO()
532+
view = JsonlView(out=out)
533+
view.emit_start("p", [])
534+
view.on_delta("see the ")
535+
view.on_delta("[final check] section")
536+
view.emit_result("x")
537+
lines = self._lines(out)
538+
deltas = [line["text"] for line in lines if line["type"] == "delta"]
539+
self.assertEqual("".join(deltas), "see the [final check] section")
540+
541+
def test_delta_stream_holds_only_ambiguous_tail(self):
542+
"""Ordinary prose streams through unheld: a bracket that cannot
543+
start a header is not mistaken for a block in flight."""
544+
out = io.StringIO()
545+
view = JsonlView(out=out)
546+
view.emit_start("p", [])
547+
view.on_delta("line one\n")
548+
view.on_delta("see [1] and [2] here")
549+
lines = self._lines(out)
550+
deltas = [line["text"] for line in lines if line["type"] == "delta"]
551+
self.assertEqual("".join(deltas), "line one\nsee [1] and [2] here")
552+
553+
def test_delta_stream_resets_on_tool_start_and_retry(self):
554+
"""A message boundary starts a fresh filtered stream: the next
555+
message's [FINAL CHECK] must not be confused with the previous
556+
message's already-emitted text."""
557+
out = io.StringIO()
558+
view = JsonlView(out=out)
559+
view.emit_start("p", [])
560+
view.on_delta("First answer.")
561+
view.on_notify("tool_start", ["Bash"])
562+
view.on_delta(
563+
"Second answer.\n\n[FINAL CHECK]\n- Goal: g\n- Status: SUCCESS\n- Evidence: e"
564+
)
565+
view.emit_result("x")
566+
lines = self._lines(out)
567+
deltas = [line["text"] for line in lines if line["type"] == "delta"]
568+
self.assertEqual("".join(deltas), "First answer.Second answer.")
569+
570+
def test_delta_stream_retry_discards_partial(self):
571+
out = io.StringIO()
572+
view = JsonlView(out=out)
573+
view.emit_start("p", [])
574+
view.on_delta("partial ")
575+
view.on_notify("retry")
576+
view.on_delta("fresh start")
577+
view.emit_result("x")
578+
lines = self._lines(out)
579+
deltas = [line["text"] for line in lines if line["type"] == "delta"]
580+
self.assertEqual("".join(deltas), "partial fresh start")
581+
582+
def test_delta_stream_parity_holds_for_every_chunk_split(self):
583+
"""The invariant: concatenating the emitted chunks equals
584+
strip_final_check over the whole message, whatever the network
585+
chose as chunk boundaries. Checked exhaustively for every single
586+
split point plus the char-by-char worst case, because the leak
587+
this guards against only appears at specific boundaries (a
588+
half-streamed header, a decoration run before one)."""
589+
messages = [
590+
"Done. \n\n[FINAL CHECK]\n- Goal: g\n- Status: SUCCESS\n- Evidence: e",
591+
"A.\n\n**[FINAL CHECK]**\n- **Goal**: g\n- **Status**: s\n- **Evidence**: e",
592+
"A.\n\n## Final Check\nGoal: g\nStatus: s\nEvidence: e",
593+
"a\n\n> **[FINAL CHECK]**\n> Goal: g\n> Status: s\n> Evidence: e",
594+
"a\n\n> \n[FINAL CHECK]\nGoal: g\nStatus: s\nEvidence: e",
595+
"s**[FINAL CHECK]**Goal: g Status: s Evidence: e",
596+
"[FINAL CHECK]\n- Goal: g\n- Status: s\n- Evidence: e", # check-only
597+
"see the [final check] section", # header that never completes
598+
"let me do the final check now",
599+
"line one\nsee [1] and [2] here",
600+
"use 2 * 3 and a_b and #1 in prose",
601+
]
602+
for msg in messages:
603+
want = strip_final_check(msg)
604+
# every single split point, and one chunk per character
605+
splits = [[i] for i in range(1, len(msg))] + [list(range(1, len(msg)))]
606+
for cuts in [[]] + splits:
607+
stream = FilteredDeltaStream()
608+
parts, prev = [], 0
609+
for cut in cuts:
610+
parts.append(msg[prev:cut])
611+
prev = cut
612+
parts.append(msg[prev:])
613+
got = "".join(stream.feed(p) for p in parts) + stream.flush()
614+
with self.subTest(msg=msg[:30], cuts=cuts[:3]):
615+
self.assertEqual(got, want)
616+
503617
def test_events_before_start_are_buffered_behind_start_line(self):
504618
"""The worker starts before emit_start runs, so early events must
505619
be buffered and flushed after start — start is always first."""
@@ -515,11 +629,15 @@ def test_events_before_start_are_buffered_behind_start_line(self):
515629
view.emit_result("ok")
516630
lines = [json.loads(line) for line in out.getvalue().splitlines() if line]
517631
self.assertEqual(lines[0]["type"], "start")
518-
self.assertEqual(lines[1], {"seq": 2, "type": "delta", "text": "early "})
632+
# trailing whitespace is held by the delta filter and released at
633+
# the tool_start boundary, so "early " arrives as two deltas --
634+
# buffering preserves their order behind the start line
635+
self.assertEqual(lines[1], {"seq": 2, "type": "delta", "text": "early"})
636+
self.assertEqual(lines[2], {"seq": 3, "type": "delta", "text": " "})
519637
self.assertEqual(
520-
lines[2], {"seq": 3, "type": "notify", "kind": "tool_start", "data": ["Bash"]}
638+
lines[3], {"seq": 4, "type": "notify", "kind": "tool_start", "data": ["Bash"]}
521639
)
522-
self.assertEqual(lines[3], {"seq": 4, "type": "delta", "text": "live"})
640+
self.assertEqual(lines[4], {"seq": 5, "type": "delta", "text": "live"})
523641
self.assertEqual(lines[-1]["type"], "result")
524642

525643
def test_concurrent_emit_and_start_never_corrupts_stream(self):

0 commit comments

Comments
 (0)