curl -L -o ralph-loop-playbook.skill "https://aiskillstore.io/v1/agent/skills/240d008d-cc28-4818-b0a4-cb9251cabed8/download?platform=ClaudeCode"
{
"tool": "download_skill",
"arguments": {
"skill_id": "240d008d-cc28-4818-b0a4-cb9251cabed8",
"platform": "ClaudeCode"
}
}
{
"mcpServers": {
"skill-store": {
"url": "https://aiskillstore.io/mcp/"
}
}
}
Playbook: run an AI agent in a safe 'iterate until done' (Ralph) loop. Use when you want an agent to loop until tests pass, need loop-fitness check, command-verified exit conditions, stagnation/cost limits and a loop-kit/ (PROMPT, todo, progress, exit conditions). 에이전트 반복 루프 안전 설계 플레이북.
Compatible Platforms any
Findings: ["메타데이터 경고: 권장 필드 없음: 'requirements' (SKILL.md v2 권장)"]
✅ No security risks found.
AI Review Stage
스킬 메타데이터에 선언된 `network: false`, `filesystem: false`, `subprocess: false` 권한이 코드 분석 결과와 일치합니다. `main.py`, `lib/common.py`, `lib/kit.py`, `lib/rules.py`의 모든 코드를 검토한 결과, 외부 네트워크 통신, 파일 시스템 접근(읽기/쓰기/삭제), 외부 프로세스 실행을 시도하는 코드가 전혀 없습니다. `os` 모듈은 `sys.path` 설정 및 경로 조작(`os.path.dirname`, `os.path.abspath`)에만 사용되며, 실제 파일 시스템 I/O는 수행하지 않습니다. 입력은 `stdin`으로 받고 출력은 `stdout`으로만 이루어지며, `scaffold` 액션에서 생성되는 '파일'은 실제 디스크에 쓰여지는 것이 아니라 `files` 배열 형태로 JSON 출력에 포함됩니다. 코드 난독화나 사용자 데이터 무단 수집/전송 시도도 발견되지 않았습니다. 코드 품질은 스킬의 목적에 부합하며 명확하고 안전하게 작성되었습니다.
Representative input/output examples for this skill. Agents can use these to understand how to invoke the skill and what output to expect.
Generate PROMPT.md, todo.md, progress.md, EXIT_CONDITIONS.md, RULES.md and loop.config.json. The oversized third task gets a TASK_TOO_BIG warning. (output abridged)
{
"action": "scaffold",
"goal": "Migrate the billing module from callbacks to async/await",
"max_iterations": 15,
"protected_paths": [
"tests/",
"pyproject.toml"
],
"tasks": [
"Convert invoice.create to async",
"Convert invoice.refund to async",
{
"size": "L",
"title": "Update all call sites and then fix every failing test across the whole repo, and clean up"
}
],
"verify_commands": [
{
"command": "pytest -q tests/billing",
"name": "unit tests"
},
{
"command": "mypy billing/",
"name": "types"
}
]
}
{
"action": "scaffold",
"config": {
"branch": "loop/migrate-the-billing-module-from-callback",
"exit_conditions": [
{
"command": "pytest -q tests/billing",
"expect_exit_code": 0,
"name": "unit tests"
},
{
"command": "mypy billing/",
"expect_exit_code": 0,
"name": "types"
}
],
"gate_commands": [
{
"command": "pytest -q tests/billing",
"expect_exit_code": 0,
"name": "unit tests"
},
{
"command": "mypy billing/",
"expect_exit_code": 0,
"name": "types"
}
],
"goal": "Migrate the billing module from callbacks to async/await",
"kit_dir": "loop-kit",
"max_diff_lines": 400,
"max_iterations": 15,
"max_new_todo_per_iteration": 1,
"max_total_minutes": 480,
"protected_paths": [
"tests/",
"pyproject.toml"
],
"recent_progress_entries": 5,
"same_failure_limit": 3,
"stall_limit": 3,
"task_ids": [
"T01",
"T02",
"T03"
],
"timebox_minutes": 30
},
"files": [
{
"content": "# Loop prompt: Migrate the billing module from callbacks to async/await\n\nYou are ONE iteration of a bounded loop. You remember nothing from earlier iterations: the files in `loop-kit/` are your memory.\n\n## Read first (in this order)\n1. `loop-kit/todo.md`\n2. the last 5 entries of `loop-kit/progress.md`\n3. `loop-kit/EXIT_CONDITIONS.md`\n\n...",
"description": "Per-iteration prompt the loop feeds to a fresh agent context.",
"path": "loop-kit/PROMPT.md"
},
{
"content": "# Todo: Migrate the billing module from callbacks to async/await\n\nOne iteration = one item. Keep each item small enough to finish and verify in a single iteration (sizes: S/M; split L).\n\n- [ ] T01 (S) Convert invoice.create to async\n- [ ] T02 (S) Convert invoice.refund to async\n- [ ] T03 (L) Update all call sites and then fix every failing test across the whole repo, and clean up",
"description": "Ordered task list; exactly one unchecked item is worked per iteration.",
"path": "loop-kit/todo.md"
},
{
"content": "# Progress log (append-only)\n\nEntry template (copy for each iteration):\n\n```\n## Iteration N \u2014 YYYY-MM-DD HH:MM\n- item: \u003cid\u003e \u003ctitle\u003e\n- result: done | partial | failed\n- gate: \u003ccommand\u003e -\u003e exit \u003ccode\u003e (output tail: ...)\n...",
"description": "Append-only iteration log; the agent\u0027s memory between iterations.",
"path": "loop-kit/progress.md"
}
],
"findings": [
{
"code": "TASK_TOO_BIG",
"message": "Task \u0027Update all call sites and then fix every failing test across\u0027 may not fit one iteration (size is L; bundles several actions; unbounded words (all/entire/everything)). Split it.",
"severity": "warning",
"where": "tasks[2]"
}
],
"lang": "en",
"next_steps": [
"Save the files and commit them on the loop branch.",
"Dry-run one iteration before automating.",
"After each iteration call the `evaluate` action."
],
"playbook": "ralph-loop-playbook",
"version": "1.0.0"
}
Prose and always-passing commands cannot gate completion; only a real command with a meaningful exit code passes. (output abridged)
{
"action": "lint_exit_conditions",
"exit_conditions": [
"make sure it works",
"pytest -q || true",
{
"command": "npm test"
}
]
}
{
"action": "lint_exit_conditions",
"findings": [
{
"code": "NOT_A_COMMAND",
"message": "Exit condition \u0027make sure it works\u0027 is prose, not a runnable command. Replace it with a command whose exit code decides pass/fail.",
"severity": "error",
"where": "exit_conditions[0]"
},
{
"code": "ALWAYS_PASSES",
"message": "This command can never fail (\u0027|| true\u0027 / \u0027exit 0\u0027); it cannot gate completion.",
"severity": "error",
"where": "exit_conditions[1]"
}
],
"lang": "en",
"passed": false,
"playbook": "ralph-loop-playbook",
"version": "1.0.0"
}
No command can verify completion, so an unattended loop is refused. (output abridged)
{
"action": "fitness",
"answers": {
"decomposable": true,
"safe_to_retry": true,
"under_version_control": true,
"verifiable_by_command": false
}
}
{
"action": "fitness",
"advice": "Do not run an unattended loop. Fix the blockers first (add a verifying command, a branch, or a sandbox).",
"answered": 4,
"blockers": [
{
"message": "Required but false: Completion can be verified by a command (tests, build, lint, script) with a clear pass/fail.",
"question": "verifiable_by_command"
}
],
"lang": "en",
"missing": [
"no_human_judgement",
"has_baseline_tests",
"bounded_scope"
],
"playbook": "ralph-loop-playbook",
"questions": [
{
"id": "verifiable_by_command",
"required": true,
"text": "Completion can be verified by a command (tests, build, lint, script) with a clear pass/fail."
},
{
"id": "under_version_control",
"required": true,
"text": "Work happens in a git repository on a dedicated branch, so every iteration can be reverted."
},
{
"id": "safe_to_retry",
"required": true,
"text": "A failed iteration has no irreversible side effects (no production writes, payments, emails, deletions)."
},
{
"id": "decomposable",
"required": false,
"text": "The goal splits into small tasks that each fit in one iteration."
},
{
"id": "no_human_judgement",
"required": false,
"text": "Done does not depend on subjective taste or stakeholder judgement."
},
{
"id": "has_baseline_tests",
"required": false,
"text": "A passing baseline exists (existing tests or build) to detect regressions."
},
{
"id": "bounded_scope",
"required": false,
"text": "Scope is bounded: the file/module area is known and changes stay inside it."
}
],
"score": 3,
"verdict": "UNSUITABLE",
"version": "1.0.0"
}
The same gate failure fingerprint three iterations in a row triggers THRASH and STALL stop rules. (output abridged)
{
"action": "evaluate",
"history": [
{
"fingerprint": "TypeError:undefined",
"gate": "fail",
"items_done": 0
},
{
"fingerprint": "TypeError:undefined",
"gate": "fail",
"items_done": 0
},
{
"fingerprint": "TypeError:undefined",
"gate": "fail",
"items_done": 0
}
]
}
{
"action": "evaluate",
"decision": "STOP_ESCALATE",
"iteration": 3,
"lang": "en",
"message": "Stop the loop and escalate to a human with progress.md.",
"playbook": "ralph-loop-playbook",
"triggered": [
{
"action": "STOP",
"message": "The same failure repeated 3 times.",
"mode": "THRASH",
"rule": "same_failure_limit"
},
{
"action": "STOP",
"message": "No item completed in 3 consecutive iterations.",
"mode": "STALL",
"rule": "stall_limit"
}
],
"version": "1.0.0"
}
Changing a protected path halts the loop for human review (gate tampering defence). (output abridged)
{
"action": "evaluate",
"history": [
{
"gate": "pass",
"items_done": 1
},
{
"gate": "pass",
"items_done": 1,
"protected_changed": true
}
]
}
{
"action": "evaluate",
"decision": "STOP_ESCALATE",
"iteration": 2,
"lang": "en",
"message": "Stop the loop and escalate to a human with progress.md.",
"playbook": "ralph-loop-playbook",
"triggered": [
{
"action": "STOP",
"message": "A protected path (tests/gate config) was modified. Halt for human review.",
"mode": "FALSE_DONE",
"rule": "protected_path_changed"
}
],
"version": "1.0.0"
}
When every exit-condition command passed, the decision is DONE. (output abridged)
{
"action": "evaluate",
"history": [
{
"exit_conditions_passed": true,
"gate": "pass",
"items_done": 1
}
]
}
{
"action": "evaluate",
"decision": "DONE",
"iteration": 1,
"lang": "en",
"message": "All exit-condition commands passed. Record the evidence and stop.",
"playbook": "ralph-loop-playbook",
"triggered": [],
"version": "1.0.0"
}
lang=ko generates the kit documents in Korean. (output abridged)
{
"action": "scaffold",
"goal": "\uacb0\uc81c \ubaa8\ub4c8 \ube44\ub3d9\uae30 \uc804\ud658",
"lang": "ko",
"protected_paths": [
"tests/"
],
"tasks": [
"invoice.create \ube44\ub3d9\uae30 \uc804\ud658"
],
"verify_commands": [
"pytest -q tests/billing"
]
}
{
"action": "scaffold",
"config": {
"branch": "loop/work",
"exit_conditions": [
{
"command": "pytest -q tests/billing",
"expect_exit_code": 0,
"name": "check-1"
}
],
"gate_commands": [
{
"command": "pytest -q tests/billing",
"expect_exit_code": 0,
"name": "check-1"
}
],
"goal": "\uacb0\uc81c \ubaa8\ub4c8 \ube44\ub3d9\uae30 \uc804\ud658",
"kit_dir": "loop-kit",
"max_diff_lines": 400,
"max_iterations": 20,
"max_new_todo_per_iteration": 1,
"max_total_minutes": 480,
"protected_paths": [
"tests/"
],
"recent_progress_entries": 5,
"same_failure_limit": 3,
"stall_limit": 3,
"task_ids": [
"T01"
],
"timebox_minutes": 30
},
"files": [
{
"content": "# \ub8e8\ud504 \ud504\ub86c\ud504\ud2b8: \uacb0\uc81c \ubaa8\ub4c8 \ube44\ub3d9\uae30 \uc804\ud658\n\n\ub2f9\uc2e0\uc740 \uc81c\ud55c\ub41c \ub8e8\ud504\uc758 **\ud55c \ud68c\ucc28**\uc785\ub2c8\ub2e4. \uc774\uc804 \ud68c\ucc28\ub97c \uae30\uc5b5\ud558\uc9c0 \ubabb\ud558\uba70, `loop-kit/` \uc758 \ud30c\uc77c\uc774 \uae30\uc5b5\uc785\ub2c8\ub2e4.\n\n## \uba3c\uc800 \uc77d\uae30 (\uc21c\uc11c\ub300\ub85c)\n1. `loop-kit/todo.md`\n2. `loop-kit/progress.md` \uc758 \ucd5c\uadfc 5\uac1c \ud56d\ubaa9\n3. `loop-kit/EXIT_CONDITIONS.md`\n\n...",
"description": "Per-iteration prompt the loop feeds to a fresh agent context.",
"path": "loop-kit/PROMPT.md"
},
{
"content": "# \ud560 \uc77c: \uacb0\uc81c \ubaa8\ub4c8 \ube44\ub3d9\uae30 \uc804\ud658\n\n\ud55c \ud68c\ucc28 = \ud55c \ud56d\ubaa9. \uac01 \ud56d\ubaa9\uc740 \ud55c \ud68c\ucc28\uc5d0 \ub05d\ub0b4\uace0 \uac80\uc99d\ud560 \uc218 \uc788\uc744 \ub9cc\ud07c \uc791\uc544\uc57c \ud569\ub2c8\ub2e4(\ud06c\uae30 S/M, L\uc740 \ucabc\uac1c\uae30).\n\n- [ ] T01 (S) invoice.create \ube44\ub3d9\uae30 \uc804\ud658",
"description": "Ordered task list; exactly one unchecked item is worked per iteration.",
"path": "loop-kit/todo.md"
},
{
"content": "# \uc9c4\ud589 \uae30\ub85d (\ucd94\uac00 \uc804\uc6a9)\n\n\ud56d\ubaa9 \ud15c\ud50c\ub9bf(\ud68c\ucc28\ub9c8\ub2e4 \ubcf5\uc0ac):\n\n```\n## \ud68c\ucc28 N \u2014 YYYY-MM-DD HH:MM\n- \ud56d\ubaa9: \u003cid\u003e \u003c\uc81c\ubaa9\u003e\n- \uacb0\uacfc: done | partial | failed\n- \uac8c\uc774\ud2b8: \u003c\uba85\ub839\u003e -\u003e \uc885\ub8cc\ucf54\ub4dc \u003c\ucf54\ub4dc\u003e (\ucd9c\ub825 \ub05d\ubd80\ubd84: ...)\n...",
"description": "Append-only iteration log; the agent\u0027s memory between iterations.",
"path": "loop-kit/progress.md"
}
],
"findings": [],
"lang": "ko",
"next_steps": [
"\ud30c\uc77c\uc744 \uc800\uc7a5\ud558\uace0 \ub8e8\ud504 \ube0c\ub79c\uce58\uc5d0 \ucee4\ubc0b.",
"\uc790\ub3d9\ud654 \uc804\uc5d0 \ud55c \ud68c\ucc28\ub97c \uc2dc\ud5d8 \uc2e4\ud589.",
"\ub9e4 \ud68c\ucc28 \ud6c4 `evaluate` \uc561\uc158 \ud638\ucd9c."
],
"playbook": "ralph-loop-playbook",
"version": "1.0.0"
}
Invalid input returns a structured error and exit code 2. (output abridged)
{
"action": "scaffold",
"goal": "x",
"verify_commands": [
"pytest -q"
]
}
{
"error": {
"code": "INVALID_INPUT",
"message": "\u0027tasks\u0027 must be an array with at least 1 item(s)."
}
}
All examples are also available via the agent API:
/v1/agent/skills/240d008d-cc28-4818-b0a4-cb9251cabed8/schema
No reviews yet. Be the first to leave one!