test(acp): pin the abandoned-turn settle await; retrieve a dropped driver's late failure - #3887
Conversation
…iver's late failure (#3860) - test_cancelling_the_consumer_mid_stream_stops_the_driver records driver state at the consumer's finally (where a caller releases), and test_a_runtime_that_ignores_the_cancel_is_bounded asserts it waited the bound — both now fail when _stop_abandoned_driver only cancels - a driver still running past _ACP_CANCEL_SETTLE_S gets a done-callback that retrieves (debug-logs) its exception, so asyncio no longer reports "Task exception was never retrieved"; the warning prints %g (0.2s, not 0s) - executor: note TurnStalled can surface up to the settle bound later Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Warning Review limit reachedNext included review available in 44 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (3)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
QA panel review — PASS
code-review · head 3b1b4b2e3e01 · formal
Low-risk PR: adds a late-outcome logger for abandoned ACP driver turns and a clarifying comment in the executor. No correctness concerns surfaced across the panel. The one actionable item is the missing changelog.d/ fragment, which PROTO.md lists as a must-pass gate for non-test-only changes. The verifier confirmed the fragment is absent from the diff; the only open question is whether a skip-changelog label is applied on the PR (not verifiable from the diff alone). If the label is present, this finding is a false positive and can be dropped.
Findings
| Severity | Location | Finding | Verified | |
|---|---|---|---|---|
| 🟡 | minor | changelog.d/3887.fixed.md |
The PR changes production code in server/chat_acp.py (adds _late_outcome_logger and calls it in _stop_abandoned_driver) but ships no changelog.d/ fragment, whi… | confirmed |
findings JSON (machine-readable)
[
{
"file": "changelog.d/3887.fixed.md",
"line": 0,
"severity": "minor",
"category": "conventions",
"claim": "The PR changes production code in server/chat_acp.py (adds _late_outcome_logger and calls it in _stop_abandoned_driver) but ships no changelog.d/ fragment, which PROTO.md requires for non-test-only changes.",
"evidence": "The diff touches server/chat_acp.py (adding _late_outcome_logger and driver.add_done_callback(...)) and a2a_impl/executor.py (comment), but no changelog.d/ file is present. PROTO.md: 'a changelog.d/<pr>.<kind>.md fragment \u2014 shape and kinds in changelog.d/README.md' is listed as a must-pass gate.",
"verdict": "confirmed",
"note": "Diff confirms no changelog.d/ fragment; PROTO.md gate table explicitly requires one. Caveat: skip-changelog label status unverified (would need API call), so this is confirmed on the fragment-absence alone."
}
]1 finding(s) excluded from the verdict by in-diff confinement (file not among this PR's changed paths):
changelog.d/3887.fixed.md(minor) — The PR changes production code in server/chat_acp.py (adds _late_outcome_logger and calls it in _stop_abandoned_driver) but ships no changelog.d/ fragment, whic
There was a problem hiding this comment.
Promoting the PASS verdict for head 3b1b4b2e3e01: all checks terminal-green, zero unresolved review threads. (approve-on-green)
Open findings carried by this approval — non-blocking, but they did not go away:
- minor
changelog.d/3887.fixed.md:0— The PR changes production code in server/chat_acp.py (adds _late_outcome_logger and calls it in _stop_abandoned_driver) but ships no changelog.d/ fragment, which PROTO.md requires for non-test-only ch
Approving a WARN does not resolve its findings (issue #22).
Summary
Follow-ups from the adversarial review of #3858:
tests/test_acp_abandoned_turn.py)test_cancelling_the_consumer_mid_stream_stops_the_driverrecords_driver_stopped(rt)in the consumer's ownfinally— the moment a caller runs_acp_release— instead of afterawait consumer(by which point a few loop turns had let the driver finish anyway).test_a_runtime_that_ignores_the_cancel_is_boundedasserts the close actually waited the bound (0.15 <= elapsed < 2.0at a 0.2s bound) and the warning readswithin 0.2s.server/chat_acp.py) — a driver still running after_ACP_CANCEL_SETTLE_Sgets a done-callback (_late_outcome_logger) that callstask.exception()and debug-logs it, so a later failure no longer ends as asyncio's "Task exception was never retrieved" at GC. New testtest_a_driver_dropped_past_the_bound_has_its_late_failure_retrievedinstalls a loop exception handler, lets the dropped driver fail after the bound,gc.collect()s, and asserts no "never retrieved" report + the debug line. Also%.0fs→%gsso sub-second bounds don't print "0s".a2a_impl/executor.py) — one-line comment at_stall_guarded'swait_for: on a wedged ACP runtimeTurnStalledcan surface up to_ACP_CANCEL_SETTLE_Slater.Labelled
skip-changelog: the production change is debug-level logging for an already-abandoned turn (no user-visible behaviour).Mutation proofs
M1 —
_stop_abandoned_drivercancels without awaiting (done, _ = await asyncio.wait({driver}, timeout=…)→done = {driver} if driver.done() else set()):origin/maintest file: 4 failed / 3 passed —test_cancelling_the_consumer_mid_stream_stops_the_driverandtest_a_runtime_that_ignores_the_cancel_is_boundedstayed green (the gap).assert [False] == [True]("the consumer finished while the driver task was still running") andassert 0.15 <= 0.00046. (6 failed / 2 passed overall.)M2 — drop the
add_done_callback:test_a_driver_dropped_past_the_bound_has_its_late_failure_retrievedkilled — the handler received{'message': 'Task exception was never retrieved', 'exception': RuntimeError('boom after the bound'), …}.Unmutated:
ruff check .clean,lint-imports4 kept / 0 broken, fullpytest tests/11161 passed / 17 skipped. (server/chat.pyuntouched.)Fixes #3860
🤖 Generated with Claude Code