Description
lib/response_analyzer.sh detects loop progress exclusively through git: it runs git rev-parse --git-dir from the loop's working directory and, only if that succeeds, counts changed files via git diff (JSON path around response_analyzer.sh:568-602, text path around :763-796 at e8533cc).
When Ralph supervises a multi-repo workspace — a root folder that contains several sub-repositories plus .ralph/, where the root itself is not a git repository — the git rev-parse gate fails every loop, so the entire file-change check is skipped. The Claude CLI JSON result contains no files_modified / metadata.files_changed field either, so files_modified stays 0 and has_progress stays false on every loop, regardless of how much real work was done.
Work that is invisible to the analyzer in this layout includes:
- commits made in sub-repositories via
git -C <sub-repo> (encouraged by the compound-command guidance, since cd is unavailable)
- GitHub-side work (
gh pr comment, review consolidation, issue updates) that changes no local file
.ralph/fix_plan.md tracker updates (even when .ralph/ is itself a git repository — git rev-parse from the parent CWD searches upward, not downward)
The result: consecutive_no_progress increments unconditionally and the circuit breaker opens after the threshold, no matter what the agent actually accomplished. The operator's only recourse is repeated manual ralph --reset-circuit, or raising CB_NO_PROGRESS_THRESHOLD so high that the no-progress breaker is effectively disabled — both of which discard the stagnation signal entirely.
This is the non-git-CWD sibling of #141 ("no progress when Claude commits within the loop"). The #141 fix added committed-changes detection, but it still assumes the loop CWD is inside a git repository.
Current Behavior
In a non-git multi-repo workspace root:
- A loop completes genuine work (e.g. consolidates a PR review, posts the review summary, updates
.ralph/fix_plan.md, syncs a RAG store) and exits with subtype: success, no permission denials.
- The analyzer still records
has_progress: false, files_modified: 0.
consecutive_no_progress reaches the threshold and the breaker opens: "No recovery, opening circuit after N loops".
- Every subsequent run repeats this, so the breaker re-opens perpetually.
Expected Behavior
Progress detection should have at least one signal path that works when the loop CWD is not a git repository.
Possible directions (any one of these would fix the class of false positives):
- Fall back to the
.ralph/ directory's own repository: if git -C "$RALPH_DIR" rev-parse succeeds, diff there too — tracker updates (fix_plan.md) are a reasonable progress proxy.
- Trust an explicit agent self-report: the analyzer already extracts
EXIT_SIGNAL from a RALPH_STATUS block embedded in the .result text. A FILES_MODIFIED: N (or PROGRESS: true) line in the same block could set has_progress with the same explicit-intent semantics.
- Configurable progress roots: e.g.
RALPH_PROGRESS_GIT_DIRS="sub-repo-a sub-repo-b .ralph" in .ralphrc; the analyzer iterates them with git -C.
Workaround (if any)
Raising CB_NO_PROGRESS_THRESHOLD to a very high value in .ralphrc — which effectively disables no-progress stagnation detection rather than fixing it.
Environment
- ralph-claude-code: e8533cc (main)
CLAUDE_OUTPUT_FORMAT="json", PROJECT_TYPE="multi"
- macOS (bash loop, not SDK)
- Workspace layout: non-git root folder containing multiple sub-repositories +
.ralph/
Description
lib/response_analyzer.shdetects loop progress exclusively through git: it runsgit rev-parse --git-dirfrom the loop's working directory and, only if that succeeds, counts changed files viagit diff(JSON path aroundresponse_analyzer.sh:568-602, text path around:763-796at e8533cc).When Ralph supervises a multi-repo workspace — a root folder that contains several sub-repositories plus
.ralph/, where the root itself is not a git repository — thegit rev-parsegate fails every loop, so the entire file-change check is skipped. The Claude CLI JSON result contains nofiles_modified/metadata.files_changedfield either, sofiles_modifiedstays0andhas_progressstaysfalseon every loop, regardless of how much real work was done.Work that is invisible to the analyzer in this layout includes:
git -C <sub-repo>(encouraged by the compound-command guidance, sincecdis unavailable)gh pr comment, review consolidation, issue updates) that changes no local file.ralph/fix_plan.mdtracker updates (even when.ralph/is itself a git repository —git rev-parsefrom the parent CWD searches upward, not downward)The result:
consecutive_no_progressincrements unconditionally and the circuit breaker opens after the threshold, no matter what the agent actually accomplished. The operator's only recourse is repeated manualralph --reset-circuit, or raisingCB_NO_PROGRESS_THRESHOLDso high that the no-progress breaker is effectively disabled — both of which discard the stagnation signal entirely.This is the non-git-CWD sibling of #141 ("no progress when Claude commits within the loop"). The #141 fix added committed-changes detection, but it still assumes the loop CWD is inside a git repository.
Current Behavior
In a non-git multi-repo workspace root:
.ralph/fix_plan.md, syncs a RAG store) and exits withsubtype: success, no permission denials.has_progress: false, files_modified: 0.consecutive_no_progressreaches the threshold and the breaker opens:"No recovery, opening circuit after N loops".Expected Behavior
Progress detection should have at least one signal path that works when the loop CWD is not a git repository.
Possible directions (any one of these would fix the class of false positives):
.ralph/directory's own repository: ifgit -C "$RALPH_DIR" rev-parsesucceeds, diff there too — tracker updates (fix_plan.md) are a reasonable progress proxy.EXIT_SIGNALfrom aRALPH_STATUSblock embedded in the.resulttext. AFILES_MODIFIED: N(orPROGRESS: true) line in the same block could sethas_progresswith the same explicit-intent semantics.RALPH_PROGRESS_GIT_DIRS="sub-repo-a sub-repo-b .ralph"in.ralphrc; the analyzer iterates them withgit -C.Workaround (if any)
Raising
CB_NO_PROGRESS_THRESHOLDto a very high value in.ralphrc— which effectively disables no-progress stagnation detection rather than fixing it.Environment
CLAUDE_OUTPUT_FORMAT="json",PROJECT_TYPE="multi".ralph/