Skip to content

fix: nudge injection loop — 3 defects from issue #216 - #218

Merged
ranxianglei merged 2 commits into
masterfrom
2026-07-27_nudge-loop-fix
Jul 27, 2026
Merged

ranxianglei merged 2 commits into
masterfrom
2026-07-27_nudge-loop-fix

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Summary

Fixes the self-reinforcing nudge injection loop described in issue #216.

Defect 1: applyAnchoredNudges before nothingToCompress check

applyAnchoredNudges ran BEFORE nothingToCompress was computed, injecting full nudge text (with HOW_TO_COMPRESS rules ~1.5K tokens) even when the filter determined there was nothing to compress.

Fix: Moved buildCompressibleRanges + filterRecommendedRanges + nothingToCompress computation BEFORE applyAnchoredNudges. Changed gate from if (nudgeAllowed) to if (shouldInjectNudge).

Defect 2: Failed compress calls never reset pending-nudge state

messageHasCompress only counted status === "completed". Failed/rejected compress calls didn't reset lastNudgeShownTokens, keeping the threshold permanently halved → nudge frequency accelerated instead of backing off.

Fix: New messageHasCompressAttempt() counts ANY compress tool call regardless of status. Used at currentTurnHasCompress call site. messageHasCompress (completed-only) retained for priority classification.

Defect 3: Heavy nudge payload self-inflates

Every nudge carried full HOW_TO_COMPRESS rules (~1.5K tokens). Loop from 1+2 caused injection every turn → nudges themselves inflated context.

Fix: compressRulesShown flag in session state — rules sent only once per session.

Test plan

  • 936 tests pass (929 existing + 7 new)
  • typecheck clean
  • New regression tests: messageHasCompressAttempt detects failed compress (status=error, invalid_request_error), distinguishes from messageHasCompress
  • Dual-agent review

Closes #216.

ranxianglei and others added 2 commits July 27, 2026 22:56
Defect 1: applyAnchoredNudges ran before nothingToCompress was computed,
injecting full nudge text even when filter said nothing to compress.
Fix: moved recommendation filter computation before applyAnchoredNudges,
gated with shouldInjectNudge.

Defect 2: messageHasCompress only counted status=completed, so failed
compress calls never reset lastNudgeShownTokens → threshold permanently
halved → nudge frequency accelerated instead of backing off.
Fix: new messageHasCompressAttempt counts any compress tool call
regardless of status.

Defect 3: HOW_TO_COMPRESS_RULES (~1.5K tokens) sent on every nudge.
Fix: compressRulesShown flag — rules sent only once per session.

Files: lib/messages/{inject/inject,query}.ts, lib/state/{types,state,
persistence,utils}.ts, tests/nudge-loop-fix.test.ts (7 new).
936 tests pass, typecheck clean.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Defect 2: failed compress now clears halved-threshold state (anchors,
lastNudgeShownTokens) but does NOT run proportional baseline adjustment.
Baseline stays unchanged — growth accumulates until there's genuinely
more to compress. Previously treated failed compress identically to
success, eating growth.

Defect 3 (HOW_TO_COMPRESS_RULES dedup): reverted. Rules are needed every
time for compression quality. Fixes 1+2 break the loop that caused
rules to repeat every ~10s — without the loop, per-nudge rules overhead
is acceptable.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@ranxianglei
ranxianglei merged commit 6042020 into master Jul 27, 2026
5 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

1 participant