Bug Description
The Foreman coder str_replace tool reports success ({"replacements":1}) for a no-op edit where old_string == new_string. Because the file is not actually changed, a model that issues such a call sees "success" but observes no change, so it re-issues the identical call, driving a degenerate loop until the RepeatedToolCall stuck-loop detector terminates the run. The tool should reject an identical-strings replacement instead of rewarding it with a phantom success.
Observed live on two different models (Ornith-35B-A3B MoE and Qwopus3.6-27B dense) on the same issue, so it is model-independent: it is a tool-behavior bug, not a model capability limit.
Steps to Reproduce
- Run a coder task where the model constructs a
str_replace call with old_string equal to new_string (e.g. it intends an edit but emits identical strings).
- Observe the tool returns
{"path":"...","replacements":1} (success), while the file is unchanged.
- The model, seeing no change, re-issues the identical call. Repeat until the RepeatedToolCall progress monitor fires (>= 5 identical calls) and the run ends INCOMPLETE / STUCK-LOOP-DETECTED.
Expected Behavior
str_replace returns an error when old_string == new_string, e.g. "old_string and new_string are identical; no change made. If the file already has the desired content, proceed; otherwise correct new_string." This breaks the loop on the first no-op instead of letting the model believe it edited.
Actual Behavior
A no-op (old == new) returns {"replacements":1} (success). The model loops on the identical call. Live transcript (Qwopus, issue #944 A/B): six byte-identical str_replace calls (old and new both 1856 chars, identical), each returning replacements:1, model reasoning "I keep writing the same thing," RepeatedToolCall alert at turn 34, run ends INCOMPLETE.
Environment
LLMKube Version: foreman-agent 0.8.27 (also reproduced on the PR #964 build)
Notes
Bug Description
The Foreman coder
str_replacetool reports success ({"replacements":1}) for a no-op edit whereold_string == new_string. Because the file is not actually changed, a model that issues such a call sees "success" but observes no change, so it re-issues the identical call, driving a degenerate loop until the RepeatedToolCall stuck-loop detector terminates the run. The tool should reject an identical-strings replacement instead of rewarding it with a phantom success.Observed live on two different models (Ornith-35B-A3B MoE and Qwopus3.6-27B dense) on the same issue, so it is model-independent: it is a tool-behavior bug, not a model capability limit.
Steps to Reproduce
str_replacecall withold_stringequal tonew_string(e.g. it intends an edit but emits identical strings).{"path":"...","replacements":1}(success), while the file is unchanged.Expected Behavior
str_replacereturns an error whenold_string == new_string, e.g. "old_string and new_string are identical; no change made. If the file already has the desired content, proceed; otherwise correct new_string." This breaks the loop on the first no-op instead of letting the model believe it edited.Actual Behavior
A no-op (
old == new) returns{"replacements":1}(success). The model loops on the identical call. Live transcript (Qwopus, issue #944 A/B): six byte-identicalstr_replacecalls (old and new both 1856 chars, identical), each returningreplacements:1, model reasoning "I keep writing the same thing," RepeatedToolCall alert at turn 34, run ends INCOMPLETE.Environment
LLMKube Version: foreman-agent 0.8.27 (also reproduced on the PR #964 build)
Notes
str_replacefailing to match; this isstr_replacesucceeding on a no-op.str_replacetool: add anold_string == new_stringguard that returns an error before attempting the replacement, with a message steering the model to either proceed (if content is already correct) or correctnew_string.