feat(spawn): flag zero-tool-call completed runs as likely premature returns (#61) - #71
Merged
Merged
Conversation
…eturns #61 dogfood finding: an implementer returned after a single planning statement with zero tool calls and no turn-budget exhaustion, looking like a normal (terse) completion. The controller had to inspect the repo to discover no work was done. - spawnSubagent counts executed tool calls and surfaces toolCallCount on SpawnResult + the run:ended journal event. - The subagent tool prefixes a completed zero-tool result with a '[FLEET] zero-tool-call run - likely a premature return' warning and exposes details.toolCallCount, so the controller verifies (git status/log) before trusting the result. Failed runs are unchanged (they already carry an error). Surfacing-only by design: a zero-tool run made no side effects, so the retry/auto-retry question stays with the controller.
…CAL) Review on PR #71 caught that the claude backend never emitted tool events (mapClaudeEvent flattened tool_use into message content), so the #61 zero-tool-call signal would have falsely flagged every completed claude run as a premature return. - mapClaudeEvents(line) returns ALL events a CC line implies: an assistant message with tool_use blocks yields message_end + one tool_execution_end per block (toolCallId/toolName from the block). mapClaudeEvent stays as a first-event compat wrapper (detector). - ClaudeChildSession.onLine iterates the mapped events. - ChildSessionEvent documents the tool-event fields (toolCallId/ toolName/result/isError) that consumers previously cast for. Tests: mapper events (multi-block, text-only, compat wrapper) + a claude-path counting test through spawnSubagent (mapped CC line -> toolCallCount >= 2).
rz1989s
force-pushed
the
feat/61-zero-tool-run-signal
branch
from
August 29, 2026 04:54
ab5b873 to
9f3efd3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem (#61)
A foreground implementer dispatched with
maxTurns: 70returned after its first turn with only a planning statement ("Let me read the relevant existing files first…"), zero tool calls, zero file changes — and the result shape was indistinguishable from a normal completion. The controller had to inspect the repo to discover nothing happened, and a re-dispatch of the identical task succeeded (so the task was feasible; the run just exited early).Changes (surfacing-only by design)
engine/spawnSubagent.ts— counts executed tool calls (tool_execution_end) and surfacestoolCallCountonSpawnResultand on therun:endedjournal event (post-hoc diagnosability, same rationale as fix(subagent): surface primary error when fallback retry masks it + journal failure reason (#59) #69'serrorfield).tools/subagent.ts— a completed run withtoolCallCount === 0gets its result text prefixed with[FLEET] zero-tool-call run — likely a premature return (#61); verify with git status/log before trusting this result.and exposesdetails.toolCallCount. Failed runs are unchanged (they already carry an error).Design notes:
details.toolCallCountalso gives lifecycle/workflow consumers the raw signal for later policy.Tests
details.toolCallCount === 0; run with a tool → no prefix, count 1.pnpm typecheckclean.Closes #61