Skip to content

fix(observer): auto-scale max_turns by analysis batch size#2062

Open
zanni098 wants to merge 1 commit into
affaan-m:mainfrom
zanni098:fix/observer-max-turns-autoscale
Open

fix(observer): auto-scale max_turns by analysis batch size#2062
zanni098 wants to merge 1 commit into
affaan-m:mainfrom
zanni098:fix/observer-max-turns-autoscale

Conversation

@zanni098
Copy link
Copy Markdown

@zanni098 zanni098 commented May 26, 2026

Fixes #2035.

Problem

observer-loop.sh hardcodes max_turns=20 as the default for the Claude analysis subprocess, while MAX_ANALYSIS_LINES defaults to 500. The two defaults are mismatched: Claude exhausts its turn budget before it can finish reading all observations and writing all qualifying instinct files.

Error: Reached max turns (20)

Fix

When ECC_OBSERVER_MAX_TURNS is not explicitly set, auto-scale max_turns proportionally to the actual analysis batch size (analysis_count, which is already computed just before this code):

max_turns = clamp(analysis_count / 10, floor=20, cap=100)

Results:

analysis_count max_turns
< 200 lines 20 (existing floor, unchanged)
500 lines 50 ← resolves the reported failure
800 lines 80
≥ 1000 lines 100 (cap)

Setting ECC_OBSERVER_MAX_TURNS explicitly still overrides the auto-scaled value, preserving the existing escape hatch.

Why not just raise the hardcoded default to 50?

Auto-scaling is strictly better: it handles any value of ECC_OBSERVER_MAX_ANALYSIS_LINES correctly, whereas a new hardcoded default of 50 would still fail if a user cranks MAX_ANALYSIS_LINES beyond 500.

Files changed

  • skills/continuous-learning-v2/agents/observer-loop.sh — replace 3-line max_turns assignment with an 8-line auto-scaling block

Testing

Manual verification of the logic (pure shell arithmetic, no external deps):

for lines in 10 50 199 200 500 800 1000 1500; do
  turns=$(( lines / 10 ))
  [ "$turns" -lt 20 ] && turns=20
  [ "$turns" -gt 100 ] && turns=100
  echo "$lines lines → $turns turns"
done
# 10 lines → 20 turns
# 50 lines → 20 turns
# 199 lines → 20 turns
# 200 lines → 20 turns
# 500 lines → 50 turns  ✓
# 800 lines → 80 turns
# 1000 lines → 100 turns
# 1500 lines → 100 turns (cap)

Summary by cubic

Auto-scales the observer’s max_turns based on analysis_count so Claude doesn’t hit the turn limit before processing the default 500-line batch and writing all instinct files.

  • Bug Fixes
    • Compute max_turns = clamp(analysis_count / 10, 20, 100) when ECC_OBSERVER_MAX_TURNS is not set.
    • Preserve manual override via ECC_OBSERVER_MAX_TURNS.
    • Examples: 500 lines → 50 turns; 800 → 80; <200 → 20.

Written for commit 8dff671. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • Improvements
    • Observer loop now automatically scales its iteration depth based on analysis size instead of using a fixed default, optimizing performance without requiring manual configuration adjustments.

Review Change Stack

…2035)

The hardcoded default of MAX_TURNS=20 is insufficient when
MAX_ANALYSIS_LINES=500 (also the default). Claude exhausts its turn
budget before it can write all discovered instinct files, producing:

  Error: Reached max turns (20)

Fix: when ECC_OBSERVER_MAX_TURNS is not explicitly set, compute
max_turns proportionally to the actual analysis batch size:
  max_turns = clamp(analysis_count / 10, 20, 100)

This gives:
  - 20–199 lines → 20 turns  (existing floor, unchanged)
  - 500 lines    → 50 turns  (resolves the reported failure)
  - 1000 lines   → 100 turns (cap)

Explicitly setting ECC_OBSERVER_MAX_TURNS still overrides the
auto-scaled value, preserving the existing escape hatch.
@zanni098 zanni098 requested a review from affaan-m as a code owner May 26, 2026 06:11
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

📝 Walkthrough

Walkthrough

The observer loop's max_turns default parameter is now calculated dynamically based on the input analysis_count when the ECC_OBSERVER_MAX_TURNS environment variable is not set. The scaling formula is 1 turn per 10 lines of analysis, with hard bounds enforcing a minimum of 20 and maximum of 100 turns.

Changes

Observer Max Turns Auto-scaling

Layer / File(s) Summary
Observer max_turns auto-scaling logic
skills/continuous-learning-v2/agents/observer-loop.sh
Default max_turns assignment replaces the fixed 20 value with a scaled calculation from analysis_count, dividing by 10 and enforcing minimum and maximum bounds.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 A scaling tale of turns and lines,
No fixed bounds need bind these designs,
Ten lines per turn, a ratio fair,
The observer leaps through code with care!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: auto-scaling max_turns based on analysis batch size in the observer component.
Linked Issues check ✅ Passed The PR implements auto-scaling of max_turns based on analysis_count as proposed in issue #2035, addressing the insufficient turn limit for the configured batch size.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the observer's max_turns scaling issue; no out-of-scope modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
skills/continuous-learning-v2/agents/observer-loop.sh (1)

221-229: 💤 Low value

