⚠️ WARNING : This skill was deprecated in favor of a new command that uses a Python orchestrator script. The old command will be removed soon. Please migrate to the new command. Ralph Loop — Python Orchestrator ⚠️ IMPORTANT : This skill uses a Python orchestrator script. Do NOT execute arbitrary bash commands. Use ONLY to run . All task commands (like ) are shown to the user to execute manually. Overview The Ralph Loop applies Geoffrey Huntley's "Ralph Wiggum as a Software Engineer" technique to specification-driven development. It uses a Python orchestrator script that manages a state machin…

, frontmatter, re.MULTILINE)\n if id_match:\n task_data[\"id\"] = id_match.group(1).strip().strip('\"\\'')\n\n title_match = re.search(r'^title:\\s*(.+)

⚠️ WARNING : This skill was deprecated in favor of a new command that uses a Python orchestrator script. The old command will be removed soon. Please migrate to the new command. Ralph Loop — Python Orchestrator ⚠️ IMPORTANT : This skill uses a Python orchestrator script. Do NOT execute arbitrary bash commands. Use ONLY to run . All task commands (like ) are shown to the user to execute manually. Overview The Ralph Loop applies Geoffrey Huntley's "Ralph Wiggum as a Software Engineer" technique to specification-driven development. It uses a Python orchestrator script that manages a state machin…

, frontmatter, re.MULTILINE)\n if title_match:\n task_data[\"title\"] = title_match.group(1).strip().strip('\"\\'')\n\n status_match = re.search(r'^status:\\s*(.+)

⚠️ WARNING : This skill was deprecated in favor of a new command that uses a Python orchestrator script. The old command will be removed soon. Please migrate to the new command. Ralph Loop — Python Orchestrator ⚠️ IMPORTANT : This skill uses a Python orchestrator script. Do NOT execute arbitrary bash commands. Use ONLY to run . All task commands (like ) are shown to the user to execute manually. Overview The Ralph Loop applies Geoffrey Huntley's "Ralph Wiggum as a Software Engineer" technique to specification-driven development. It uses a Python orchestrator script that manages a state machin…

, frontmatter, re.MULTILINE)\n if status_match:\n task_data[\"status\"] = status_match.group(1).strip().strip('\"\\'')\n\n lang_match = re.search(r'^lang(?:uage)?:\\s*(.+)

⚠️ WARNING : This skill was deprecated in favor of a new command that uses a Python orchestrator script. The old command will be removed soon. Please migrate to the new command. Ralph Loop — Python Orchestrator ⚠️ IMPORTANT : This skill uses a Python orchestrator script. Do NOT execute arbitrary bash commands. Use ONLY to run . All task commands (like ) are shown to the user to execute manually. Overview The Ralph Loop applies Geoffrey Huntley's "Ralph Wiggum as a Software Engineer" technique to specification-driven development. It uses a Python orchestrator script that manages a state machin…

, frontmatter, re.MULTILINE)\n if lang_match:\n task_data[\"lang\"] = lang_match.group(1).strip().strip('\"\\'')\n\n deps_match = re.search(r'^dependencies:\\s*\\[(.*?)\\]', frontmatter, re.MULTILINE | re.DOTALL)\n if deps_match:\n deps_str = deps_match.group(1)\n task_data[\"dependencies\"] = [d.strip().strip('\"\\'') for d in deps_str.split(\",\") if d.strip()]\n\n complexity_match = re.search(r'^complexity:\\s*(\\w+)', frontmatter, re.MULTILINE)\n if complexity_match:\n task_data[\"complexity\"] = complexity_match.group(1).strip()\n\n agent_match = re.search(r'^agent:\\s*(\\w+)', frontmatter, re.MULTILINE)\n if agent_match:\n task_data[\"agent\"] = agent_match.group(1).strip().lower()\n\n # Extract description from first paragraph after frontmatter\n content_without_front = re.sub(r'^---\\s*\\n.*?\\n---\\s*\\n', '', content, flags=re.DOTALL)\n desc_match = re.search(r'##?\\s*Description\\s*\\n\\s*(.+?)(?:\\n\\n|\\n##|\\Z)', content_without_front, re.DOTALL | re.IGNORECASE)\n if desc_match:\n task_data[\"description\"] = desc_match.group(1).strip()[:100]\n else:\n # First paragraph\n first_para = content_without_front.strip().split('\\n\\n')[0]\n task_data[\"description\"] = first_para[:100] if first_para else \"\"\n\n return task_data\n\n\ndef parse_tasks_from_files(spec_path: str) -> List[dict]:\n \"\"\"Parses all task files from the tasks directory\"\"\"\n task_files = find_task_files(spec_path)\n tasks = []\n\n for task_file in task_files:\n task_data = parse_task_file(task_file)\n tasks.append(task_data)\n\n # Sort by task ID\n tasks.sort(key=lambda t: t[\"id\"])\n\n return tasks\n\n\ndef filter_tasks_by_range(tasks: List[dict], from_task: Optional[str], to_task: Optional[str]) -> List[dict]:\n \"\"\"Filters tasks by range\"\"\"\n if not from_task and not to_task:\n return tasks\n\n def task_num(task_id: str) -> int:\n match = re.search(r'(\\d+)', task_id)\n return int(match.group(1)) if match else 0\n\n from_num = task_num(from_task) if from_task else 0\n to_num = task_num(to_task) if to_task else float('inf')\n\n filtered = []\n for task in tasks:\n num = task_num(task[\"id\"])\n if from_num \u003c= num \u003c= to_num:\n filtered.append(task)\n\n return filtered\n\n\ndef get_next_pending_task(fix_plan: dict) -> Optional[dict]:\n \"\"\"Finds the next pending task with satisfied dependencies\"\"\"\n tasks = fix_plan.get(\"tasks\", [])\n completed_tasks = {t[\"id\"] for t in tasks if t[\"status\"] in [\"done\", \"completed\", \"implemented\", \"reviewed\"]}\n\n for task in tasks:\n if task[\"status\"] not in [\"pending\", \"in_progress\"]:\n continue\n\n # Check dependencies\n deps_satisfied = all(dep in completed_tasks for dep in task.get(\"dependencies\", []))\n if deps_satisfied:\n return task\n\n return None\n\n\ndef get_agent_config(fix_plan: dict, args_agent: str = None) -> dict:\n \"\"\"Returns the agent configuration to use\"\"\"\n # 1. Use agent from CLI args if specified\n if args_agent and args_agent in SUPPORTED_AGENTS:\n return SUPPORTED_AGENTS[args_agent]\n\n # 2. Use agent from current task\n current_task_id = fix_plan.get(\"state\", {}).get(\"current_task\")\n if current_task_id:\n for task in fix_plan.get(\"tasks\", []):\n if task[\"id\"] == current_task_id and task.get(\"agent\"):\n return SUPPORTED_AGENTS.get(task[\"agent\"], SUPPORTED_AGENTS[\"claude\"])\n\n # 3. Use default agent from fix_plan\n default_agent = fix_plan.get(\"default_agent\", \"claude\")\n return SUPPORTED_AGENTS.get(default_agent, SUPPORTED_AGENTS[\"claude\"])\n\n\ndef format_command(agent: dict, cmd_type: str, **kwargs) -> str:\n \"\"\"Formats a command for the specified agent\"\"\"\n cmd_template = agent.get(cmd_type, cmd_type)\n cmd = cmd_template.format(**kwargs)\n return f\"{agent['cmd_prefix']}{cmd}\"\n\n\ndef run_git_commit(spec_path: str, task_id: str, task_title: str, iteration: int) -> bool:\n \"\"\"Commits changes for a completed task\"\"\"\n try:\n # Check if there are changes to commit\n result = subprocess.run(\n [\"git\", \"-C\", spec_path, \"status\", \"--porcelain\"],\n capture_output=True,\n text=True\n )\n if not result.stdout.strip():\n print(f\" No changes to commit\")\n return True\n\n # Stage all changes\n subprocess.run(\n [\"git\", \"-C\", spec_path, \"add\", \"-A\"],\n capture_output=True,\n check=True\n )\n\n # Commit\n commit_msg = f\"Ralph iteration {iteration}: {task_id} - {task_title[:50]}\"\n subprocess.run(\n [\"git\", \"-C\", spec_path, \"commit\", \"-m\", commit_msg],\n capture_output=True,\n check=True\n )\n\n print(f\" Committed: {commit_msg}\")\n return True\n except subprocess.CalledProcessError as e:\n print(f\" ⚠️ Git commit failed: {e}\")\n return False\n except FileNotFoundError:\n print(f\" ⚠️ Git not found\")\n return False\n\n\ndef update_task_status(fix_plan: dict, task_id: str, new_status: str):\n \"\"\"Updates the status of a task in the tasks list\"\"\"\n for task in fix_plan.get(\"tasks\", []):\n if task[\"id\"] == task_id:\n task[\"status\"] = new_status\n break\n\n\ndef action_start(spec_path: str, from_task: Optional[str], to_task: Optional[str], agent: str = \"claude\"):\n \"\"\"Initializes fix_plan.json from task files\"\"\"\n print(f\"🚀 Ralph Loop | Initializing...\")\n print(f\" Spec: {spec_path}\")\n\n # Parse task files\n tasks = parse_tasks_from_files(spec_path)\n\n if not tasks:\n # Fallback: try to find tasks.md\n tasks_file = find_tasks_file(spec_path)\n if tasks_file:\n print(f\" Tasks file: {tasks_file}\")\n print(\" ⚠️ No individual task files found. Creating from tasks.md...\")\n # Create basic task entries from tasks.md parsing\n # This is a simplified version\n else:\n print(\"❌ No tasks found. Create task files in tasks/ directory.\")\n sys.exit(1)\n\n print(f\" Found {len(tasks)} tasks\")\n\n # Apply range filter\n filtered_tasks = filter_tasks_by_range(tasks, from_task, to_task)\n\n # Separate into categories\n pending = [t[\"id\"] for t in filtered_tasks if t[\"status\"] in [\"pending\", \"in_progress\"]]\n done = [t[\"id\"] for t in filtered_tasks if t[\"status\"] in [\"done\", \"completed\", \"implemented\", \"reviewed\"]]\n\n # Create fix_plan with full structure\n fix_plan = {\n \"spec_id\": Path(spec_path).name,\n \"spec_folder\": spec_path,\n \"task_range\": {\n \"from\": from_task,\n \"to\": to_task,\n \"from_num\": int(re.search(r'(\\d+)', from_task).group(1)) if from_task else 0,\n \"to_num\": int(re.search(r'(\\d+)', to_task).group(1)) if to_task else 999,\n \"total_in_range\": len(filtered_tasks),\n },\n \"default_agent\": agent,\n \"tasks\": filtered_tasks,\n \"pending\": pending,\n \"done\": done,\n \"optional\": [],\n \"superseded\": [],\n \"learnings\": [],\n \"state\": {\n \"step\": \"choose_task\",\n \"current_task\": None,\n \"current_task_file\": None,\n \"current_task_lang\": None,\n \"iteration\": 0,\n \"retry_count\": 0,\n \"review_file_retry\": 0, # separate counter for file validation failures\n \"review_file_error\": None, # last file validation error message\n \"last_updated\": datetime.now().isoformat(),\n \"error\": None,\n \"range_progress\": {\n \"done_in_range\": len(done),\n \"total_in_range\": len(filtered_tasks),\n }\n }\n }\n\n save_fix_plan(spec_path, fix_plan)\n\n agent_config = SUPPORTED_AGENTS[agent]\n\n print(f\"✅ fix_plan.json created\")\n print(f\" Tasks in range: {len(filtered_tasks)}\")\n print(f\" Pending: {len(pending)}\")\n print(f\" Done: {len(done)}\")\n print(f\" Default agent: {agent_config['name']}\")\n if from_task or to_task:\n print(f\" Range: {from_task or 'START'} → {to_task or 'END'}\")\n\n print(f\"\\n📋 Initial state: {fix_plan['state']['step']}\")\n print(f\"\\n➡️ Next: Run loop to start processing\")\n ralph_cmd = format_command(agent_config, \"ralph_loop\", action=\"loop\", spec=spec_path)\n print(f\" {ralph_cmd}\")\n\n\ndef action_loop(spec_path: str, args_agent: str = None, no_commit: bool = False):\n \"\"\"Executes one step of the state machine\"\"\"\n fix_plan = load_fix_plan(spec_path)\n state = fix_plan[\"state\"]\n step = state[\"step\"]\n\n agent_config = get_agent_config(fix_plan, args_agent)\n\n print(f\"🔄 Ralph Loop | Iteration {state['iteration']} | Step: {step}\")\n\n # Handle each step\n if step == \"init\":\n handle_init(spec_path, fix_plan)\n\n elif step == \"choose_task\":\n handle_choose_task(spec_path, fix_plan, agent_config)\n\n elif step == \"implementation\":\n handle_implementation(spec_path, fix_plan, agent_config)\n\n elif step == \"review\":\n handle_review(spec_path, fix_plan, agent_config)\n\n elif step == \"fix\":\n handle_fix(spec_path, fix_plan, agent_config)\n\n elif step == \"cleanup\":\n handle_cleanup(spec_path, fix_plan, agent_config)\n\n elif step == \"sync\":\n handle_sync(spec_path, fix_plan, agent_config)\n\n elif step == \"update_done\":\n handle_update_done(spec_path, fix_plan, agent_config, no_commit)\n\n elif step == \"complete\":\n handle_complete(fix_plan)\n\n elif step == \"failed\":\n handle_failed(fix_plan)\n\n else:\n print(f\"❌ Unknown state: {step}\")\n print(f\" Valid states: init, choose_task, implementation, review, fix, cleanup, sync, update_done, complete, failed\")\n \n # Provide helpful guidance for common errors\n if \"-\" in step:\n suggested = step.replace(\"-\", \"_\")\n print(f\"\\n💡 HINT: The state '{step}' uses a dash (-) instead of an underscore (_).\")\n print(f\" Did you mean: '{suggested}'?\")\n print(f\"\\n To fix this, run:\")\n print(f\" sed -i 's/\\\"{step}\\\"/\\\"{suggested}\\\"/g' {get_fix_plan_path(spec_path)}\")\n \n sys.exit(1)\n\n\ndef handle_init(spec_path: str, fix_plan: dict):\n \"\"\"Handle init step - transition to choose_task\"\"\"\n print(\"→ Initializing...\")\n fix_plan[\"state\"][\"step\"] = \"choose_task\"\n save_fix_plan(spec_path, fix_plan)\n print(\"→ Initialized | Next: choose_task\")\n\n\ndef handle_choose_task(spec_path: str, fix_plan: dict, agent_config: dict):\n \"\"\"Handle choose_task step - select next task\"\"\"\n next_task = get_next_pending_task(fix_plan)\n\n if not next_task:\n # Check for misalignment: pending list not empty but no pending tasks found\n pending_list = fix_plan.get(\"pending\", [])\n if pending_list:\n # Misalignment detected: pending list has items but task statuses don't match\n print(\"⚠️ WARNING: Task status misalignment detected!\")\n print(f\" Pending list has {len(pending_list)} task(s), but no pending tasks found in task list.\")\n \n # Get the first task from pending list\n first_pending_id = pending_list[0]\n tasks = fix_plan.get(\"tasks\", [])\n \n for task in tasks:\n if task[\"id\"] == first_pending_id:\n next_task = task\n # Fix the status if it's incorrectly marked as completed\n if task[\"status\"] == \"completed\":\n task[\"status\"] = \"pending\"\n print(f\" Fixed {first_pending_id}: status 'completed' → 'pending'\")\n # Save immediately to persist the fix\n save_fix_plan(spec_path, fix_plan)\n print(f\" Using first pending task: {first_pending_id}\")\n break\n \n if not next_task:\n print(f\" ⚠️ Task {first_pending_id} not found in task list!\")\n \n if not next_task:\n fix_plan[\"state\"][\"step\"] = \"complete\"\n save_fix_plan(spec_path, fix_plan)\n print(\"→ No more tasks in range\")\n print(\"═══════════════════════════════════════════════════════\")\n print(\"Ralph Loop COMPLETE\")\n print(\"═══════════════════════════════════════════════════════\")\n return\n\n task_id = next_task[\"id\"]\n task_title = next_task.get(\"title\", \"\")\n\n fix_plan[\"state\"][\"current_task\"] = task_id\n fix_plan[\"state\"][\"current_task_file\"] = next_task.get(\"file\", \"\")\n fix_plan[\"state\"][\"current_task_lang\"] = next_task.get(\"lang\", \"\")\n fix_plan[\"state\"][\"step\"] = \"implementation\"\n fix_plan[\"state\"][\"retry_count\"] = 0\n\n save_fix_plan(spec_path, fix_plan)\n\n print(f\"→ Selected: {task_id}\")\n if task_title:\n print(f\" Title: {task_title}\")\n deps = next_task.get(\"dependencies\", [])\n if deps:\n print(f\" Dependencies: {', '.join(deps)} ✓\")\n print(f\"→ Next: implementation\")\n print(\"\")\n print(\"Execute:\")\n cmd = format_command(agent_config, \"task_impl\", spec=spec_path, task=task_id)\n print(f\" {cmd}\")\n\n\ndef handle_implementation(spec_path: str, fix_plan: dict, agent_config: dict):\n \"\"\"Handle implementation step\"\"\"\n current_task = fix_plan[\"state\"].get(\"current_task\")\n task_file = fix_plan[\"state\"].get(\"current_task_file\", \"\")\n task_lang = fix_plan[\"state\"].get(\"current_task_lang\", \"\")\n\n if not current_task:\n fix_plan[\"state\"][\"step\"] = \"choose_task\"\n save_fix_plan(spec_path, fix_plan)\n print(\"⚠️ No current task, returning to choose_task\")\n return\n\n # Check for task-specific agent\n task_agent = None\n for task in fix_plan.get(\"tasks\", []):\n if task[\"id\"] == current_task and task.get(\"agent\"):\n task_agent = SUPPORTED_AGENTS.get(task[\"agent\"])\n break\n\n if task_agent:\n agent_config = task_agent\n print(f\"🤖 Using agent: {agent_config['name']}\")\n\n print(f\"→ Implementation: {current_task}\")\n print(\"\")\n print(\"Execute:\")\n cmd = format_command(agent_config, \"task_impl\", spec=spec_path, task=current_task)\n print(f\" {cmd}\")\n print(\"\")\n print(\"After execution, update state:\")\n print(\" python3 ralph_loop.py --action=loop --spec=\" + spec_path)\n\n\ndef handle_review(spec_path: str, fix_plan: dict, agent_config: dict):\n \"\"\"Handle review step\"\"\"\n current_task = fix_plan[\"state\"].get(\"current_task\")\n task_file = fix_plan[\"state\"].get(\"current_task_file\", \"\")\n task_lang = fix_plan[\"state\"].get(\"current_task_lang\", \"\")\n\n if not current_task:\n fix_plan[\"state\"][\"step\"] = \"choose_task\"\n save_fix_plan(spec_path, fix_plan)\n return\n\n retry_count = fix_plan[\"state\"].get(\"retry_count\", 0)\n review_file_error = fix_plan[\"state\"].get(\"review_file_error\")\n review_file_retry = fix_plan[\"state\"].get(\"review_file_retry\", 0)\n\n # Check for task-specific agent\n task_agent = None\n for task in fix_plan.get(\"tasks\", []):\n if task[\"id\"] == current_task and task.get(\"agent\"):\n task_agent = SUPPORTED_AGENTS.get(task[\"agent\"])\n break\n\n if task_agent:\n agent_config = task_agent\n\n print(f\"→ Review: {current_task} | Retry: {retry_count}/3\")\n\n # If there was a review file error from the previous --action=next, surface it to the agent\n if review_file_error:\n print(f\"\")\n print(f\"⚠️ REVIEW FILE ERROR (attempt {review_file_retry}/3):\")\n print(f\" {review_file_error}\")\n print(f\"\")\n print(f\" The review file MUST be created with this exact frontmatter structure:\")\n print(f\" ---\")\n print(f\" review_status: PASSED # or FAILED\")\n print(f\" critical_issues: 0 # required if FAILED\")\n print(f\" major_issues: 0 # required if FAILED\")\n print(f\" ---\")\n print(f\"\")\n print(f\" Expected file path: tasks/{current_task}--review.md\")\n print(f\"\")\n\n print(\"\")\n print(\"Execute:\")\n cmd = format_command(agent_config, \"task_review\", spec=spec_path, task=current_task)\n print(f\" {cmd}\")\n print(\"\")\n print(\"Review the generated review report, then update state:\")\n print(\" python3 ralph_loop.py --action=loop --spec=\" + spec_path)\n\n\ndef handle_fix(spec_path: str, fix_plan: dict, agent_config: dict):\n \"\"\"Handle fix step - apply fixes from review\"\"\"\n current_task = fix_plan[\"state\"].get(\"current_task\")\n\n if not current_task:\n fix_plan[\"state\"][\"step\"] = \"choose_task\"\n save_fix_plan(spec_path, fix_plan)\n return\n\n # Check for task-specific agent\n task_agent = None\n for task in fix_plan.get(\"tasks\", []):\n if task[\"id\"] == current_task and task.get(\"agent\"):\n task_agent = SUPPORTED_AGENTS.get(task[\"agent\"])\n break\n\n if task_agent:\n agent_config = task_agent\n\n print(f\"→ Fix: {current_task}\")\n print(\"\")\n print(\"Steps:\")\n print(f\" 1. Read review report: {current_task}--review.md\")\n print(f\" 2. Apply fixes\")\n print(f\" 3. Update state:\")\n print(f\" python3 ralph_loop.py --action=loop --spec=\" + spec_path)\n print(\"\")\n print(\"Or execute directly:\")\n cmd = format_command(agent_config, \"task_impl\", spec=spec_path, task=current_task)\n print(f\" {cmd}\")\n\n\ndef handle_cleanup(spec_path: str, fix_plan: dict, agent_config: dict):\n \"\"\"Handle cleanup step\"\"\"\n current_task = fix_plan[\"state\"].get(\"current_task\")\n task_file = fix_plan[\"state\"].get(\"current_task_file\", \"\")\n task_lang = fix_plan[\"state\"].get(\"current_task_lang\", \"\")\n\n if not current_task:\n fix_plan[\"state\"][\"step\"] = \"choose_task\"\n save_fix_plan(spec_path, fix_plan)\n return\n\n # Check for task-specific agent\n task_agent = None\n for task in fix_plan.get(\"tasks\", []):\n if task[\"id\"] == current_task and task.get(\"agent\"):\n task_agent = SUPPORTED_AGENTS.get(task[\"agent\"])\n break\n\n if task_agent:\n agent_config = task_agent\n\n print(f\"→ Cleanup: {current_task}\")\n print(\"\")\n print(\"Execute:\")\n cmd = format_command(agent_config, \"code_cleanup\", spec=spec_path, task=current_task)\n print(f\" {cmd}\")\n print(\"\")\n print(\"After cleanup, update state:\")\n print(\" python3 ralph_loop.py --action=loop --spec=\" + spec_path)\n\n\ndef handle_sync(spec_path: str, fix_plan: dict, agent_config: dict):\n \"\"\"Handle sync step\"\"\"\n current_task = fix_plan[\"state\"].get(\"current_task\")\n\n if not current_task:\n fix_plan[\"state\"][\"step\"] = \"choose_task\"\n save_fix_plan(spec_path, fix_plan)\n return\n\n # Check for task-specific agent\n task_agent = None\n for task in fix_plan.get(\"tasks\", []):\n if task[\"id\"] == current_task and task.get(\"agent\"):\n task_agent = SUPPORTED_AGENTS.get(task[\"agent\"])\n break\n\n if task_agent:\n agent_config = task_agent\n\n print(f\"→ Sync: {current_task}\")\n print(\"\")\n print(\"Execute:\")\n cmd = format_command(agent_config, \"spec_sync\", spec=spec_path, task=current_task)\n print(f\" {cmd}\")\n print(\"\")\n print(\"After sync, update state:\")\n print(\" python3 ralph_loop.py --action=loop --spec=\" + spec_path)\n\n\ndef handle_update_done(spec_path: str, fix_plan: dict, agent_config: dict, no_commit: bool):\n \"\"\"Handle update_done step - mark task complete and commit\"\"\"\n current_task = fix_plan[\"state\"].get(\"current_task\")\n\n if not current_task:\n fix_plan[\"state\"][\"step\"] = \"choose_task\"\n save_fix_plan(spec_path, fix_plan)\n return\n\n # Find task details\n task_title = \"\"\n for task in fix_plan.get(\"tasks\", []):\n if task[\"id\"] == current_task:\n task[\"status\"] = \"completed\"\n task_title = task.get(\"title\", \"\")\n break\n\n # Update lists\n if \"pending\" not in fix_plan:\n fix_plan[\"pending\"] = []\n if \"done\" not in fix_plan:\n fix_plan[\"done\"] = []\n if current_task in fix_plan[\"pending\"]:\n fix_plan[\"pending\"].remove(current_task)\n if current_task not in fix_plan[\"done\"]:\n fix_plan[\"done\"].append(current_task)\n\n # Update state\n iteration = fix_plan[\"state\"].get(\"iteration\", 0) + 1\n fix_plan[\"state\"][\"iteration\"] = iteration\n fix_plan[\"state\"][\"current_task\"] = None\n fix_plan[\"state\"][\"current_task_file\"] = None\n fix_plan[\"state\"][\"current_task_lang\"] = None\n fix_plan[\"state\"][\"step\"] = \"choose_task\"\n fix_plan[\"state\"][\"retry_count\"] = 0\n\n # Update progress\n done_count = len(fix_plan.get(\"done\", []))\n total_count = fix_plan.get(\"task_range\", {}).get(\"total_in_range\", 0)\n if \"range_progress\" not in fix_plan[\"state\"]:\n fix_plan[\"state\"][\"range_progress\"] = {}\n fix_plan[\"state\"][\"range_progress\"][\"done_in_range\"] = done_count\n\n save_fix_plan(spec_path, fix_plan)\n\n print(f\"→ Done: {current_task}\")\n if task_title:\n print(f\" {task_title}\")\n\n # Git commit\n if not no_commit:\n print(\"→ Committing changes...\")\n run_git_commit(spec_path, current_task, task_title, iteration)\n\n progress_pct = (done_count / total_count * 100) if total_count > 0 else 0\n print(f\"→ Progress: {done_count}/{total_count} in range ({progress_pct:.0f}%)\")\n print(f\"→ Iteration: {iteration}\")\n print(f\"→ Next: choose_task\")\n\n\ndef handle_complete(fix_plan: dict):\n \"\"\"Handle complete state\"\"\"\n done_count = len(fix_plan.get(\"done\", []))\n total_count = fix_plan.get(\"task_range\", {}).get(\"total_in_range\", 0)\n iteration = fix_plan[\"state\"].get(\"iteration\", 0)\n\n print(\"═══════════════════════════════════════════════════════\")\n print(\"Ralph Loop COMPLETE\")\n print(\"═══════════════════════════════════════════════════════\")\n print(f\"Task Range: {fix_plan.get('task_range', {}).get('from', 'START')} → {fix_plan.get('task_range', {}).get('to', 'END')}\")\n print(f\"Tasks Completed: {done_count}/{total_count}\")\n print(f\"Total Iterations: {iteration}\")\n print(\"\")\n print(\"All tasks in range implemented and verified.\")\n print(\"Run --action=start with a new range to continue.\")\n\n\ndef handle_failed(fix_plan: dict):\n \"\"\"Handle failed state\"\"\"\n current_task = fix_plan[\"state\"].get(\"current_task\", \"N/A\")\n error = fix_plan[\"state\"].get(\"error\", \"Unknown error\")\n retry_count = fix_plan[\"state\"].get(\"retry_count\", 0)\n\n print(\"═══════════════════════════════════════════════════════\")\n print(\"Ralph Loop FAILED\")\n print(\"═══════════════════════════════════════════════════════\")\n print(f\"Task: {current_task}\")\n print(f\"Error: {error}\")\n print(f\"Retry count: {retry_count}/3\")\n print(\"\")\n print(\"Fix the issues manually, then resume:\")\n print(\" python3 ralph_loop.py --action=loop --spec=\" + fix_plan.get(\"spec_folder\", \"\"))\n\n\ndef action_status(spec_path: str):\n \"\"\"Shows current status\"\"\"\n fix_plan = load_fix_plan(spec_path)\n state = fix_plan[\"state\"]\n\n print(\"📊 Ralph Loop Status\")\n print(\"═══════════════════════════════════════════════════════\")\n print(f\"Spec: {spec_path}\")\n print(f\"Step: {state['step']}\")\n print(f\"Iteration: {state['iteration']}\")\n\n if fix_plan.get('default_agent'):\n agent_name = SUPPORTED_AGENTS.get(fix_plan['default_agent'], {}).get('name', fix_plan['default_agent'])\n print(f\"Default Agent: {agent_name}\")\n\n if state.get('current_task'):\n print(f\"Current Task: {state['current_task']}\")\n if state.get('current_task_lang'):\n print(f\"Language: {state['current_task_lang']}\")\n if state.get('retry_count', 0) > 0:\n print(f\"Retry: {state['retry_count']}/3\")\n\n done_count = len(fix_plan.get(\"done\", []))\n pending_count = len(fix_plan.get(\"pending\", []))\n total_count = fix_plan.get(\"task_range\", {}).get(\"total_in_range\", 0)\n\n print(\"\")\n print(f\"Progress: {done_count}/{total_count} done, {pending_count} pending\")\n\n if fix_plan.get(\"tasks\"):\n print(\"\")\n print(\"Tasks:\")\n for task in fix_plan[\"tasks\"]:\n status = task.get(\"status\", \"pending\")\n icon = {\n \"pending\": \"⏳\",\n \"in_progress\": \"🔄\",\n \"done\": \"✅\",\n \"completed\": \"✅\",\n \"failed\": \"❌\"\n }.get(status, \"❓\")\n agent_info = f\" [{task.get('agent', '')}]\" if task.get(\"agent\") else \"\"\n print(f\" {icon} {task['id']}{agent_info}: {task.get('title', task.get('description', ''))[:40]}\")\n\n\ndef action_resume(spec_path: str, args_agent: str = None, no_commit: bool = False):\n \"\"\"Resumes the loop from current state\"\"\"\n fix_plan = load_fix_plan(spec_path)\n state = fix_plan[\"state\"]\n\n print(f\"▶️ Ralph Loop | Resume from {state['step']}\")\n\n if state[\"step\"] in [\"complete\", \"failed\"]:\n print(f\" Final state: {state['step']}\")\n print(\" To restart, use --action=start\")\n return\n\n # Continue with loop\n action_loop(spec_path, args_agent, no_commit)\n\n\ndef validate_review_file(spec_path: str, task_id: str) -> tuple[bool, str]:\n \"\"\"\n Structural pre-validation of the review file before semantic check.\n Verifies: file exists, has frontmatter delimiters, has valid review_status field.\n Returns: (True, \"\") if valid, (False, \"\u003cerror message>\") if not.\n \"\"\"\n spec_dir = Path(spec_path)\n review_file = spec_dir / \"tasks\" / f\"{task_id}--review.md\"\n\n if not review_file.exists():\n return False, (\n f\"Review file not found: tasks/{task_id}--review.md. \"\n f\"The agent must create this file with a valid YAML frontmatter.\"\n )\n\n try:\n with open(review_file, 'r', encoding='utf-8') as f:\n content = f.read()\n except Exception as e:\n return False, f\"Cannot read review file: {e}\"\n\n frontmatter_match = re.match(r'^---\\s*\\n(.*?)\\n---\\s*\\n', content, re.DOTALL)\n if not frontmatter_match:\n return False, (\n f\"Review file tasks/{task_id}--review.md has no valid YAML frontmatter. \"\n f\"The file must start with '---' delimiters containing the frontmatter block.\"\n )\n\n frontmatter = frontmatter_match.group(1)\n status_match = re.search(r'^review_status:\\s*(\\w+)', frontmatter, re.MULTILINE | re.IGNORECASE)\n if not status_match:\n return False, (\n f\"Review file tasks/{task_id}--review.md is missing the 'review_status' field. \"\n f\"Required frontmatter: review_status: PASSED|FAILED\"\n )\n\n review_status = status_match.group(1).upper()\n if review_status not in {\"PASSED\", \"FAILED\"}:\n return False, (\n f\"Invalid review_status value '{review_status}' in tasks/{task_id}--review.md. \"\n f\"Allowed values: PASSED or FAILED.\"\n )\n\n return True, \"\"\n\n\ndef check_review_result(spec_path: str, task_id: str) -> tuple[bool, str]:\n \"\"\"\n Check the review report to determine if review passed or failed.\n \n First tries to parse the YAML frontmatter for structured review_status field.\n Falls back to text pattern matching if YAML frontmatter is not present.\n \n Returns:\n tuple: (passed: bool, reason: str)\n - passed: True if review passed, False if failed\n - reason: Description of the result\n \"\"\"\n spec_dir = Path(spec_path)\n \n # Look for review report file: tasks/TASK-XXX--review.md\n review_file = spec_dir / \"tasks\" / f\"{task_id}--review.md\"\n \n if not review_file.exists():\n # No review report found - assume not implemented\n return False, f\"Review report not found: {review_file}\"\n \n try:\n with open(review_file, 'r', encoding='utf-8') as f:\n content = f.read()\n \n content_lower = content.lower()\n \n # === METHOD 1: Parse YAML frontmatter (preferred, structured approach) ===\n frontmatter_match = re.match(r'^---\\s*\\n(.*?)\\n---\\s*\\n', content, re.DOTALL)\n if frontmatter_match:\n frontmatter = frontmatter_match.group(1)\n \n # Extract review_status from frontmatter\n status_match = re.search(r'^review_status:\\s*(\\w+)', frontmatter, re.MULTILINE | re.IGNORECASE)\n if status_match:\n review_status = status_match.group(1).upper()\n \n if review_status == \"PASSED\":\n return True, \"Review status: PASSED (from YAML frontmatter)\"\n elif review_status == \"FAILED\":\n # Check for critical/major issues count\n critical_match = re.search(r'^critical_issues:\\s*(\\d+)', frontmatter, re.MULTILINE | re.IGNORECASE)\n major_match = re.search(r'^major_issues:\\s*(\\d+)', frontmatter, re.MULTILINE | re.IGNORECASE)\n \n critical_count = int(critical_match.group(1)) if critical_match else 0\n major_count = int(major_match.group(1)) if major_match else 0\n \n return False, f\"Review status: FAILED with {critical_count} critical, {major_count} major issues\"\n elif review_status == \"PARTIAL\":\n return False, \"Review status: PARTIAL (needs more work)\"\n else:\n return False, f\"Unknown review_status: {review_status}\"\n \n # === METHOD 2: Fallback to text pattern matching (for legacy reviews) ===\n # Check for common failure indicators in review reports (English + Italian)\n failure_indicators = [\n # English\n \"❌ not started\",\n \"❌ not implemented\",\n \"❌ fail\",\n \"❌ failed\",\n \"❌ missing\",\n \"implementation status: ❌\",\n \"implementation: not started\",\n \"implementation: not implemented\",\n \"acceptance criteria: 0/\",\n \"acceptance criteria: 1/\",\n \"acceptance criteria: 2/\",\n \"acceptance criteria: 3/\",\n \"acceptance criteria: 4/\",\n \"passed: 0/\",\n \"passed: 1/\",\n \"passed: 2/\",\n \"passed: 3/\",\n \"passed: 4/\",\n # Italian\n \"❌ non implementato\",\n \"❌ non iniziato\",\n \"❌ fallito\",\n \"❌ mancante\",\n \"stato implementazione: ❌\",\n \"implementazione: non iniziata\",\n \"implementazione: non implementata\",\n \"criteri di accettazione: 0/\",\n \"criteri di accettazione: 1/\",\n \"criteri di accettazione: 2/\",\n \"criteri di accettazione: 3/\",\n \"criteri di accettazione: 4/\",\n \"esito: ❌\",\n \"non conforme\",\n \"non soddisfatti\",\n \"task non implementato\",\n \"needs revision\",\n \"need revision\",\n \"deve essere risolto\",\n \"correggere\",\n ]\n \n success_indicators = [\n # English\n \"✅ implemented\",\n \"✅ complete\",\n \"✅ pass\",\n \"✅ passed\",\n \"implementation status: ✅\",\n \"implementation: complete\",\n \"implementation: done\",\n \"acceptance criteria: 5/5\",\n \"acceptance criteria: 4/4\",\n \"acceptance criteria: 3/3\",\n \"acceptance criteria: 100%\",\n \"passed: 5/5\",\n \"passed: 4/4\",\n \"passed: 3/3\",\n \"all criteria met\",\n \"all tests passed\",\n \"no issues found\",\n \"review passed\",\n # Italian\n \"✅ implementato\",\n \"✅ completato\",\n \"✅ passato\",\n \"✅ superato\",\n \"stato implementazione: ✅\",\n \"implementazione: completata\",\n \"implementazione: completato\",\n \"criteri di accettazione: 5/5\",\n \"criteri di accettazione: 4/4\",\n \"criteri di accettazione: 3/3\",\n \"criteri di accettazione: 100%\",\n \"criteri: 5/5\",\n \"criteri: 4/4\",\n \"criteri: 3/3\",\n \"tutti i criteri soddisfatti\",\n \"tutti i test passano\",\n \"nessun problema trovato\",\n \"nessun issue\",\n \"review superata\",\n \"esito: ✅\",\n ]\n \n # Check for critical issues that should fail the review regardless of other indicators\n critical_issue_patterns = [\n r\"critical.*issue\",\n r\"issue.*critical\",\n r\"problema.*critico\",\n r\"critico\",\n r\"blocking issue\",\n r\"must fix\",\n r\"deve essere corretto\",\n r\"deve essere risolto\",\n r\"blocking\",\n r\"needs fix\",\n r\"needs revision\",\n r\"non conforme\",\n ]\n \n for pattern in critical_issue_patterns:\n if re.search(pattern, content_lower):\n return False, f\"Found critical issue indicator: '{pattern}'\"\n \n # Check for explicit failure indicators FIRST (take precedence over success)\n for indicator in failure_indicators:\n if indicator.lower() in content_lower:\n return False, f\"Found failure indicator: '{indicator}'\"\n \n # Check for explicit success indicators\n for indicator in success_indicators:\n if indicator.lower() in content_lower:\n return True, f\"Found success indicator: '{indicator}'\"\n \n # Check acceptance criteria percentage\n # Pattern: \"X/Y soddisfatte\" or \"X/Y satisfied\"\n criteria_match = re.search(r'(\\d+)\\s*/\\s*(\\d+)\\s*(?:soddisfatte|satisfied|pass)', content_lower)\n if criteria_match:\n passed = int(criteria_match.group(1))\n total = int(criteria_match.group(2))\n if passed >= total:\n return True, f\"All acceptance criteria met: {passed}/{total}\"\n else:\n return False, f\"Acceptance criteria not fully met: {passed}/{total}\"\n \n # Check for \"Not Started\" or \"Not Implemented\"\n if \"not started\" in content_lower or \"not implemented\" in content_lower:\n return False, \"Implementation not started\"\n \n # Default: FAIL rather than pass (safer - require explicit success indicators)\n return False, \"Review report exists but no clear success indicators found\"\n \n except Exception as e:\n return False, f\"Error reading review report: {e}\"\n\n\ndef action_next(spec_path: str, args_agent: str = None, no_commit: bool = False):\n \"\"\"Manually advance to next step (checks review results before advancing)\"\"\"\n fix_plan = load_fix_plan(spec_path)\n state = fix_plan[\"state\"]\n current_step = state[\"step\"]\n\n print(f\"⏭️ Ralph Loop | Advancing from '{current_step}' to next step\")\n\n if current_step in [\"complete\", \"failed\"]:\n print(f\" Final state: {current_step}\")\n print(\" Cannot advance further.\")\n return\n\n # Define standard step transitions\n step_transitions = {\n \"init\": \"choose_task\",\n \"choose_task\": \"implementation\",\n \"implementation\": \"review\",\n \"fix\": \"review\",\n \"cleanup\": \"sync\",\n \"sync\": \"update_done\",\n \"update_done\": \"choose_task\",\n }\n \n # Special handling for review step - check results before advancing\n if current_step == \"review\":\n current_task = state.get(\"current_task\")\n retry_count = state.get(\"retry_count\", 0)\n max_retries = 3\n \n if not current_task:\n print(\" ⚠️ No current task, returning to choose_task\")\n state[\"step\"] = \"choose_task\"\n save_fix_plan(spec_path, fix_plan)\n return\n \n max_file_retries = 3\n\n # === PRE-VALIDATION: structural check before semantic review ===\n file_valid, file_error = validate_review_file(spec_path, current_task)\n\n if not file_valid:\n # Do NOT increment retry_count — this is not a review failure\n review_file_retry = state.get(\"review_file_retry\", 0) + 1\n state[\"review_file_retry\"] = review_file_retry\n state[\"review_file_error\"] = file_error\n\n print(f\" ⚠️ Review file invalid (attempt {review_file_retry}/{max_file_retries}): {file_error}\")\n\n if review_file_retry >= max_file_retries:\n print(f\" ❌ Max file retries ({max_file_retries}) exceeded - review file never created correctly\")\n state[\"step\"] = \"failed\"\n state[\"error\"] = f\"Review file validation failed after {max_file_retries} attempts: {file_error}\"\n else:\n print(f\" 🔄 Staying on 'review' — agent must create/fix the review file\")\n # state[\"step\"] remains \"review\" — no assignment needed\n\n save_fix_plan(spec_path, fix_plan)\n print(f\"\")\n print(\"Run loop to see next command:\")\n print(f\" python3 ralph_loop.py --action=loop --spec={spec_path}\")\n return\n\n # File is structurally valid — reset file retry counters\n state[\"review_file_retry\"] = 0\n state[\"review_file_error\"] = None\n\n # === SEMANTIC CHECK: check review content ===\n review_passed, reason = check_review_result(spec_path, current_task)\n\n if review_passed:\n print(f\" ✅ Review passed: {reason}\")\n print(f\" Advanced: review → cleanup\")\n state[\"step\"] = \"cleanup\"\n state[\"retry_count\"] = 0\n save_fix_plan(spec_path, fix_plan)\n else:\n print(f\" ❌ Review failed: {reason}\")\n retry_count += 1\n state[\"retry_count\"] = retry_count\n\n if retry_count >= max_retries:\n print(f\" ❌ Max retries ({max_retries}) exceeded\")\n print(f\" Advanced: review → failed\")\n state[\"step\"] = \"failed\"\n state[\"error\"] = f\"Review failed after {max_retries} retries: {reason}\"\n save_fix_plan(spec_path, fix_plan)\n else:\n print(f\" 🔄 Retry {retry_count}/{max_retries}\")\n print(f\" Advanced: review → fix\")\n state[\"step\"] = \"fix\"\n save_fix_plan(spec_path, fix_plan)\n\n print(f\"\")\n print(\"Run loop to see next command:\")\n print(f\" python3 ralph_loop.py --action=loop --spec={spec_path}\")\n return\n\n # Standard transition for other steps\n next_step = step_transitions.get(current_step)\n\n if not next_step:\n print(f\" Unknown step: {current_step}\")\n return\n\n # Special handling for update_done - need to mark task complete\n if current_step == \"update_done\":\n handle_update_done(spec_path, fix_plan, get_agent_config(fix_plan, args_agent), no_commit)\n return\n\n # Update state\n state[\"step\"] = next_step\n save_fix_plan(spec_path, fix_plan)\n\n print(f\" Advanced: {current_step} → {next_step}\")\n print(f\"\")\n print(\"Run loop to see next command:\")\n print(f\" python3 ralph_loop.py --action=loop --spec={spec_path}\")\n\n\ndef main():\n args = parse_args()\n\n # Validate spec folder structure\n if not validate_spec_path(args.spec):\n print(f\"❌ Invalid spec folder: {args.spec}\")\n print(f\" Expected format: docs/specs/[ID-feature]/ or [ID-feature]/\")\n print(f\" Folder must exist and contain spec files or tasks/ directory\")\n sys.exit(1)\n\n # Ensure _ralph_loop directory exists for clean state\n spec_dir = Path(args.spec).resolve()\n ralph_dir = spec_dir / \"_ralph_loop\"\n if not ralph_dir.exists():\n ralph_dir.mkdir(parents=True, exist_ok=True)\n\n if args.action == \"start\":\n action_start(args.spec, args.from_task, args.to_task, args.agent)\n elif args.action == \"loop\":\n action_loop(args.spec, args.agent, args.no_commit)\n elif args.action == \"status\":\n action_status(args.spec)\n elif args.action == \"resume\":\n action_resume(args.spec, args.agent, args.no_commit)\n elif args.action == \"next\":\n action_next(args.spec, args.agent, args.no_commit)\n elif args.action == \"next\":\n action_next(args.spec, args.agent, args.no_commit)\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":53669,"content_sha256":"1ace0eb017758c8bfc0aa7e490a7eda6efda2c3a4040030665895ac7128b7abb"}],"content_json":{"type":"doc","content":[{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"⚠️ WARNING","type":"text","marks":[{"type":"strong"}]},{"text":": This skill was deprecated in favor of a new command ","type":"text"},{"text":"ralph-loop-v2","type":"text","marks":[{"type":"code_inline"}]},{"text":" that uses a Python orchestrator script. The old ","type":"text"},{"text":"/specs:ralph-loop","type":"text","marks":[{"type":"code_inline"}]},{"text":" command will be removed soon. Please migrate to the new command.","type":"text"}]}]},{"type":"heading","attrs":{"level":1},"content":[{"text":"Ralph Loop — Python Orchestrator","type":"text"}]},{"type":"paragraph","content":[{"text":"⚠️ ","type":"text"},{"text":"IMPORTANT","type":"text","marks":[{"type":"strong"}]},{"text":": This skill uses a Python orchestrator script. Do NOT execute arbitrary bash commands. Use ","type":"text"},{"text":"Bash","type":"text","marks":[{"type":"code_inline"}]},{"text":" ONLY to run ","type":"text"},{"text":"ralph_loop.py","type":"text","marks":[{"type":"code_inline"}]},{"text":". All task commands (like ","type":"text"},{"text":"/developer-kit-specs:specs.task-implementation","type":"text","marks":[{"type":"code_inline"}]},{"text":") are shown to the user to execute manually.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"The Ralph Loop applies Geoffrey Huntley's \"Ralph Wiggum as a Software Engineer\" technique to specification-driven development. It uses a ","type":"text"},{"text":"Python orchestrator script","type":"text","marks":[{"type":"strong"}]},{"text":" that manages a state machine: one invocation = one step, state persisted in ","type":"text"},{"text":"fix_plan.json","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Key insight","type":"text","marks":[{"type":"strong"}]},{"text":": Implementing + reviewing + syncing in one invocation explodes the context window. Solution: each loop iteration does exactly one step, saves state to ","type":"text"},{"text":"fix_plan.json","type":"text","marks":[{"type":"code_inline"}]},{"text":", and stops. The next iteration resumes from saved state.","type":"text"}]},{"type":"paragraph","content":[{"text":"Key improvement","type":"text","marks":[{"type":"strong"}]},{"text":": The Python script ","type":"text"},{"text":"ralph_loop.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" handles all state management, task selection, and command generation. It does NOT execute task commands directly — it shows you the correct command to execute in your CLI.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Use","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User runs ","type":"text"},{"text":"/loop","type":"text","marks":[{"type":"code_inline"}]},{"text":" command for recurring automation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User asks to \"automate implementation\" or \"run tasks in loop\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User wants to \"iterate through tasks step-by-step\" or \"run workflow automation\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User needs \"context window management\" across multiple SDD commands","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User wants to \"process task range\" from TASK-N to TASK-M","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User needs multi-agent support (different CLIs for different tasks)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Architecture","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐\n│ ralph_loop.py │────▶│ fix_plan.json │────▶│ User executes │\n│ (orchestrator)│ │ (state file) │ │ command in CLI │\n└─────────────────┘ └─────────────────┘ └─────────────────┘\n │ │\n │ ▼\n │ ┌─────────────────┐\n └──────────────────────────────────────│ Task result │\n │ (success/ │\n │ failure) │\n └─────────────────┘","type":"text"}]},{"type":"paragraph","content":[{"text":"One Step Flow:","type":"text","marks":[{"type":"strong"}]}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"ralph_loop.py --action=loop","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Script reads ","type":"text"},{"text":"fix_plan.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" and determines current step","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Script shows the command to execute (e.g., ","type":"text"},{"text":"/developer-kit-specs:specs.task-implementation","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User executes the command in their CLI","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User runs ","type":"text"},{"text":"ralph_loop.py --action=loop","type":"text","marks":[{"type":"code_inline"}]},{"text":" again","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Script updates state based on result and shows next command","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"State Machine","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"fix_plan.json state machine:\n┌─────────────────────────────────────────────────────────────┐\n│ state: \"init\" │\n│ → --action=start: Initialize fix_plan.json │\n│ → Load tasks from tasks/TASK-*.md files │\n│ → Apply task_range filter │\n│ │\n│ state: \"choose_task\" │\n│ → Pick next pending task (within range, deps satisfied)│\n│ → No tasks in range → state: \"complete\" │\n│ → Task found → state: \"implementation\" │\n│ │\n│ state: \"implementation\" │\n│ → Show /developer-kit-specs:specs.task-implementation command │\n│ → User executes, then runs loop again │\n│ → Next state: \"review\" │\n│ │\n│ state: \"review\" ││ → Show /developer-kit-specs:specs.task-implementation --action=cleanup command│},{find: │\n│ → User reviews results, then runs loop again │\n│ → Issues found → state: \"fix\" (retry ≤ 3) │\n│ → Clean → state: \"cleanup\" │\n│ │\n│ state: \"fix\" │\n│ → Show commands to fix issues │\n│ → User applies fixes, then runs loop again │\n│ → Next state: \"review\" │\n│ │\n│ state: \"cleanup\" │\n│ → Show /developer-kit-specs:specs.task-implementation --action=cleanup command│\n│ → Next state: \"sync\" │\n│ │\n│ state: \"sync\" │\n│ → Show /developer-kit-specs:specs.sync command │\n│ → Next state: \"update_done\" │\n│ │\n│ state: \"update_done\" │\n│ → Mark task done, commit git changes │\n│ → Re-evaluate dependencies │\n│ → state: \"choose_task\" │\n│ │\n│ state: \"complete\" | \"failed\" │\n│ → Print result, stop │\n└─────────────────────────────────────────────────────────────┘","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"File Location Requirements","type":"text"}]},{"type":"paragraph","content":[{"text":"⚠️ CRITICAL","type":"text","marks":[{"type":"strong"}]},{"text":": The ","type":"text"},{"text":"fix_plan.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" file MUST ALWAYS be located in:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"docs/specs/[ID-feature]/_ralph_loop/fix_plan.json","type":"text"}]},{"type":"paragraph","content":[{"text":"This is enforced by the script to prevent LLMs from creating files in wrong locations.","type":"text"}]},{"type":"paragraph","content":[{"text":"Migration","type":"text","marks":[{"type":"strong"}]},{"text":": If you have an old ","type":"text"},{"text":"fix_plan.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" in the root of your spec folder, the script will automatically migrate it to ","type":"text"},{"text":"_ralph_loop/","type":"text","marks":[{"type":"code_inline"}]},{"text":" on first run.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Instructions","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 1: Initialize","type":"text"}]},{"type":"paragraph","content":[{"text":"Run the Python script with ","type":"text"},{"text":"--action=start","type":"text","marks":[{"type":"code_inline"}]},{"text":" to scan task files and create ","type":"text"},{"text":"fix_plan.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" in the correct location:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 plugins/developer-kit-specs/skills/ralph-loop/scripts/ralph_loop.py \\\n --action=start \\\n --spec=docs/specs/001-feature/ \\\n --from-task=TASK-036 \\\n --to-task=TASK-041","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 2: Execute Loop Steps","type":"text"}]},{"type":"paragraph","content":[{"text":"Run the script with ","type":"text"},{"text":"--action=loop","type":"text","marks":[{"type":"code_inline"}]},{"text":" to get the current state and the command to execute:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 plugins/developer-kit-specs/skills/ralph-loop/scripts/ralph_loop.py \\\n --action=loop \\\n --spec=docs/specs/001-feature/","type":"text"}]},{"type":"paragraph","content":[{"text":"The script will show you the exact command to execute for the current step. Execute it in your CLI, then run the loop command again.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 3: Advance State (Manual)","type":"text"}]},{"type":"paragraph","content":[{"text":"After executing the shown command, manually advance to the next step:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 plugins/developer-kit-specs/skills/ralph-loop/scripts/ralph_loop.py \\\n --action=next \\\n --spec=docs/specs/001-feature/","type":"text"}]},{"type":"paragraph","content":[{"text":"This updates ","type":"text"},{"text":"fix_plan.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" to the next state (e.g., ","type":"text"},{"text":"implementation","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":"review","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 4: Monitor Progress","type":"text"}]},{"type":"paragraph","content":[{"text":"Check status anytime with ","type":"text"},{"text":"--action=status","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 plugins/developer-kit-specs/skills/ralph-loop/scripts/ralph_loop.py \\\n --action=status \\\n --spec=docs/specs/001-feature/","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Start","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1. Initialize","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 plugins/developer-kit-specs/skills/ralph-loop/scripts/ralph_loop.py \\\n --action=start \\\n --spec=docs/specs/001-feature/ \\\n --from-task=TASK-036 \\\n --to-task=TASK-041 \\\n --agent=claude","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2. Run Loop","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 plugins/developer-kit-specs/skills/ralph-loop/scripts/ralph_loop.py \\\n --action=loop \\\n --spec=docs/specs/001-feature/","type":"text"}]},{"type":"paragraph","content":[{"text":"The script will show you the command to execute. Run it, then run the loop again.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3. Check Status","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 plugins/developer-kit-specs/skills/ralph-loop/scripts/ralph_loop.py \\\n --action=status \\\n --spec=docs/specs/001-feature/","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Arguments","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Argument","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Description","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--action","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"start","type":"text","marks":[{"type":"code_inline"}]},{"text":" (init), ","type":"text"},{"text":"loop","type":"text","marks":[{"type":"code_inline"}]},{"text":" (run one step), ","type":"text"},{"text":"status","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"resume","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"next","type":"text","marks":[{"type":"code_inline"}]},{"text":" (advance step)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--spec","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Spec folder path (e.g. ","type":"text"},{"text":"docs/specs/001-feature/","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--from-task","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Start of task range (e.g. ","type":"text"},{"text":"TASK-036","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--to-task","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"End of task range (e.g. ","type":"text"},{"text":"TASK-041","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--agent","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Default agent: ","type":"text"},{"text":"claude","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"codex","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"copilot","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"kimi","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"gemini","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"glm4","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"minimax","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--no-commit","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Skip git commits (for testing)","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step Details","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 1: Initialize (","type":"text"},{"text":"--action=start","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"paragraph","content":[{"text":"The script:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Scans ","type":"text"},{"text":"tasks/TASK-*.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" files in the spec folder","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Extracts metadata from YAML frontmatter (id, title, status, lang, dependencies, agent)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Applies ","type":"text"},{"text":"--from-task","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"--to-task","type":"text","marks":[{"type":"code_inline"}]},{"text":" filters","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Creates ","type":"text"},{"text":"fix_plan.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" with full state","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 2: Choose Task (","type":"text"},{"text":"choose_task","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"paragraph","content":[{"text":"The script:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Finds pending tasks within range","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Checks dependencies are satisfied","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Selects next task","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Updates ","type":"text"},{"text":"fix_plan.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"current_task","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Shows command to execute","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3: Implementation (","type":"text"},{"text":"implementation","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"paragraph","content":[{"text":"The script shows:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"→ Implementation: TASK-037\n\nExecute:\n /developer-kit-specs:specs.task-implementation --task=TASK-037\n\nAfter execution, update state:\n python3 ralph_loop.py --action=loop --spec=docs/specs/001-feature/","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 4: Review (","type":"text"},{"text":"review","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"paragraph","content":[{"text":"The script shows:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"→ Review: TASK-037 | Retry: 0/3\n\nExecute:\n /developer-kit-specs:specs.task-review --task=TASK-037\n\nReview the generated review report, then update state:\n python3 ralph_loop.py --action=loop --spec=docs/specs/001-feature/","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 5: Fix (","type":"text"},{"text":"fix","type":"text","marks":[{"type":"code_inline"}]},{"text":") - If Review Failed","type":"text"}]},{"type":"paragraph","content":[{"text":"If issues found, script shows fix instructions. After fixes, user runs loop again.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 6: Cleanup (","type":"text"},{"text":"cleanup","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"paragraph","content":[{"text":"The script shows:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"→ Cleanup: TASK-037\n\nExecute:\n /developer-kit-specs:specs.task-implementation --task=TASK-037 --action=cleanup","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 7: Sync (","type":"text"},{"text":"sync","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"paragraph","content":[{"text":"The script shows:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"→ Sync: TASK-037\n\nExecute:\n /developer-kit-specs:specs.sync docs/specs/001-feature/ --after-task=TASK-037","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 8: Update Done (","type":"text"},{"text":"update_done","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"paragraph","content":[{"text":"The script:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Marks task as completed in ","type":"text"},{"text":"fix_plan.json","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Commits git changes (unless ","type":"text"},{"text":"--no-commit","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Updates iteration count","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Returns to ","type":"text"},{"text":"choose_task","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Multi-Agent Support","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Default Agent for All Tasks","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 ralph_loop.py --action=start --spec=... --agent=codex","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Per-Task Agent","type":"text"}]},{"type":"paragraph","content":[{"text":"Specify agent in task file YAML frontmatter:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"---\nid: TASK-036\ntitle: Refactor user service\nstatus: pending\nlang: java\nagent: codex\n---","type":"text"}]},{"type":"paragraph","content":[{"text":"Supported agents: ","type":"text"},{"text":"claude","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"codex","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"copilot","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"kimi","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"gemini","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"glm4","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"minimax","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Using with /loop (Claude Code)","type":"text"}]},{"type":"paragraph","content":[{"text":"For automatic scheduling every 5 minutes:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"/loop 5m python3 plugins/developer-kit-specs/skills/ralph-loop/scripts/ralph_loop.py \\\n --action=loop \\\n --spec=docs/specs/001-feature/","type":"text"}]},{"type":"paragraph","content":[{"text":"This will repeatedly run the loop, showing you the next command each time.","type":"text"}]},{"type":"paragraph","content":[{"text":"Note","type":"text","marks":[{"type":"strong"}]},{"text":": The Ralph Loop is now managed directly through the Python script. The deprecated ","type":"text"},{"text":"/developer-kit-specs:specs.ralph-loop","type":"text","marks":[{"type":"code_inline"}]},{"text":" command has been removed.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Task File Format","type":"text"}]},{"type":"paragraph","content":[{"text":"Each task should be a separate file: ","type":"text"},{"text":"tasks/TASK-XXX.md","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"---\nid: TASK-036\ntitle: Implement user authentication\nstatus: pending\nlang: java\ndependencies: []\ncomplexity: medium\nagent: claude\n---\n\n## Description\n\nImplement JWT-based authentication for the API.\n\n## Acceptance Criteria\n\n- [ ] Login endpoint returns JWT token\n- [ ] Token validation middleware\n- [ ] Refresh token mechanism","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Examples","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example 1: Basic Usage","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Initialize\npython3 ralph_loop.py --action=start \\\n --spec=docs/specs/001-feature/ \\\n --from-task=TASK-001 \\\n --to-task=TASK-005\n\n# Loop until complete\nwhile true; do\n python3 ralph_loop.py --action=loop --spec=docs/specs/001-feature/\n # Execute the shown command manually\n # Then continue loop\ndone","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example 2: With Claude Code /loop","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Start with specific range\n/loop 5m python3 plugins/developer-kit-specs/skills/ralph-loop/scripts/ralph_loop.py \\\n --action=loop \\\n --spec=docs/specs/002-tdd-command \\\n --from-task=TASK-001 \\\n --to-task=TASK-010","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example 3: Multi-Agent Setup","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Initialize with Claude as default\npython3 ralph_loop.py --action=start \\\n --spec=docs/specs/001-feature/ \\\n --agent=claude\n\n# Some tasks have \"agent: codex\" in their frontmatter\n# Those will show Codex-formatted commands","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Best Practices","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"One step per invocation","type":"text","marks":[{"type":"strong"}]},{"text":": Execute exactly one step, save state, stop","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Trust the state","type":"text","marks":[{"type":"strong"}]},{"text":": Read from ","type":"text"},{"text":"fix_plan.json","type":"text","marks":[{"type":"code_inline"}]},{"text":", write to ","type":"text"},{"text":"fix_plan.json","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No context accumulation","type":"text","marks":[{"type":"strong"}]},{"text":": State lives in the file, not in context","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Manual command execution","type":"text","marks":[{"type":"strong"}]},{"text":": The script shows commands; you execute them in your CLI","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Retry on review failure","type":"text","marks":[{"type":"strong"}]},{"text":": Max 3 retries before failing","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Range filtering","type":"text","marks":[{"type":"strong"}]},{"text":": Always filter by ","type":"text"},{"text":"task_range","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dependencies first","type":"text","marks":[{"type":"strong"}]},{"text":": Only pick tasks where all dependencies are done","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Git commits","type":"text","marks":[{"type":"strong"}]},{"text":": The script auto-commits after each completed task","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Constraints and Warnings","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Context explosion","type":"text","marks":[{"type":"strong"}]},{"text":": Do NOT implement + review + sync in one invocation — context will overflow","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Max retries","type":"text","marks":[{"type":"strong"}]},{"text":": Review failures retry up to 3 times, then fail","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Git state","type":"text","marks":[{"type":"strong"}]},{"text":": Ensure clean git state before starting","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Test infrastructure","type":"text","marks":[{"type":"strong"}]},{"text":": Loop requires tests to pass — without tests, backpressure is ineffective","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Strict state validation","type":"text","marks":[{"type":"strong"}]},{"text":": Valid ","type":"text"},{"text":"state.step","type":"text","marks":[{"type":"code_inline"}]},{"text":" values are ONLY: ","type":"text"},{"text":"init","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"choose_task","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"implementation","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"review","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"fix","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"cleanup","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"sync","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"update_done","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"complete","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"failed","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"NO automatic command execution","type":"text","marks":[{"type":"strong"}]},{"text":": The script shows commands but does NOT execute them — you must run them in your CLI","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Troubleshooting","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"\"fix_plan.json not found\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"--action=start","type":"text","marks":[{"type":"code_inline"}]},{"text":" first:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 ralph_loop.py --action=start --spec=docs/specs/001-feature/","type":"text"}]},{"type":"paragraph","content":[{"text":"The script will create ","type":"text"},{"text":"fix_plan.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" in the correct location:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"docs/specs/001-feature/_ralph_loop/fix_plan.json","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"\"fix_plan.json in wrong location\"","type":"text"}]},{"type":"paragraph","content":[{"text":"If you see a warning about the file being in the wrong location, the script will guide you through migration:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Manual migration if needed\nmkdir -p docs/specs/001-feature/_ralph_loop\nmv docs/specs/001-feature/fix_plan.json docs/specs/001-feature/_ralph_loop/fix_plan.json","type":"text"}]},{"type":"paragraph","content":[{"text":"The script will automatically migrate old files on first run.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"\"Invalid spec folder\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"--action=start","type":"text","marks":[{"type":"code_inline"}]},{"text":" first:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 ralph_loop.py --action=start --spec=docs/specs/001-feature/","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Task files not found","type":"text"}]},{"type":"paragraph","content":[{"text":"Ensure tasks are in ","type":"text"},{"text":"tasks/TASK-XXX.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" format with YAML frontmatter.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Wrong agent commands","type":"text"}]},{"type":"paragraph","content":[{"text":"Check ","type":"text"},{"text":"--agent","type":"text","marks":[{"type":"code_inline"}]},{"text":" parameter or task ","type":"text"},{"text":"agent:","type":"text","marks":[{"type":"code_inline"}]},{"text":" frontmatter field.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"References","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/state-machine.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Complete state machine documentation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/multi-cli-integration.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Multi-CLI setup guide","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/loop-prompt-template.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Prompt template for shell loops","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"ralph-loop","author":"@skillopedia","source":{"stars":264,"repo_name":"developer-kit","origin_url":"https://github.com/giuseppe-trisciuoglio/developer-kit/blob/HEAD/plugins/developer-kit-specs/skills/ralph-loop/SKILL.md","repo_owner":"giuseppe-trisciuoglio","body_sha256":"4f088e922d661a4192d9d167ad79e55867a556d09a9c5a81250ea6e6cce3bbc2","cluster_key":"b57b8d2b716884a89aba87aff22ab53da1b330c8d384edcfcbe6756fe2c8f6c0","clean_bundle":{"format":"clean-skill-bundle-v1","source":"giuseppe-trisciuoglio/developer-kit/plugins/developer-kit-specs/skills/ralph-loop/SKILL.md","attachments":[{"id":"3b8f3e0e-6056-5d3e-bc1a-08ca4fcd2a10","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3b8f3e0e-6056-5d3e-bc1a-08ca4fcd2a10/attachment.json","path":"references/examples/_ralph_loop/fix_plan.json","size":2045,"sha256":"2197f388a2754de2a0f4bbe5d575c86d8cda98837ae4edc16bde10dbe4eb9853","contentType":"application/json; charset=utf-8"},{"id":"58816374-8411-55fa-b56a-8c74be330b33","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/58816374-8411-55fa-b56a-8c74be330b33/attachment.md","path":"references/loop-prompt-template.md","size":6208,"sha256":"95e52f3dd40cb315a839d9313dc6c46260e8e430c3def191adf95766c7a9233c","contentType":"text/markdown; charset=utf-8"},{"id":"f4c61c23-1777-504d-9d91-c2d0f98d5656","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f4c61c23-1777-504d-9d91-c2d0f98d5656/attachment.md","path":"references/multi-cli-integration.md","size":8676,"sha256":"5299c1c5c3563b1bb68eb9dcbc23c38c2636f29ea7e12e1325079bfeade74239","contentType":"text/markdown; charset=utf-8"},{"id":"7a83ff0c-ed74-5e5d-b5e8-25a49ee2252b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7a83ff0c-ed74-5e5d-b5e8-25a49ee2252b/attachment.md","path":"references/state-machine.md","size":15187,"sha256":"2c1a1d1af3e11f3e71f6d406962d4614e8ac5c7de4279c9c29c24a3e1ce1ca27","contentType":"text/markdown; charset=utf-8"},{"id":"1522f860-63a3-5659-9425-c341f970535a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1522f860-63a3-5659-9425-c341f970535a/attachment.py","path":"scripts/ralph_loop.py","size":53669,"sha256":"1ace0eb017758c8bfc0aa7e490a7eda6efda2c3a4040030665895ac7128b7abb","contentType":"text/x-python; charset=utf-8"}],"bundle_sha256":"0ab4c17709764c6ec09cd8f0ce5d7702f6334c78594bfcc49a9a53615f61876b","attachment_count":5,"text_attachments":5,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"plugins/developer-kit-specs/skills/ralph-loop/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"productivity-workflow","category_label":"Productivity"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"productivity-workflow","import_tag":"clean-skills-v1","description":"Ralph Wiggum-inspired automation loop for specification-driven development. Orchestrates task implementation, review, cleanup, and synchronization using a Python script. Use when: user runs /loop command, user asks to automate task implementation, user wants to iterate through spec tasks step-by-step, or user wants to run development workflow automation with context window management. One step per invocation. State machine: init → choose_task → implementation → review → fix → cleanup → sync → update_done. Supports --from-task and --to-task for task range filtering. State persisted in fix_plan.json.","allowed-tools":"Read, Write, Edit, Bash, Grep, Glob, TodoWrite"}},"renderedAt":1782988851812}

⚠️ WARNING : This skill was deprecated in favor of a new command that uses a Python orchestrator script. The old command will be removed soon. Please migrate to the new command. Ralph Loop — Python Orchestrator ⚠️ IMPORTANT : This skill uses a Python orchestrator script. Do NOT execute arbitrary bash commands. Use ONLY to run . All task commands (like ) are shown to the user to execute manually. Overview The Ralph Loop applies Geoffrey Huntley's "Ralph Wiggum as a Software Engineer" technique to specification-driven development. It uses a Python orchestrator script that manages a state machin…