From 98ea4c4f545b3e8a81cc412ece0191c154012dba Mon Sep 17 00:00:00 2001 From: Devika Verma Date: Mon, 17 Aug 2026 18:01:38 +0530 Subject: [PATCH] render_history: label ask_user replies 'reply', not 'turn N' A turn is a message the user sent on their own; labelling an answer as one reads as a spontaneous statement - stronger evidence than it is. Turn numbering now counts real messages only. --- coworker/reviewer.py | 15 +++++++++++---- tests/test_auto_approve.py | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/coworker/reviewer.py b/coworker/reviewer.py index 47b6d27e..0767e200 100644 --- a/coworker/reviewer.py +++ b/coworker/reviewer.py @@ -202,16 +202,23 @@ def render_history(user_messages: list[dict[str, Any]]) -> str: """The EARLIER-IN-THIS-SESSION block: the user's own words, mechanically extracted, clipped hard, with `ask_user` replies tagged as replies (§8.2). `user_messages` is a list of {"text": str, "is_reply": bool} in chronological order, current turn excluded. - """ + + Replies are labelled `reply`, never `turn N`: a "turn" is a message the user sent on + their own, and labelling an answer as one would read as a spontaneous statement — + stronger evidence than it is. Turn numbering counts real messages only.""" if not user_messages: return "" lines = ["EARLIER IN THIS SESSION (the user's own words, verbatim)"] - for i, msg in enumerate(user_messages, start=1): + turn = 0 + for msg in user_messages: text = clip_message(str(msg.get("text", ""))) if not text: continue - tag = " [reply to a question the agent asked]" if msg.get("is_reply") else "" - lines.append(f" turn {i} {text}{tag}") + if msg.get("is_reply"): + lines.append(f" reply {text} [reply to a question the agent asked]") + else: + turn += 1 + lines.append(f" turn {turn} {text}") return "\n".join(lines) if len(lines) > 1 else "" diff --git a/tests/test_auto_approve.py b/tests/test_auto_approve.py index 2c007540..f836646b 100644 --- a/tests/test_auto_approve.py +++ b/tests/test_auto_approve.py @@ -158,6 +158,21 @@ def test_reply_tag_is_rendered(): assert "[reply to a question the agent asked]" in rendered +def test_replies_are_labelled_reply_and_turn_numbering_skips_them(): + # A reply is not a "turn": labelling it as one would read as a spontaneous statement. + rendered = reviewer_mod.render_history( + [ + {"text": "fix the tests"}, + {"text": "yes", "is_reply": True}, + {"text": "update the changelog"}, + ] + ) + lines = rendered.splitlines()[1:] + assert lines[0].startswith(" turn 1 fix the tests") + assert lines[1].startswith(" reply yes") and "turn" not in lines[1] + assert lines[2].startswith(" turn 2 update the changelog") # numbering skipped the reply + + # -- ask_user answers reach the reviewer history (§8.2 reply capture) --------------