Skip to content

Commit ac28646

Browse files
committed
Stop hiding response which contains only partial verification keywors.
1 parent e3d7451 commit ac28646

2 files changed

Lines changed: 45 additions & 22 deletions

File tree

python_agent_harness/tui.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -147,15 +147,20 @@ def _strip_final_check(text: str) -> str:
147147
148148
- an explicit "[FINAL CHECK]" header: everything from it is dropped;
149149
- the bare checklist: a trailing block of Goal:/Status:/Evidence:
150-
bullets with at least 2 distinct labels (fallback for replies
151-
that answer the completion rules without the header).
152-
153-
The agent loop still produces and stores the message unchanged;
154-
this only trims it from the TUI display.
150+
bullets with ALL THREE labels (fallback for replies that answer
151+
the completion rules without the header; requiring all three
152+
avoids eating genuine summary bullets like "- Status: ...").
153+
154+
The strip NEVER empties a reply: if the check block is the whole
155+
message (no real content before it), the message is kept intact —
156+
hiding it would hide the model's only visible response. The agent
157+
loop still produces and stores the message unchanged; this only
158+
trims it from the TUI display.
155159
"""
156160
idx = text.find("[FINAL CHECK]")
157161
if idx != -1:
158-
return text[:idx].rstrip()
162+
prefix = text[:idx].rstrip()
163+
return prefix if prefix else text
159164
lines = text.splitlines()
160165
labels: set[str] = set()
161166
first: int | None = None
@@ -166,13 +171,14 @@ def _strip_final_check(text: str) -> str:
166171
labels.add(label)
167172
if first is None:
168173
first = i
169-
if len(labels) < 2 or first is None:
174+
if len(labels) < len(_FINAL_CHECK_LABELS) or first is None:
170175
return text
171176
# only a TRAILING bullet block is the completion check — bullets in
172177
# the middle of a real reply (followed by more content) stay visible
173178
if not all(not line.strip() or _is_bullet_line(line) for line in lines[first:]):
174179
return text
175-
return "\n".join(lines[:first]).rstrip()
180+
prefix = "\n".join(lines[:first]).rstrip()
181+
return prefix if prefix else text
176182

177183

178184
def _strip_reasoning(text: str, reasoning: str) -> str:

tests/test_tui.py

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,11 @@ def test_nudge_and_final_check_hidden(self):
168168
out = buf.getvalue()
169169
self.assertIn("build the thing", out)
170170
self.assertIn("Done. All tests pass.", out)
171-
self.assertNotIn("FINAL CHECK", out)
172-
self.assertNotIn("Status: SUCCESS", out)
171+
# the [FINAL CHECK] block on a content-carrying reply is hidden,
172+
# but a reply that IS only the check block stays visible — the
173+
# model's only visible response must not vanish
174+
self.assertNotIn("FINAL CHECK\n- Goal: build the thing", out)
175+
self.assertIn("Status: SUCCESS", out)
173176
self.assertNotIn(config.NUDGE_MESSAGE, out)
174177
# the agent loop's history is untouched
175178
self.assertEqual(
@@ -185,7 +188,7 @@ def test_nudge_and_final_check_hidden(self):
185188
def test_final_check_without_header_stripped(self):
186189
"""A reply that answers the completion rules WITHOUT the
187190
[FINAL CHECK] header (just the checklist bullets) is hidden too:
188-
at least 2 of Goal:/Status:/Evidence: in the trailing block."""
191+
all three of Goal:/Status:/Evidence: in the trailing block."""
189192
tui, buf = make_tui()
190193
tui.session.last_messages = [
191194
Message(role="user", content="build the thing"),
@@ -202,9 +205,9 @@ def test_final_check_without_header_stripped(self):
202205
self.assertNotIn("Status: SUCCESS", out)
203206
self.assertNotIn("Evidence:", out)
204207

205-
def test_final_check_pure_bullets_stripped(self):
206-
"""A pure-checklist reply (no header, no content, "•" bullets)
207-
vanishes entirely from the panel."""
208+
def test_final_check_pure_bullets_kept(self):
209+
"""A pure-checklist reply (no header, no other content) stays
210+
visible — stripping it would hide the model's only response."""
208211
tui, buf = make_tui()
209212
tui.session.last_messages = [
210213
Message(role="user", content="build the thing"),
@@ -215,23 +218,28 @@ def test_final_check_pure_bullets_stripped(self):
215218
]
216219
tui.console.print(tui._render_conversation())
217220
out = buf.getvalue()
218-
self.assertNotIn("Status", out)
219-
self.assertNotIn("Evidence", out)
220-
221-
def test_regular_reply_mentioning_one_label_kept(self):
222-
"""A genuine reply mentioning a single checklist label is NOT
223-
stripped — at least 2 distinct labels are required."""
221+
self.assertIn("Status: SUCCESS", out)
222+
self.assertIn("Evidence: done", out)
223+
self.assertIn("Goal: build it", out)
224+
225+
def test_partial_checklist_kept(self):
226+
"""A genuine reply ending in only two checklist-looking bullets
227+
is NOT stripped — all three labels are required for the
228+
no-header fallback, so real summary bullets survive (also
229+
covers the single-label case, which takes the same path)."""
224230
tui, buf = make_tui()
225231
tui.session.last_messages = [
226232
Message(role="user", content="build the thing"),
227233
Message(
228234
role="assistant",
229-
content="Goal: build the thing. Now verifying the build...",
235+
content="Deployed.\n- Status: SUCCESS\n- Goal: deploy the service",
230236
),
231237
]
232238
tui.console.print(tui._render_conversation())
233239
out = buf.getvalue()
234-
self.assertIn("Goal: build the thing", out)
240+
self.assertIn("Deployed.", out)
241+
self.assertIn("Status: SUCCESS", out)
242+
self.assertIn("Goal: deploy the service", out)
235243

236244
def test_final_check_mid_message_not_stripped(self):
237245
"""Checklist bullets in the MIDDLE of a reply (followed by real
@@ -258,6 +266,15 @@ def test_stream_final_check_hidden(self):
258266
self.assertIn("Working...", row.plain)
259267
self.assertNotIn("Status:", row.plain)
260268

269+
def test_stream_pure_final_check_kept(self):
270+
"""A stream that is only the check block stays on screen — the
271+
stream row must never go blank on the final reply."""
272+
tui, _ = make_tui()
273+
tui.stream_text = "[FINAL CHECK]\n- Status: SUCCESS"
274+
row = tui._stream_row()
275+
self.assertIsNotNone(row)
276+
self.assertIn("Status: SUCCESS", row.plain)
277+
261278
def test_reasoning_streams_normally(self):
262279
"""While streaming, reasoning content shows up live like any
263280
other text — the collapse only happens in the final history."""

0 commit comments

Comments
 (0)