Skip to content

feat(own-loop): explicit stop guidance + same-tool repetition detector - #78

Merged
jonnyparris merged 1 commit into
mainfrom
fix/stop-condition-and-no-progress
May 25, 2026
Merged

feat(own-loop): explicit stop guidance + same-tool repetition detector#78
jonnyparris merged 1 commit into
mainfrom
fix/stop-condition-and-no-progress

Conversation

@jonnyparris

Copy link
Copy Markdown
Owner

What

Two complementary changes that make weak orchestrators decide when to stop calling tools and write their conclusion.

1. System prompt stop condition

New rule #7 in the operating rules. Four concrete cases the model should treat as 'time to stop':

  • 3 consecutive tool calls produce no new useful information
  • The question has been answered (don't 'verify' with more tools)
  • A tool keeps returning errors (write what failed, don't retry with variations)
  • Final reply must always be plain text, never a tool call

2. Same-tool repetition detector

A new layer between the existing doom-loop detector (identical tool+args) and the Anthropic-only no-text loop:

If the last N tool calls are all the same tool name (different args), and the model hasn't emitted a text answer, intervene.

Two-stage:

  1. Nudge at SAME_TOOL_NUDGE_THRESHOLD = 6 — inject [STOP-CHECK] system message asking 'do you have enough information?' Fires once per turn.
  2. Hard-break at SAME_TOOL_HARD_BREAK_THRESHOLD = 10 — exit with a wrap-up text-delta.

Provider-agnostic. Pure function detectSameToolRepetition in new src/loop-detection.ts.

Why

Reproduced today on Gemma 4 26B post-PR-77 deploy:

step 0   tool=explore
step 1   tool=codemode
step 2   tool=codemode
...
step 18  tool=codemode   cumulativeInputTokens=611168 (298% of budget)

18 consecutive codemode calls with different args, zero text output, never wrapping up. Doom-loop didn't fire because args differed. No-text loop didn't fire because it's gated on anthropic/ models. Budget thresholds didn't fire because projected next-call size stayed reasonable (PR #77 working as intended). The session would have run forever — only the new cost runaway backstop at 5× budget would eventually catch it, after ~1M tokens of waste.

Tests

  • 10 new unit tests for detectSameToolRepetition covering: empty buffer, short buffer, degenerate threshold, happy path (same tool different args), negative case (streak broken), tail-window isolation (earlier mixed entries don't affect outcome), args containing colons.
  • All 28 existing compaction + prune tests still pass.
  • Typecheck clean.

Linked

beep-boop-🤖

Two related changes that together make weak orchestrators decide when
to stop calling tools and write their conclusion. Found by deploying
PR #77 and watching a Gemma session run 18 consecutive codemode calls
without ever producing a text answer.

## 1. System prompt stop condition

New rule #7 in the operating rules section, with four concrete cases:
- 3 consecutive tool calls producing no new useful info → write and stop
- Question answered → don't run more tools to 'verify'
- Tool returning errors → write what failed and stop
- Final reply must always be plain text, never a tool call

Existing rules renumbered 8/9/10.

## 2. Same-tool repetition detector

Layer between the existing doom-loop detector (identical tool+args)
and the no-text loop (Anthropic-only). Catches the pattern:

> Same tool name N times in a row, with DIFFERENT args, no text answer.

Two-stage response, both provider-agnostic:

1. **Nudge** at SAME_TOOL_NUDGE_THRESHOLD (6): inject a [STOP-CHECK]
   system message asking the model whether it's done. The nudge fires
   ONCE per turn — if the model continues legitimately, it's not
   pestered repeatedly.
2. **Hard-break** at SAME_TOOL_HARD_BREAK_THRESHOLD (10): exit the
   loop with exitReason=doom-loop and a wrap-up text-delta telling
   the model to write its conclusion from what it has.

The detector lives in src/loop-detection.ts as a pure function
(detectSameToolRepetition) so it's testable without booting the DO.
Reads from the same recentToolCalls buffer the doom-loop check uses,
with retention bumped to cover the new harder threshold.

## Tests

- 10 unit tests for detectSameToolRepetition: edge cases (empty,
  short buffer, degenerate threshold), happy paths (same tool with
  different args), negative cases (streak broken by other tool), and
  args-handling (JSON with colons doesn't confuse the split).
- All 28 existing compaction + prune tests still pass.
- Typecheck clean.
@jonnyparris
jonnyparris merged commit 44e19fb into main May 25, 2026
1 of 2 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.

1 participant