Skip to content

Claude gets stuck in read/plan loops without producing code changes #146

@LarsCowe

Description

@LarsCowe

Problem

When Ralph starts a complex story, Claude often spends the entire timeout duration (30-60 minutes) reading and exploring the codebase without ever writing code or committing changes. The loop then times out (exit 124) or silently dies, and the user has to manually restart — only for the same cycle to repeat.

Observed pattern

  1. Ralph starts Loop [P0] mergeClaudeMd truncates user content after BMAD section #1 on a new story (e.g., "Calculate & Display Optimal Time")
  2. Claude begins exploring the codebase — reading files, analyzing patterns
  3. 30-60 minutes pass with no file changes, no commits
  4. Timeout kills the process, output log is 0 bytes
  5. User restarts, same thing happens again

This was observed across multiple consecutive runs over 4+ hours with zero code output on a single story, while earlier simpler stories completed successfully.

Root cause analysis

Several factors contribute:

  1. Stories are too broad — "Calculate & Display Optimal Time" spans backend logic, UI, and integration. Claude tries to understand everything before writing anything.

  2. No "start writing" pressure — the prompt and fix plan don't push Claude to begin coding early. Claude defaults to thorough exploration, which with Opus can take 30+ minutes on a non-trivial codebase.

  3. Ultimate review mode overhead — adds a review step after every story, consuming time and tokens that could go toward implementation.

  4. Opus is slow for implementation — Claude Opus optimizes for thoroughness over speed. For straightforward implementation tasks, Sonnet is 3-5x faster and often sufficient.

  5. Lost context on restart — each timeout kills the session. When Ralph restarts, Claude begins exploration from scratch, creating a Groundhog Day loop.

Proposed solutions

A. Framework-level improvements (bmalph changes)

A1. Add "write early" guidance to default prompts

The default PROMPT.md or @AGENT.md should include instructions like:

Start writing code within the first 5 minutes. Do not spend more than 
5 minutes reading the codebase before creating your first file. 
You can refine and iterate — the goal is incremental progress, not 
perfect understanding before the first line of code.

A2. Detect and break read-only loops

If Ralph could monitor Claude's tool usage (via the JSON output stream), it could detect when Claude has only used Read/Grep/Glob for N minutes without any Write/Edit calls, and inject a nudge:

You have been reading for 10 minutes without writing any code. 
Please start implementing now with what you know.

A3. Support model-per-phase configuration

Allow different models for different phases:

# In .ralphrc
IMPLEMENTATION_MODEL="sonnet"    # Fast model for writing code
REVIEW_MODEL="opus"              # Thorough model for code review
PLANNING_MODEL="opus"            # Thorough model for initial planning

A4. Warm-start for new stories

Before starting a full implementation loop, run a short "planning" call (5-minute timeout, cheaper model) that produces a concrete plan. Then feed that plan into the implementation loop. This prevents the implementation loop from spending 30+ minutes planning.

A5. Progress heartbeat detection

Instead of only checking git changes at the end of a loop, periodically check for file system changes during execution. If no Write/Edit tool calls have been made in 10 minutes, log a warning or kill the process early rather than waiting for the full timeout.

B. User-level workarounds (available now)

B1. Split stories into atomic tasks

Break broad stories into focused, single-responsibility tasks:

❌ "Calculate & Display Optimal Time"
✅ "Create calculateOptimalTime() in lib/optimal-time.ts with unit tests"
✅ "Create OptimalTimeDisplay component that shows the result"
✅ "Wire optimal time calculation into the meeting response flow"

B2. Make @fix_plan.md hyper-specific

- [ ] Create `lib/optimal-time.ts` — export `calculateOptimalTime(responses: Response[]): TimeSlot`
- [ ] Add tests in `lib/optimal-time.test.ts` — test empty input, single response, overlapping slots
- [ ] Create `components/OptimalTimeDisplay.tsx` — receives TimeSlot, renders formatted time

B3. Use Sonnet for implementation, Opus for review

bmalph run --model sonnet --review enhanced

B4. Seed the first file manually

Start the story in an interactive Claude session, create the skeleton files, commit, then let Ralph iterate. Ralph is better at refining than starting from scratch.

B5. Turn off review mode for faster iteration

REVIEW_MODE="off"

Environment

  • bmalph version: 2.10.0
  • Platform: Windows 11 (bash via Git Bash)
  • Driver: claude-code
  • Model: Claude Opus 4.6
  • Story: 3.1 "Calculate & Display Optimal Time" — stuck for 4+ hours across multiple restarts

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions