Skip to content

fix(loop-detection): escalate generic_repeat to critical at criticalThreshold#1912

Open
BingqingLyu wants to merge 3 commits intomainfrom
fork-pr-60248-fix-generic-repeat-critical-threshold
Open

fix(loop-detection): escalate generic_repeat to critical at criticalThreshold#1912
BingqingLyu wants to merge 3 commits intomainfrom
fork-pr-60248-fix-generic-repeat-critical-threshold

Conversation

@BingqingLyu
Copy link
Copy Markdown
Owner

@BingqingLyu BingqingLyu commented Apr 28, 2026

Summary

Fixes openclaw#60111.

The generic_repeat detector only checked warningThreshold and always returned level: "warning", making criticalThreshold a no-op for the most common runaway loop pattern (same tool, same arguments, repeated calls).

  • Added a criticalThreshold check in the generic_repeat code path, before the existing warningThreshold check
  • Updated the test that was asserting the broken warn-only behavior to assert the correct critical escalation

This makes generic_repeat consistent with known_poll_no_progress and ping_pong, which both escalate to "critical" at criticalThreshold.

Detector warningThreshold criticalThreshold
known_poll_no_progress ⚠️ Warns ✅ Blocks
ping_pong ⚠️ Warns ✅ Blocks (with noProgressEvidence)
generic_repeat (before) ⚠️ Warns ❌ Not checked
generic_repeat (after) ⚠️ Warns ✅ Blocks

Test plan

  • Existing 29 tests pass
  • Updated test now asserts level: "critical" and detector: "generic_repeat" at criticalThreshold

🤖 Generated with Claude Code

jwchmodx and others added 3 commits April 3, 2026 15:45
…ostReplyRootId

Direct messages in Mattermost were creating threads even with
replyToMode=off because resolveMattermostReplyRootId would fall back
to payload.replyToId regardless of chat kind. When a downstream payload
carried a replyToId (e.g. from block-streaming delivery), this
bypassed the earlier DM threading guard and set root_id on the outbound
post, making DM replies appear as threads instead of in the channel body.

Fix: pass kind through all three delivery call sites and hard-return
undefined inside resolveMattermostReplyRootId for kind="direct",
mirroring the resolveMattermostEffectiveReplyToId guard that already
existed for session-key resolution.

Fixes openclaw#59981
…hreshold

The generic_repeat detector only checked warningThreshold and always
returned level "warning", making criticalThreshold effectively a no-op
for the most common runaway loop pattern (same tool + same args).

Add a critical-threshold check before the warning check, consistent
with how known_poll_no_progress and ping_pong already behave.

Fixes openclaw#60111

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…aining-only fields

MiniMax's usage_percent / usagePercent fields report the *remaining* quota
as a percentage, not the consumed quota. When count fields (prompt_limit /
prompt_remain) are also present, fromCounts already computed the correct
usedPercent and the inverted value was silently ignored. But when only
usage_percent is returned (no count fields), the code treated it as a
used-percent and passed it through unchanged, causing the menu bar to show
"2% left" instead of "98% left".

Move usage_percent and usagePercent from PERCENT_KEYS to a new
REMAINING_PERCENT_KEYS array. deriveUsedPercent now inverts remaining-percent
values to obtain usedPercent, matching the behaviour already validated by the
existing "prefers count-based usage when percent looks inverted" test. Count-
based fromCounts still takes priority over both key groups.

Fixes openclaw#60193

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

[Bug]: generic_repeat loop detector never escalates to blocking — criticalThreshold has no effect

2 participants