fix(#6806): emit fallback ResultEvent when stream ends without result line - #6808
fix(#6806): emit fallback ResultEvent when stream ends without result line#6808fullsend-ai-coder[bot] wants to merge 2 commits into
Conversation
… line When the Claude Code agent process terminates abnormally (e.g., killed by a signal after writing output but before emitting its final result NDJSON line), parseClaudeStream now emits a synthetic ResultEvent with the best-available metrics accumulated from message_start and message_delta events during the stream. Previously, metrics relied entirely on the "result" event emitted at the very end of the Claude Code NDJSON stream. If the process was killed before that line was written, RunMetrics stayed at zero values (num_turns: 0, all token counts: 0), producing a metrics.json with zeroed data despite the agent having done substantial work. The fix tracks per-turn token counts incrementally: each message_start accumulates input and cache tokens, and each subsequent message_start folds in the previous turn's output tokens. At EOF or read error, if no real result event was seen and at least one turn was observed, a fallback ResultEvent is emitted with subtype "stream_incomplete" and IsError: true. When a real result event IS present, the fallback is suppressed and the definitive values are used as before. TotalCostUSD remains zero in the fallback because it cannot be derived from token counts alone without pricing data. This is an acceptable limitation — non-zero token counts and num_turns are the primary improvement over the previous all-zeros behavior. Closes #6806
|
🤖 Finished Review · ✅ Success · Started 4:05 PM UTC · Completed 4:22 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $5.72 |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
Risk Assessment: moderate (2/5) DetailsSmall, well-tested bot PR with good test coverage, but elevated git history signals (10 fix/revert commits in 90d and 4-author contention on these files) offset the otherwise low-risk metadata, yielding a moderate composite score consistent with the prior assessment. Previous runRisk Assessment: moderate (2/5) DetailsSmall, well-tested bot PR with good test coverage, but elevated git history signals (high fix/revert churn and multi-author contention on these files) offset the otherwise low-risk metadata, yielding a moderate composite score. |
ReviewFindingsLow
Previous runReviewFindingsLow
Next steps:
|
|
🤖 Finished Fix · ✅ Success · Started 4:24 PM UTC · Completed 4:30 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $1.23 |
Add ErrorMessage field to the synthetic ResultEvent emitted when the Claude stream ends without a result line, matching the pattern used by the real result handler and sibling parsers. Update fallback tests to assert the new field. Addresses review feedback on #6808
🔧 Fix agent — iteration 1 (bot-triggered)Addressed the one actionable finding (missing-error-message) by adding ErrorMessage to the fallback ResultEvent. The other three findings were informational or positive observations requiring no code changes. Fixed (1):
Disagreed (3):
Tests: passedNext steps:
|
|
🤖 Finished Review · ✅ Success · Started 4:32 PM UTC · Completed 4:49 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $4.30 |
Superseded by updated review
Summary
When the Claude Code agent process terminates abnormally after doing work (e.g., killed by signal, exit code -1),
parseClaudeStreamnow emits a fallbackResultEventwith accumulated per-turn token counts and turn count. Previously,metrics.jsonreported all-zero values because metrics relied entirely on theresultNDJSON line emitted at the very end of the stream — if the process died before writing it, everything was lost.Changes
internal/runtime/claude_progress.go: Track cumulative token counts across turns (input, output, cache read, cache creation) and turn count frommessage_startevents. At EOF or read error, if noresultevent was seen and ≥1 turn occurred, emit a syntheticResultEventwithSubtype: "stream_incomplete"andIsError: true.internal/runtime/claude_progress_test.go: Added 4 test cases covering the fallback on incomplete stream (multi-turn), suppression when real result is present,progressParsermetrics population, and fallback on read error (broken pipe).Testing
TestParseClaudeStreamFallbackResultOnIncompleteStream,TestParseClaudeStreamNoFallbackWhenResultPresent,TestProgressParserFallbackMetrics,TestParseClaudeStreamFallbackOnReadError)TestProgressParserNoResultEventstill passes (nomessage_startevents → no fallback, consistent with legacy behavior)internal/runtime/...test suite passesgo vetandgo buildpassparseClaudeStreamat 90.4% (above 80% threshold)Limitations
TotalCostUSDremains zero in the fallback — it cannot be derived from token counts without pricing data. Non-zeronum_turnsand token counts are the primary improvement.Closes #6806
Post-script verification
agent/6806-metrics-fallback-stream-incomplete)32ad401f884b3fe989d689bab02be30657dce380..HEAD)