Bug: tool_storm_detect diversity threshold uses int() (truncation) instead of ceil — false positives on 2-tool sequences
File
internal/agent/tool_storm_detect.go line 263
Problem
The code comment explicitly states the intended formula is ceil(windowSize * ratio), but the implementation uses int() (truncation toward zero):
// Comment (lines 88-89):
// stormMinDiversityRatio: fraction of the window that must use
// distinct tools. 0.6 means at least ceil(4*0.6)=3 distinct tools in
// a 4-iteration window.
// Code (line 263):
minDistinct := int(float64(len(s.window)) * stormMinDiversityRatio)
// = int(4 * 0.6)
// = int(2.4)
// = 2 ← truncation, NOT ceil!
Mathematical verification:
int(4 * 0.6) = int(2.4) = 2
ceil(4 * 0.6) = ceil(2.4) = 3
Impact
A legitimate 2-tool pattern like read_file, grep, read_file, grep has 2 distinct tools. With threshold = 2 (instead of intended 3):
2 >= 2 → true → storm warning fires
- With correct
ceil: 2 >= 3 → false → no warning (correct)
This directly contradicts the detector's design intent (lines 90-91): "This distinguishes diverse-tool storms (our target) from same-tool repetition (already covered by repetition_tracker)."
The detector was designed to NOT fire on low-diversity patterns (2 tools alternating), but the truncation bug lowers the threshold so it does fire — producing false positive storm warnings that interrupt legitimate agent workflow.
Trigger Scenario
Agent makes 4 rapid calls: read_file → grep → read_file → grep with brief reasoning ("Let me check." between each). This is a normal exploration pattern, but:
- 2 distinct tools >= threshold 2 → storm fires
- Agent gets false "[tool-storm] 4 consecutive tool calls" warning
- Agent is told to "Pause and synthesize" when it was working efficiently
Root Cause
Go's int() conversion truncates toward zero. For 2.4, it returns 2. The correct function is math.Ceil(2.4) which returns 3.
Fix
import "math"
minDistinct := int(math.Ceil(float64(len(s.window)) * stormMinDiversityRatio))
Severity
Medium — produces false positive warnings on common 2-tool exploration patterns (read_file + grep), interfering with legitimate agent work. This is a direct business logic failure: the detector's own documentation says it should NOT fire on this pattern.
Verification
Mathematically verified: go run confirms int(4 * 0.6) = 2, not 3. The file has no uncommitted changes confirming the bug exists in production code.
Bug: tool_storm_detect diversity threshold uses int() (truncation) instead of ceil — false positives on 2-tool sequences
File
internal/agent/tool_storm_detect.goline 263Problem
The code comment explicitly states the intended formula is
ceil(windowSize * ratio), but the implementation usesint()(truncation toward zero):Mathematical verification:
Impact
A legitimate 2-tool pattern like
read_file, grep, read_file, grephas 2 distinct tools. With threshold = 2 (instead of intended 3):2 >= 2→ true → storm warning firesceil:2 >= 3→ false → no warning (correct)This directly contradicts the detector's design intent (lines 90-91): "This distinguishes diverse-tool storms (our target) from same-tool repetition (already covered by repetition_tracker)."
The detector was designed to NOT fire on low-diversity patterns (2 tools alternating), but the truncation bug lowers the threshold so it does fire — producing false positive storm warnings that interrupt legitimate agent workflow.
Trigger Scenario
Agent makes 4 rapid calls:
read_file → grep → read_file → grepwith brief reasoning ("Let me check." between each). This is a normal exploration pattern, but:Root Cause
Go's
int()conversion truncates toward zero. For2.4, it returns2. The correct function ismath.Ceil(2.4)which returns3.Fix
Severity
Medium — produces false positive warnings on common 2-tool exploration patterns (read_file + grep), interfering with legitimate agent work. This is a direct business logic failure: the detector's own documentation says it should NOT fire on this pattern.
Verification
Mathematically verified:
go runconfirmsint(4 * 0.6) = 2, not 3. The file has no uncommitted changes confirming the bug exists in production code.