fix(steering): skip phantom zero-turn result on --resume; guard empty replies

On --resume, a background task stopped by the previous restart is replayed
as its own zero-turn turn with an empty result, before the real turn.
consume_stream took it as the answer -> empty Discord message (400) ->
"Sorry, something went wrong", while the real turn ran orphaned.

- stream_json: skip result with num_turns=0, empty, non-error
- router: empty Claude response becomes a visible notice, not an adapter crash

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014y1sJNe6mkWwFMwkWmCg8J
This commit is contained in:
2026-10-01 15:51:34 +00:00
parent 9b10e8b9d1
commit 5bc52fe67f
4 changed files with 35 additions and 0 deletions

View File

@@ -349,3 +349,8 @@ def test_max_live_non_positive_logs_and_always_degrades(caplog):
registry = claude_runner.RunnerRegistry(max_live=0, idle_minutes=20)
assert any("max_live" in rec.message for rec in caplog.records)
assert registry.get("any-channel") is None
def test_stream_json_self_check_skips_phantom_resume_result():
from src import stream_json
stream_json.demo()

View File

@@ -195,6 +195,13 @@ class TestRegularMessage:
assert is_cmd is False
mock_send.assert_called_once_with("ch-1", "hello", model="sonnet", on_text=None, voice_mode=False, adapter_name=None)
@patch("src.router._get_channel_config", return_value=None)
@patch("src.router.send_message", return_value=" ")
def test_empty_response_becomes_notice(self, mock_send, _chan):
response, is_cmd = route_message("ch-1", "user-1", "hello", model="sonnet")
assert response.strip()
assert is_cmd is False
@patch("src.router.send_message")
def test_model_override(self, mock_send):
mock_send.return_value = "Response"