Skip to content

[business-logic] tool_storm_detect diversity threshold uses int() truncation instead of ceil — false positive storm warnings on 2-tool sequences #89

Description

@topcheer

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 >= 2true → storm warning fires
  • With correct ceil: 2 >= 3false → 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions