Skip to content

fix(circuit-breaker): treat unknown tool input as non-comparable to prevent false positives on flat events#2796

Merged
code-yeongyu merged 1 commit intocode-yeongyu:devfrom
guazi04:fix/circuit-breaker-false-positive-upstream
Mar 24, 2026
Merged

fix(circuit-breaker): treat unknown tool input as non-comparable to prevent false positives on flat events#2796
code-yeongyu merged 1 commit intocode-yeongyu:devfrom
guazi04:fix/circuit-breaker-false-positive-upstream

Conversation

@guazi04
Copy link
Copy Markdown
Contributor

@guazi04 guazi04 commented Mar 24, 2026

Summary

Context

#2655 introduced target-aware signatures (toolName::sortedJson(input)) to fix false-positive cancellations. Then d48ea025 refactored detection from sliding-window to consecutive-count. Both changes are correct individually, but together they leave a gap:

createToolCallSignature() falls back to bare tool name when toolInput is undefined/null:

if (toolInput === undefined || toolInput === null) {
  return toolName  // bare "read" — all flat-format calls look identical
}

This matters because resolveMessagePartInfo() has two event paths:

Path Condition state.input Signature
A (nested) properties.part exists ✅ present read::{"filePath":"/a.ts"} — differentiates
B (flat) no properties.part ❌ undefined read — bare, all calls "identical"

With consecutive-count detection, 20 flat-format read events (reading 20 different files) all produce signature "read", hitting the threshold. This is the exact scenario reported in #2652 — agents like Oracle/Explore/Librarian doing legitimate multi-file reads get cancelled.

Changes

src/features/background-agent/loop-detector.ts

Early return in recordToolCall() when toolInput is undefined/null: returns a unique sentinel signature (toolName::__unknown-input__) with consecutiveCount: 1. This means:

  • Unknown input → counter never accumulates → no false positive
  • Known identical input → existing signature logic → still detects true loops
  • Absolute maxToolCalls cap → unaffected, still catches runaway agents

src/features/background-agent/loop-detector.test.ts

  • Updated consecutive-trigger tests to use known input (matching real loop behavior)
  • Added tests: 20 calls with undefined input → does NOT trigger
  • Added tests: 20 calls with null input → does NOT trigger

src/features/background-agent/manager-circuit-breaker.test.ts

  • Updated flat-format event test: 20 consecutive read events without state.input → task keeps running
  • Strengthened absolute cap test with repeated flat-format events hitting maxToolCalls

Testing

bun test src/features/background-agent/loop-detector.test.ts        # 18 pass
bun test src/features/background-agent/manager-circuit-breaker.test.ts  # 8 pass
bun run typecheck  # clean

Related Issues

Follow-up to #2655 / #2652


Summary by cubic

Fixes circuit breaker false positives on flat-format tool events with no input. Unknown tool input is now treated as non-comparable, so consecutive counts reset and valid multi-file reads aren’t cancelled.

  • Bug Fixes
    • In recordToolCall(), undefined/null input returns toolName::__unknown-input__ with consecutiveCount = 1.
    • Keeps loop detection for identical known inputs; maxToolCalls backstop unchanged.
    • Updated tests for undefined/null input and flat-format events; threshold tests now use known input.

Written for commit b9fa2a3. Summary will update on new commits.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 3 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Auto-approved: Fixes circuit breaker false positives on flat events by resetting consecutive count for calls with unknown inputs. Safety is maintained via the maxToolCalls backstop.

@code-yeongyu code-yeongyu merged commit 2993b32 into code-yeongyu:dev Mar 24, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants