Repository navigation
fix(acp_client): end the reply's message where the live view does - #892
Conversation
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
Reviewed the full github/main...HEAD diff and the surrounding ACP collector, reply-clamping, activity/history, transcript, plan, tool-call, and steering paths. I also checked the relevant history and #889 behavior, repository rules and canonical terminology, backward compatibility/risk, and that the changed assertion reflects the intended steer semantics rather than weakened coverage. No pre-existing or style-only issues are being raised.
Verification: uv run --frozen --all-extras --python 3.12 pytest -q -p no:randomly tests/test_subagent_acp.py tests/test_rpc_dag.py tests/test_rpc_subagent_calls.py tests/test_subagent_history.py passed (408 tests); uv run --frozen --all-extras --python 3.12 pytest tests/integration/test_tui_rpc_steer_e2e.py -q -p no:randomly -o addopts="" passed (2 tests); source-language, large-file, focused Ruff lint, and Ruff format checks passed.
|
Not a blocker -- smaller items from the acceptance pass on head 8149f45 (merged onto main c3d2821), beside the blocking thread on the held-prompt heartbeat. Severity of this one finding, not a verdict on the pull request. The blocking findings from this pass are review threads on the changed files; GitHub renders those collapsed, as a file name with no text.
Checked on the merged tree: the machine gates (ruff, lint-imports 10 kept / 0 broken, commit, large-file and language checks) pass, and tests/test_subagent_acp.py + tests/test_acp_methods.py give 434 passed. |
8149f45 to
73a81d9
Compare
|
@0xKT both items from the note above reproduced on 8149f45 before any change, with main c3d2821 as the control. The blocking thread is answered in its own thread.
Rebased onto main 289426c (#709, #857, #868, #894 and #859; only #709 shares a file with this branch, CONTEXT.md, in a different entry): 0b4f945 -> 765704c and 8149f45 -> 87692c0, per-commit patch-ids unchanged. At the new head 73a81d9: tests/test_subagent_acp.py + tests/test_acp_methods.py 442 passed; the full suite 27864 passed / 7 failed / 120 skipped, and main 289426c, run the same day in the same tree and interpreter, gives 27854 / 7 / 120 with the same 7 failing IDs (five proxy tests, the npm-leak install test, the root-only node-runtime test), so none is introduced; ruff, ty, lint-imports (10 kept, 0 broken) and the commit, large-file and language gates are clean. |
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
Reviewed the refreshed github/main...HEAD diff and the full delta from 8149f4514e74, including the ACP producer/collector boundary, reply and streaming budgets, transcript/history callers, canonical context/spec text, backward compatibility, and whether the tests preserve rather than weaken the prior behavior. On this head the held-prompt heartbeat no longer splits the wake report; opening calls, terminal and status-less updates, and plan openings still end messages, while steer separators consume the same message budget counted by the reply cap. I found no additional defect.
Verification: the combined ACP/RPC/history suite passed (566 passed); the TUI steering integration passed (2 passed); source-language, large-file, commit-message, focused Ruff lint/format, and focused ty checks passed. I did not resolve the existing review thread because another reviewer opened it.
|
Not a blocker -- two small items from the re-acceptance of head 73a81d9 (merged onto main 6126965); the heartbeat fix and the rest of the delta hold. Severity of this one finding, not a verdict on the pull request. The blocking findings from this pass are review threads on the changed files; GitHub renders those collapsed, as a file name with no text.
|
A steer that landed inside the final message cut the reply to the words after it: the reply's walk stopped at a steer, while raven puts a steer on the wire as it arrives and reads it only before its next model call. And text said while a call was open joined the next message with nothing between them, because the live view ended a message at any tool frame while the reply ended one only where a call opened. A message now ends only at a step, and the stream's per-message budget and the reply read the same message starts. A steer is set apart by its break inside the message. CONTEXT.md says what the reply is, and that a turn that reports, saves and signs off returns the sign-off. Co-authored-by: Claude (claude-opus-5-5[1m]) <noreply@anthropic.com>
bounded_delta's docstring promised that what streams stays a prefix of what returns, which acp narration no longer does: the acp collector wraps each message in a budget of its own, and its break between messages is exempt with the truncation notice. And out.md keeps the whole reply, before the cap the caller's copy went through. Co-authored-by: Claude (claude-opus-5-5[1m]) <noreply@anthropic.com>
Raven's own held prompt beats a tool_call_update with status in_progress on its open wait call every 60 seconds while the wake turn's report streams on the same session. Every tool_call_update ended the message, and the reply is the last message, so a beat that landed mid-report cut the reply to the words after it: a raven-oncall node's out.md kept only the tail of its final report while closing.md kept all of it. Main returned the report whole and only put a stray break into the live view; that break is gone too. An update that says its call is still running (pending or in_progress) now ends nothing. The opening tool_call says in_progress as well and still ends the message before the call. A status-less update still ends one: claude-agent-acp reports a finished call's result on an update with no status, from its PostToolUse hook. A test in TestHeldTurn drives the real hold, heartbeat and translator and replays every frame into the real collector, so either side of that agreement changing alone turns it red. The codex tool-parsing spec said every member of _BREAKING_UPDATES breaks unconditionally; it now names the exception. Co-authored-by: Claude (claude-opus-5-5[1m]) <noreply@anthropic.com>
A steer's break sits inside the message the reply returns, and the reply's cap counts it, but the stream sent it past the message's budget. A steered message within two characters per steer of the cap therefore streamed whole and came back cut, with the truncation notice under a message the screen had shown complete. The steer's break now spends the budget of the message it sits in. The break between two messages still rides past it: that one comes before the message it opens and is in no reply, and counting it would cut the last characters of a reply that fits. The steer test's stream assertion flips with it: it expected the break to spend nothing. Co-authored-by: Claude (claude-opus-5-5[1m]) <noreply@anthropic.com>
Two boundaries the reply reads had no test that a change to them would turn red: the steer flag's reset, which sets apart only the words right after a steer, and a plan opening ending the message said before it. The plan test fed its first plan frame with nothing said before it, so the opening's break was never observable; it now has text before the frame and asserts both halves of its name. Co-authored-by: Claude (claude-opus-5-5[1m]) <noreply@anthropic.com>
A steer's break now goes to the message's budgeted callback, and the guard on the unbudgeted one did not narrow that one's optional type, so ty reported calling a value that may be None. The callback the break goes to is chosen first and checked itself. Behaviour is unchanged: the budgeted callback wraps the unbudgeted one, so the two are None together. Co-authored-by: Claude (claude-opus-5-5[1m]) <noreply@anthropic.com>
73a81d9 to
8f7a323
Compare
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
Reviewed the full github/main...HEAD diff and the delta from 73a81d9fd14d, including the ACP message-budget state, steer and step boundaries, reply trimming, downstream history/RPC callers, canonical context/spec wording, compatibility, and test-strength changes. The revision fixes both re-acceptance notes: leading whitespace and steer separators that reply.strip() removes no longer consume the returned message's stream budget, while separators inside nonblank reply text remain budgeted; the documentation now names both nonterminal statuses. I found no additional defect.
Verification: the combined ACP/RPC/history suite passed (584 passed); the TUI steering integration passed (2 passed); source-language, large-file, commit-message, focused Ruff lint/format, and focused ty checks passed.
ZuyiZhou
left a comment
There was a problem hiding this comment.
LGTM. Read the full diff and call sites; CI is green and the tests exercise the fix.
Summary
ACP replies and live output need the same message boundaries. A steer arriving during a report must not drop its earlier half, and a held prompt's heartbeat must not split that report. Return the last nonblank message while retaining narration in the transcript.
pendingorin_progressdo not; Raven's held-prompt heartbeat can arrive mid-report.replystrips it. Blank chunks and repeated steers before the first nonblank text no longer silently truncate a reply that fits. Whitespace inside substantive message text still counts.Type
Verification
Linux, Python 3.12, after rebase onto current main:
618 passed. Coverage includes the real held-prompt heartbeat, tool/plan boundaries, persisted replies, transcript rows, and downstream DAG consumers.
Native Windows, Python 3.12:
23 passed. Nine blank-prefix cases failed before the fix; the final matrix also covers prefixes after a tool boundary and explicitly preserves visible whitespace. Four isolated mutations were caught: charging stripped whitespace, treating every chunk as leading, ending the leading-prefix state on a blank chunk, and dropping visible leading whitespace. Sources were restored after each run.
Checks passed:
All 10 import contracts kept. Full repository tests, native macOS, and the TUI suite were not rerun for this follow-up. The full ACP subprocess selection ran on Linux; Windows verification covers the collector directly.
Risk
ACP callers receive the last nonblank message, so a report followed by a save step and "Saved." returns the sign-off; the earlier report remains in the transcript. Leading display-only whitespace and separators between messages can make the raw stream longer than the returned-message cap. Internal separators and substantive text remain capped together. No stored format or configuration changes.
Rollback: revert the squash commit.
Related Issues
#889