Consider adding a comment explaining the validation block's purpose.

The validation block serves as a safety net for invalid ECC_OBSERVER_MAX_TURNS input (e.g., non-numeric or too-low values). While the auto-scaled path always produces valid values ≥20, this block is still necessary for the explicit-override case.

A brief comment would help future maintainers understand why this validation remains after the auto-scaling change.

📝 Suggested clarifying comment
   fi
   exit_code=0
 
+  # Validate max_turns in case ECC_OBSERVER_MAX_TURNS was set to invalid value
   case "$max_turns" in
     ''|*[!0-9]*)
       max_turns=20
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/continuous-learning-v2/agents/observer-loop.sh` around lines 221 -
229, Add a brief explanatory comment above the validation block that guards the
max_turns value: explain that this checks the user-provided
ECC_OBSERVER_MAX_TURNS (stored in max_turns) for non-numeric or too-small values
and forces a safe default of 20, and note that although auto-scaling produces
values ≥20, the check is needed for explicit overrides; locate the block
referencing max_turns and the numeric check (case "$max_turns" in ... and the if
[ "$max_turns" -lt 4 ] test) and insert the comment immediately before it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@skills/continuous-learning-v2/agents/observer-loop.sh`:
- Around line 208-218: The test assertion expecting the literal string
max_turns="${ECC_OBSERVER_MAX_TURNS:-20}" must be updated to reflect the new
auto-scaling logic in observer-loop.sh: change tests/hooks/hooks.test.js to no
longer look for the removed ":-20" default and instead assert that
observer-loop.sh references the ECC_OBSERVER_MAX_TURNS variable and implements
the fallback autoscale block (checks for ECC_OBSERVER_MAX_TURNS being non-empty,
otherwise computes max_turns from analysis_count and clamps to 20–100). Ensure
the test verifies presence of the ECC_OBSERVER_MAX_TURNS reference and the
autoscale/clamp logic (or matches a regex covering those lines) rather than the
old fixed default literal.

---

Nitpick comments:
In `@skills/continuous-learning-v2/agents/observer-loop.sh`:
- Around line 221-229: Add a brief explanatory comment above the validation
block that guards the max_turns value: explain that this checks the
user-provided ECC_OBSERVER_MAX_TURNS (stored in max_turns) for non-numeric or
too-small values and forces a safe default of 20, and note that although
auto-scaling produces values ≥20, the check is needed for explicit overrides;
locate the block referencing max_turns and the numeric check (case "$max_turns"
in ... and the if [ "$max_turns" -lt 4 ] test) and insert the comment
immediately before it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cf9e366d-6d09-4467-a085-4444cfffc05f

📥 Commits

Reviewing files that changed from the base of the PR and between 928076c and 8dff671.

📒 Files selected for processing (1)
  • skills/continuous-learning-v2/agents/observer-loop.sh

Comment on lines +208 to +218
# Auto-scale max_turns proportional to analysis batch size when not explicitly set.
# The old hardcoded default of 20 is insufficient for the 500-line MAX_ANALYSIS_LINES
# default: Claude hits --max-turns before it can write all discovered instinct files.
# Formula: 1 turn per 10 analysis lines, floor 20, cap 100. (#2035)
if [ -n "${ECC_OBSERVER_MAX_TURNS:-}" ]; then
max_turns="${ECC_OBSERVER_MAX_TURNS}"
else
max_turns=$(( analysis_count / 10 ))
if [ "$max_turns" -lt 20 ]; then max_turns=20; fi
if [ "$max_turns" -gt 100 ]; then max_turns=100; fi
fi
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if the test still expects the old pattern

echo "=== Checking test expectations ==="
rg -n 'max_turns="\$\{ECC_OBSERVER_MAX_TURNS:-20\}"' tests/hooks/hooks.test.js

echo ""
echo "=== Checking if test was recently modified ==="
git log -1 --oneline -- tests/hooks/hooks.test.js

Repository: affaan-m/ECC

Length of output: 354


Fix failing hook test assertion (old :-20 string removed)

tests/hooks/hooks.test.js still asserts that observer-loop.sh contains the exact string max_turns="${ECC_OBSERVER_MAX_TURNS:-20}", but the new logic auto-scales max_turns (clamped to 20–100) instead of using that fixed :-20 default—so CI will fail when the literal is no longer present.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/continuous-learning-v2/agents/observer-loop.sh` around lines 208 -
218, The test assertion expecting the literal string
max_turns="${ECC_OBSERVER_MAX_TURNS:-20}" must be updated to reflect the new
auto-scaling logic in observer-loop.sh: change tests/hooks/hooks.test.js to no
longer look for the removed ":-20" default and instead assert that
observer-loop.sh references the ECC_OBSERVER_MAX_TURNS variable and implements
the fallback autoscale block (checks for ECC_OBSERVER_MAX_TURNS being non-empty,
otherwise computes max_turns from analysis_count and clamps to 20–100). Ensure
the test verifies presence of the ECC_OBSERVER_MAX_TURNS reference and the
autoscale/clamp logic (or matches a regex covering those lines) rather than the
old fixed default literal.

Copy link
Copy Markdown
Contributor

@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 1 file

Re-trigger cubic

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.

Observer default MAX_TURNS=20 insufficient for MAX_ANALYSIS_LINES=500

1 participant