[feat] Huge improvements: State-driven architecture, iterative stories, and automatic compaction#101
Conversation
Introduce a robust session-oriented Ralph workflow: add BOOTSTRAP.md, COMPACTION.md, ITERATIVE.md, PROGRESS-GUIDE.md and an initial progress.md to define bootstrapping, compaction, iterative story rules, and progress report formats. Revise CLAUDE.md and README.md to use session/state.json + progress.md semantics and to document the iterative mode and institutional memory. Update prd.json.example to demonstrate an iterative story and validationCommand. Overhaul ralph.sh to use sessions (rename vars), add --debug, validate prd.json, archive runs, bootstrap and persist state.json, switch progress.txt → progress.md, support conditional Claude debug args, and detect completion by reading prd.json. Remove obsolete skills/prd SKILL.md and update skills/ralph SKILL.md. These changes formalize state handling, session lifecycle, compaction, and iteration rules for the autonomous agent loop.
Major changes: - Move all Ralph orchestration files to scripts/ralph/ (preserves git history) - Create slim root CLAUDE.md pointer to scripts/ralph/CLAUDE.md - Update compactionThreshold from 7500 to 10000 - Improve COMPACTION.md with three-zone incremental compaction - Expand Institutional Memory guidance with review & prune rules - Update .gitignore to track .claude/settings.json (exclude only .local) - Add .claudeignore for archives, flowchart, and build artifacts - Update all path references across AGENTS.md, README.md, prompt.md, SKILL.md - Add JSON schema validation to ralph.sh (PRD fields + state.json integrity) - Add Claude Code hooks (post-commit warning + stop-time state.json check) All 16 files modified/created per restructuring plan.
…ss.md - Rewrite scripts/ralph/prompt.md to mirror CLAUDE.md's state-driven architecture (state.json orientation, iterative story support, PROMOTE step, State Update Rules, Critical Rules) - Remove leftover progress.md from root (was from a previous Ralph run) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Greptile OverviewGreptile SummaryThis PR introduces a state-driven session management architecture for Ralph, replacing the previous scan-based story selection with an explicit Major improvements:
Backwards compatibility: Existing Key architectural insight: The previous design relied on agents scanning Minor notes:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Shell as ralph.sh
participant State as state.json
participant PRD as prd.json
participant Agent as AI Agent
participant Progress as progress.md
participant Git as Git Repo
Shell->>PRD: Validate schema (project, branchName, userStories)
Shell->>State: Check if exists
alt state.json missing (first run)
Shell->>PRD: Query first story with passes==false
Shell->>State: Bootstrap with currentStory, branch, nextAction
end
Shell->>Agent: Spawn session (pass CLAUDE.md or prompt.md)
Agent->>State: Read currentStory, nextAction, compactionNeeded
alt compactionNeeded==true
Agent->>Progress: Read full file in chunks
Agent->>Progress: Compact (keep patterns, keep recent, compress middle)
Agent->>State: Set compactionNeeded=false
Agent->>Git: Commit compaction
Agent-->>Shell: End session
else story has iterationMode
Agent->>Agent: Load ITERATIVE.md (conditional)
Agent->>PRD: Read current story acceptance criteria
Agent->>Progress: Read Codebase Patterns + recent sessions
Agent->>Agent: Implement iteration N
Agent->>Agent: Run quality checks + validationCommand
Agent->>Git: Commit changes (iteration N)
Agent->>PRD: Increment currentIteration, set passes if currentIteration>=maxIterations
Agent->>Progress: Append session report
Agent->>State: Update to next story/iteration
Agent-->>Shell: End session
else standard story
Agent->>PRD: Read current story acceptance criteria
Agent->>Progress: Read Codebase Patterns + recent sessions
Agent->>Agent: Implement story
Agent->>Agent: Run quality checks
Agent->>Git: Commit changes
Agent->>PRD: Set passes=true
Agent->>Progress: Append session report + promote learnings
Agent->>State: Update to next story
Agent-->>Shell: End session
end
Shell->>PRD: Check if all stories have passes==true
alt All stories complete
Shell->>Shell: Exit with success
else More stories remain
Shell->>State: Validate integrity (currentStory, lastUpdated, branch)
Shell->>Agent: Spawn next session
end
Last reviewed commit: f3c79a2 |
| FIRST_STORY_ID=$(jq -r '[.userStories[] | select(.passes == false)] | sort_by(.priority) | first | .id // empty' "$PRD_FILE") | ||
| FIRST_STORY_TITLE=$(jq -r '[.userStories[] | select(.passes == false)] | sort_by(.priority) | first | .title // empty' "$PRD_FILE") |
There was a problem hiding this comment.
Consider using select(.passes == false or .passes == null) to handle stories where the passes field might be missing
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Pull request overview
This PR restructures Ralph into a state-driven, session-oriented agent loop with progressive disclosure docs, iterative story support, and automatic progress log compaction—moving orchestration into scripts/ralph/ and consolidating PRD generation into a single /ralph skill.
Changes:
- Introduces
state.json-driven session management plus new guides for bootstrap, compaction, and iterative stories. - Replaces root-level Ralph assets with a
scripts/ralph/layout and updates README/agent pointers accordingly. - Adds Claude Code hooks and updates
.gitignore/.claudeignorefor the new workflow.
Reviewed changes
Copilot reviewed 18 out of 19 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| skills/ralph/SKILL.md | Merges PRD generation + prd.json generation into one skill and documents iterative stories/archiving/checklists. |
| skills/prd/SKILL.md | Removes the separate /prd skill in favor of the unified /ralph skill. |
| scripts/ralph/ralph.sh | New loop script with PRD validation, state bootstrap, archiving/reset behavior, and debug mode. |
| scripts/ralph/prompt.md | Updated Amp instructions for state-first workflow, compaction gating, and promotion rules. |
| scripts/ralph/prd.json.example | Adds example schema including iterative story fields. |
| scripts/ralph/PROGRESS-GUIDE.md | Defines progress log format, promotion rules, and notes compaction guidance. |
| scripts/ralph/ITERATIVE.md | Defines iterative story semantics and PRD update rules per iteration/session. |
| scripts/ralph/COMPACTION.md | Defines compaction-only session behavior and rewrite rules for progress.md. |
| scripts/ralph/CLAUDE.md | Core Claude Code instructions updated for state-driven workflow and institutional memory section. |
| scripts/ralph/BOOTSTRAP.md | Provides deterministic state bootstrap instructions when state.json is missing. |
| ralph.sh | Removes the legacy root loop script. |
| prompt.md | Removes the legacy root prompt template. |
| prd.json.example | Removes the legacy root example PRD JSON. |
| README.md | Updates install/workflow/docs to new session + state model and new file layout. |
| CLAUDE.md | Replaces root instructions with a pointer to scripts/ralph/CLAUDE.md. |
| AGENTS.md | Updates run commands and references to new Ralph file locations. |
| .gitignore | Updates ignored working files to scripts/ralph/* and adds common build artifact ignores. |
| .claudeignore | Adds ignore patterns for archives, binaries, build artifacts, env files, etc. |
| .claude/settings.json | Adds Claude Code hooks for commit warning and session-stop gating. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| FOLDER_NAME=$(echo "$LAST_BRANCH" | sed 's|^ralph/||') | ||
| ARCHIVE_FOLDER="$ARCHIVE_DIR/$DATE-$FOLDER_NAME" | ||
|
|
||
| echo "Archiving previous run: $LAST_BRANCH" |
There was a problem hiding this comment.
The archive folder name/message is derived from LAST_BRANCH, but the files being archived are the current working files. If prd.json has already been updated to the new branch, this will archive the new PRD under the old branch’s folder/name, which is misleading and can lead to the wrong run being preserved. Consider naming the archive folder from the branchName contained in the files you are copying (or store a snapshot of the previous PRD/branch separately) so the archive metadata matches the archived contents.
| FOLDER_NAME=$(echo "$LAST_BRANCH" | sed 's|^ralph/||') | |
| ARCHIVE_FOLDER="$ARCHIVE_DIR/$DATE-$FOLDER_NAME" | |
| echo "Archiving previous run: $LAST_BRANCH" | |
| # Use the branch name from the current PRD (CURRENT_BRANCH) for archive naming, | |
| # so that the archive metadata matches the contents being copied. | |
| FOLDER_NAME=$(echo "$CURRENT_BRANCH" | sed 's|^ralph/||') | |
| ARCHIVE_FOLDER="$ARCHIVE_DIR/$DATE-$FOLDER_NAME" | |
| echo "Archiving previous run (branch changed from $LAST_BRANCH to $CURRENT_BRANCH)" |
| echo " Created state.json: $FIRST_STORY_ID - $FIRST_STORY_TITLE" | ||
| fi | ||
| fi | ||
|
|
There was a problem hiding this comment.
The script only checks prd.json completion after spawning a tool session. If all stories are already passes: true, this still starts a session unnecessarily. Consider checking ALL_PASS once right after validating prd.json (before bootstrapping/running any tool) and exiting early to avoid wasted runs.
| # Early exit if all stories already pass in prd.json | |
| ALL_PASS=$(jq '[.userStories[].passes] | all' "$PRD_FILE" 2>/dev/null || echo "false") | |
| if [ "$ALL_PASS" = "true" ]; then | |
| echo "All user stories in prd.json already pass. No sessions needed." | |
| exit 0 | |
| fi |
| if [[ "$TOOL" == "amp" ]]; then | ||
| OUTPUT=$(cat "$SCRIPT_DIR/prompt.md" | amp --dangerously-allow-all 2>&1 | tee /dev/stderr) || true | ||
| else |
There was a problem hiding this comment.
OUTPUT=$(...) captures the full tool output into a shell variable, which can become extremely large (especially in --debug stream-json mode) and consume significant memory even though OUTPUT is no longer used for completion detection. Consider streaming directly to stderr via tee without storing in a variable (or only capturing when needed).
| ITER_MODE=$(jq -r --arg id "$FIRST_STORY_ID" '[.userStories[] | select(.id == $id)] | first | .iterationMode // empty' "$PRD_FILE") | ||
| MAX_ITER=$(jq -r --arg id "$FIRST_STORY_ID" '[.userStories[] | select(.id == $id)] | first | .maxIterations // 0' "$PRD_FILE") | ||
|
|
||
| if [ -n "$ITER_MODE" ] && [ "$ITER_MODE" != "null" ]; then |
There was a problem hiding this comment.
For iterative stories, maxIterations defaults to 0 (.maxIterations // 0), which can produce nextAction like iteration 1/0 and hides invalid PRD input. Consider validating iterative story schema up-front (e.g., require maxIterations >= 1 and numeric currentIteration) and failing fast with a clear error if invalid.
| if [ -n "$ITER_MODE" ] && [ "$ITER_MODE" != "null" ]; then | |
| if [ -n "$ITER_MODE" ] && [ "$ITER_MODE" != "null" ]; then | |
| # Validate that maxIterations is a positive integer for iterative stories. | |
| if ! [[ "$MAX_ITER" =~ ^[0-9]+$ ]] || [ "$MAX_ITER" -lt 1 ]; then | |
| echo "Error: invalid or missing maxIterations for iterative story '$FIRST_STORY_ID' in '$PRD_FILE' (got: '$MAX_ITER')." >&2 | |
| echo "Please ensure 'maxIterations' is a positive integer when 'iterationMode' is set." >&2 | |
| exit 1 | |
| fi |
| "hooks": [ | ||
| { | ||
| "type": "command", | ||
| "command": "node -e \"const fs=require('fs');try{const s=JSON.parse(fs.readFileSync('scripts/ralph/state.json','utf8'));const today=new Date().toISOString().slice(0,10);if(s.lastUpdated!==today){console.log('state.json not updated today ('+s.lastUpdated+' vs '+today+')');process.exit(2)}}catch(e){}\"" |
There was a problem hiding this comment.
The Stop hook compares state.json.lastUpdated to new Date().toISOString().slice(0,10) (UTC date), while ralph.sh writes lastUpdated using local time (date +%Y-%m-%d). This can incorrectly block session stop around local/UTC midnight. Also, JSON/FS errors are silently ignored (catch(e){}), which means missing/corrupt state.json won’t block the stop as intended. Consider using local date in Node to match ralph.sh and exiting non-zero when state.json can’t be read/parsed.
| "command": "node -e \"const fs=require('fs');try{const s=JSON.parse(fs.readFileSync('scripts/ralph/state.json','utf8'));const today=new Date().toISOString().slice(0,10);if(s.lastUpdated!==today){console.log('state.json not updated today ('+s.lastUpdated+' vs '+today+')');process.exit(2)}}catch(e){}\"" | |
| "command": "node -e \"const fs=require('fs');try{const s=JSON.parse(fs.readFileSync('scripts/ralph/state.json','utf8'));const d=new Date();const today=d.getFullYear()+'-'+String(d.getMonth()+1).padStart(2,'0')+'-'+String(d.getDate()).padStart(2,'0');if(s.lastUpdated!==today){console.log('state.json not updated today ('+s.lastUpdated+' vs '+today+')');process.exit(2)}}catch(e){console.error('Failed to read or parse scripts/ralph/state.json',e);process.exit(2);}\"" |
| "Settings panel accessible from user menu", | ||
| "All preference fields rendered with correct input types", | ||
| "Save button persists changes to backend", | ||
| "Verify in browser: panel renders, fields are interactive, save works" |
There was a problem hiding this comment.
In this example PRD, US-002 acceptance criteria omit a quality gate (e.g., “Typecheck passes” / “Tests pass”), but the skill guidance in skills/ralph/SKILL.md says every story should include at least one quality gate. Consider updating the example to match the documented rules so users don’t copy an invalid pattern.
| "Verify in browser: panel renders, fields are interactive, save works" | |
| "Verify in browser: panel renders, fields are interactive, save works", | |
| "Tests pass" |
| 3. **Archive previous run** if `prd.json` already exists with a different `branchName` (see Archiving below) | ||
| 4. Generate the PRD markdown file | ||
| 5. Generate the `prd.json` file |
There was a problem hiding this comment.
Step 3 says to archive the previous run if prd.json exists with a different branchName, but the canonical path elsewhere in this doc is scripts/ralph/prd.json. Consider updating this reference to avoid archiving/checking the wrong file location.
| 3. **Archive previous run** if `prd.json` already exists with a different `branchName` (see Archiving below) | |
| 4. Generate the PRD markdown file | |
| 5. Generate the `prd.json` file | |
| 3. **Archive previous run** if `scripts/ralph/prd.json` already exists with a different `branchName` (see Archiving below) | |
| 4. Generate the PRD markdown file | |
| 5. Generate the `scripts/ralph/prd.json` file |
| Ralph will: | ||
| 1. Create a feature branch (from PRD `branchName`) | ||
| 2. Pick the highest priority story where `passes: false` | ||
| 3. Implement that single story | ||
| 4. Run quality checks (typecheck, tests) | ||
| 5. Commit if checks pass | ||
| 6. Update `prd.json` to mark story as `passes: true` | ||
| 7. Append learnings to `progress.txt` | ||
| 8. Repeat until all stories pass or max iterations reached | ||
| 1. Read `state.json` for current story, branch, and next action | ||
| 2. Create/checkout the feature branch (from `state.json`) | ||
| 3. Pick the current story (from `state.json`, NOT by scanning prd.json) | ||
| 4. Implement that single story (or one iteration of an iterative story) |
There was a problem hiding this comment.
This workflow section refers to state.json, progress.md, and prd.json without the scripts/ralph/ prefix, but the rest of the README (and the repo layout) uses scripts/ralph/*. Consider making these paths explicit (e.g., scripts/ralph/state.json) so users don’t create/modify files in the repo root by mistake.
| | **Operational** | `state.json` | Current story, next action, branch, compaction status | Per-session (updated every session) | | ||
| | **Structural** | `prd.json` | Story status, iteration counters, notes | Per-PRD | | ||
| | **Experiential** | `progress.md` | Session logs, codebase patterns, learnings | Per-PRD, append-only (auto-compacted) | | ||
| | **Institutional** | `scripts/ralph/CLAUDE.md` | Reusable conventions, gotchas, cross-file dependencies | Permanent — survives across PRDs | |
There was a problem hiding this comment.
In the memory model table, Operational/Structural/Experiential layers list files as state.json / prd.json / progress.md, but the actual tracked locations are scripts/ralph/state.json, scripts/ralph/prd.json, and scripts/ralph/progress.md. Consider aligning these paths to match the new directory structure (and, if applicable, mention where Amp vs Claude store institutional memory).
| - Memory persists via git history, `scripts/ralph/progress.md`, `scripts/ralph/prd.json`, and `scripts/ralph/state.json` | ||
| - Stories should be small enough to complete in one context window | ||
| - Always update AGENTS.md with discovered patterns for future iterations | ||
| - Always update Institutional Memory in scripts/ralph/CLAUDE.md with discovered patterns for future sessions |
There was a problem hiding this comment.
This document says to “Always update Institutional Memory in scripts/ralph/CLAUDE.md”, but the Amp prompt (scripts/ralph/prompt.md) instructs updating AGENTS.md files instead. Consider clarifying which file is the institutional memory source of truth per tool (Amp vs Claude) to avoid learnings being written to the wrong place.
| - Always update Institutional Memory in scripts/ralph/CLAUDE.md with discovered patterns for future sessions | |
| - Always update institutional memory in the appropriate file for the tool you're using: | |
| - For Amp runs, update this `AGENTS.md` and `scripts/ralph/prompt.md` with discovered patterns for future sessions | |
| - For Claude Code runs, update `scripts/ralph/CLAUDE.md` with discovered patterns for future sessions |
Summary
This PR introduces a state-driven session management system, a four-layer memory model, iterative story support, and automatic progress compaction — addressing the key reliability and scalability issues that emerge when running Ralph on real-world projects with 10+ stories.
These changes are backwards-compatible: existing
prd.jsonfiles work without modification, and the shell loop auto-bootstrapsstate.jsonon first run.Motivation
After running Ralph extensively on production codebases, we encountered recurring failure modes that the original design doesn't handle:
Session disorientation. Each session scans
prd.jsonfor the highest-priority incomplete story. When multiple stories exist at the same priority, or when a session fails mid-way, the next session may pick a different story or repeat work. There is no single source of truth for "what should I do next."Context exhaustion.
progress.md(originallyprogress.txt) grows indefinitely. After 15-20 sessions, it fills the context window, leaving no room for actual implementation. There is no compaction mechanism.No multi-pass support. Some tasks (auditing N files, multi-pass refactors) inherently benefit from multiple sessions on the same story. The original design assumes one story = one session, with no way to express "repeat this story 5 times."
Fragile completion detection. The original relies on the agent outputting
<promise>COMPLETE</promise>, which is fragile — the agent might include it in explanatory text, or omit it due to context limits.ralph.shalready checksprd.jsondirectly, making the text-based signal redundant.No guardrails. If the agent forgets to update
state.jsonor commits without marking the story as passed, there's no feedback mechanism. Errors compound silently across sessions.What changed
1. State-driven session management
New file:
scripts/ralph/BOOTSTRAP.mdEach session now starts by reading
state.jsoninstead of scanningprd.json:{ "branch": "ralph/feature-name", "currentStory": "US-003", "storyTitle": "Add filter dropdown", "nextAction": "implement", "progressTokens": 2400, "compactionNeeded": false, "compactionThreshold": 10000, "lastUpdated": "2026-02-13" }Why this is better: The previous session explicitly tells the next session what to do. No ambiguity, no scanning, no race conditions. If
state.jsonis missing (first run or corruption),BOOTSTRAP.mdprovides a deterministic recovery path.ralph.shalso auto-bootstrapsstate.jsonbefore the first session, so the agent doesn't waste context on bootstrap logic.2. Four-layer memory model
The original has two memory layers (prd.json + progress.txt). This PR introduces a structured four-layer model:
state.jsonprd.jsonprogress.mdCLAUDE.mdWhy this is better: Each layer has a clear purpose and lifespan. The Institutional Memory layer (a budgeted section at the bottom of
CLAUDE.md) persists conventions and gotchas that survive across PRDs — something the original design loses every time you start a new feature.3. Progressive disclosure
The original loads everything into every session. This PR splits the agent instructions into conditionally-loaded documents:
BOOTSTRAP.mdstate.jsonmissing (first run only)COMPACTION.mdstate.json.compactionNeeded === trueITERATIVE.mditerationModefieldPROGRESS-GUIDE.mdWhy this is better: A standard session loads only
CLAUDE.md(orprompt.mdfor Amp) +state.json+ partialprogress.md+ the relevant story fromprd.json. The agent spends its context on implementation, not on instructions it doesn't need.4. Automatic progress compaction
New file:
scripts/ralph/COMPACTION.mdWhen
progress.mdexceeds the token threshold (~10,000 tokens),state.jsonflagscompactionNeeded: true. The next session dedicates itself entirely to compaction using a three-zone approach:Before compacting, the agent promotes any un-promoted learnings to the Codebase Patterns section, preventing knowledge loss.
Why this is better: The original has no compaction. After ~20 sessions,
progress.mdfills the context window and sessions start failing. The three-zone approach preserves the most useful information while reclaiming ~70% of tokens from intermediate sessions.5. Iterative stories
New file:
scripts/ralph/ITERATIVE.mdStories can now declare
iterationMode: "fixed"with amaxIterationscount. One iteration = one session. The story is markedpasses: trueonly whencurrentIteration >= maxIterations.{ "id": "US-003", "title": "Audit notification triggers", "iterationMode": "fixed", "maxIterations": 5, "currentIteration": 0, "passes": false }Why this is better: Multi-pass tasks like audits, batch refactors, and review passes benefit from fresh context across sessions. The original forces these into a single session (which runs out of context) or requires manual splitting into N identical stories.
Key design decisions:
6. Unified PRD skill
The original has two separate skills:
skills/prd/SKILL.md(PRD generator) andskills/ralph/SKILL.md(JSON converter). This PR merges them into a singleskills/ralph/SKILL.mdthat produces both outputs in one step:tasks/prd-[feature-name].mdscripts/ralph/prd.jsonThe unified skill also adds:
7. Repository structure
All Ralph orchestration files moved from root to
scripts/ralph/:Root
CLAUDE.mdis now a slim pointer:When working on Ralph stories, read and follow scripts/ralph/CLAUDE.md.Additional files:
.claudeignore: Excludes archives, binary files, build artifacts, and env files from Claude Code's context.claude/settings.json: Two hooks (see Guardrails below).gitignore: Updated to track.claude/settings.json(shared hooks) while ignoring.claude/settings.local.json(personal settings)Why this is better: Cleaner root directory. When users copy Ralph into their projects, they copy one directory (
scripts/ralph/) instead of individual files scattered at root. The nestedCLAUDE.mdis automatically discovered by Claude Code alongside the root pointer.8. Robust shell loop
ralph.shimprovements:project,branchName,userStories) before spawning any sessionsstate.jsonbootstrapping: createsstate.jsonfromprd.jsonbefore the first session, including iterative story detectionstate.jsonintegrity check after each session: warns (non-blocking) if required fields are missing--debug): enablesstream-jsonoutput format for Claude Code diagnosticsstate.jsonalongsideprd.jsonandprogress.mdprd.jsononly (removed dependency on agent text output)iterations→sessions(iterations now refers to the iterative story feature)9. Claude Code hooks (guardrails)
New file:
.claude/settings.jsonTwo hooks that provide automated feedback to the agent:
git commitpasses: trueyetstate.json.lastUpdatedisn't today's date (ensures state is updated every session)These are committed to the repo so they work out of the box for Claude Code users. Amp users are unaffected.
10. Mandatory knowledge promotion
Every session now includes a PROMOTE step: after writing learnings in
progress.md, the agent must re-read them and promote anything useful beyond the current story to:progress.md) — survives compaction, read first every sessionCLAUDE.md) or AGENTS.md — survives across PRDsNew file:
scripts/ralph/PROGRESS-GUIDE.md— reference for progress report format and notes compaction rules.Why this is better: In the original, valuable learnings buried in session logs are effectively lost after compaction or when starting a new PRD. The promotion step ensures important discoveries bubble up to persistent layers.
Migration
No migration needed. Existing
prd.jsonfiles work as-is:ralph.shauto-bootstrapsstate.jsonfromprd.jsonon first runiterationModeare treated as standard (single-session) storiesprogress.mdis initialized automatically if missingThe only user-facing change: files are now in
scripts/ralph/instead of root. Update your.gitignorepaths accordingly.Test plan
state.jsonandprogress.md, runralph.sh— verify auto-bootstrap creates bothpasses: trueandstate.jsonupdatedcurrentIterationincrements andpassesonly becomestrueat iteration 3progress.md(>10k tokens), setcompactionNeeded: trueinstate.json, verify compaction session runs correctlybranchNameinprd.json, verify previous run is archiveduserStoriesfromprd.json, verifyralph.shexits with error before spawning sessionspasses: true, verify warning appears; end session without updatingstate.json, verify session is blocked