Geoffrey Huntley์˜ ์›ํ˜• Ralph ํŒจํ„ด๋ถ€ํ„ฐ snarktank/ralph, Claude Code Ralph Loop ํ”Œ๋Ÿฌ๊ทธ์ธ, OpenAI Codex /goal๊นŒ์ง€ โ€” ์žฅ์‹œ๊ฐ„ ์‹คํ–‰ ์ฝ”๋”ฉ ์—์ด์ „ํŠธ๊ฐ€ ์ปจํ…์ŠคํŠธ ๋ถ€ํŒจ๋ฅผ ์–ด๋–ป๊ฒŒ ์šฐํšŒํ•˜๊ณ  ์ข…๋ฃŒ๋ฅผ ์–ด๋–ป๊ฒŒ ์ •์˜ํ•˜๋Š”์ง€ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋ณธ ํฌ์ŠคํŒ…์€ ์„œ๋กœ ๋‹ค๋ฅธ ์—์ด์ „ํŠธ๊ฐ€ ralph loop ๋ฐฉ์‹์œผ๋กœ ์ž‘์„ฑํ•œ ์ดˆ์•ˆ๋“ค์„ ๋ณ‘ํ•ฉํ•œ ๊ฒฐ๊ณผ๋ฌผ์ž…๋‹ˆ๋‹ค.

What is Ralph?

2025๋…„ ํ•˜๋ฐ˜๊ธฐ, ํ˜ธ์ฃผ์˜ ๊ฐœ๋ฐœ์ž Geoffrey Huntley๊ฐ€ ํ•œ ์ค„์งœ๋ฆฌ bash ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ œ์•ˆํ•˜์˜€์Šต๋‹ˆ๋‹ค.

while :; do cat PROMPT.md | claude-code ; done

์ด๊ฒŒ ์ „๋ถ€์ž…๋‹ˆ๋‹ค. AI ์—์ด์ „ํŠธ๊ฐ€ "๊ตฌํ˜„ ๋๋‚ฌ์Šต๋‹ˆ๋‹ค"๋ผ๊ณ  ๋งํ•ด๋„ ๋ฌด์‹œํ•˜๊ณ , ๊ฐ™์€ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋ฌดํ•œํžˆ ๋‹ค์‹œ ์ง‘์–ด๋„ฃ๋Š” ๋‹จ์ˆœํ•œ ๋ฌดํ•œ ๋ฃจํ”„์ž…๋‹ˆ๋‹ค. Huntley๋Š” ์ด ํŒจํ„ด์— ์‹ฌ์Šจ๊ฐ€์กฑ ์บ๋ฆญํ„ฐ ์ด๋ฆ„์„ ๋ถ™์˜€์Šต๋‹ˆ๋‹ค โ€” Ralph Wiggum, ๋˜‘๋˜‘ํ•˜์ง€๋Š” ์•Š์ง€๋งŒ ๋ˆ์งˆ๊ธฐ๊ฒŒ ํฌ๊ธฐํ•˜์ง€ ์•Š๋Š” ๊ทธ ์บ๋ฆญํ„ฐ์ž…๋‹ˆ๋‹ค.

๋‹จ์ˆœํ•˜๋‹ค๊ณ  ๋ฌด์‹œํ•  ๊ฒŒ ์•„๋‹™๋‹ˆ๋‹ค. YC ํ•ด์ปคํ†ค์—์„œ ํ•œ ํŒ€์ด ์ด ์Šคํฌ๋ฆฝํŠธ๋ฅผ GCP ์ธ์Šคํ„ด์Šค์— ์˜ฌ๋ ค๋†“๊ณ  ์ž ๋“ค์—ˆ๋”๋‹ˆ ์•„์นจ์— 6๊ฐœ ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— 1,100๊ฐœ ์ปค๋ฐ‹์ด ์ฐํ˜€ ์žˆ์—ˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. Browser Use๋ฅผ Python์—์„œ TypeScript๋กœ ๊ฑฐ์˜ ๋‹ค ํฌํŒ…ํ–ˆ๊ณ , ๋น„์šฉ์€ 800๋‹ฌ๋Ÿฌ๋กœ ์‹œ๊ฐ„๋‹น 10.50 USD ๊ฐœ๋ฐœ์ž๋ฅผ ๊ณ ์šฉํ•œ ์…ˆ์ด์—ˆ์Šต๋‹ˆ๋‹ค. Huntley ๋ณธ์ธ๋„ ๊ฐ™์€ ํŒจํ„ด์œผ๋กœ 50k USD์งœ๋ฆฌ ์ปจํŠธ๋ž™ํŠธ ๊ฒฐ๊ณผ๋ฌผ(MVP + ํ…Œ์ŠคํŠธ + ๋ฆฌ๋ทฐ ํฌํ•จ)์„ 297 USD์— ๋‚ฉํ’ˆํ•ด 168๋ฐฐ ๋น„์šฉ ์ ˆ๊ฐ์„ ์ž…์ฆํ–ˆ์Šต๋‹ˆ๋‹ค.

Huntley๊ฐ€ ๊ฐ•์กฐํ•˜๋Š” ํ•ต์‹ฌ์€ ๋‹ค์Œ ํ•œ ์ค„๋กœ ์š”์•ฝ๋ฉ๋‹ˆ๋‹ค.

"Ralph is a technique. In its purest form, Ralph is a Bash loop."

์—ฌ๋Ÿฌ ์—์ด์ „ํŠธ๊ฐ€ ์„œ๋กœ ํ†ต์‹ ํ•˜๋Š” ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ๊ตฌ์กฐ ๋Œ€์‹ , ํ•˜๋‚˜์˜ OS ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ˆ˜์ง์œผ๋กœ ํ™•์žฅ๋˜๋Š” ๋ชจ๋†€๋ฆฌ์‹ ์ ‘๊ทผ๋ฒ•์ž…๋‹ˆ๋‹ค. ๋น„๊ฒฐ์ •์ ์ธ LLM ์—ฌ๋Ÿฌ ๊ฐœ๊ฐ€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋ฉฐ ๋งŒ๋“œ๋Š” ํ†ต์ œ ๋ถˆ๊ฐ€๋Šฅํ•œ ํ˜ผ๋ž€์„ ๋ฐฐ์ œํ•˜๊ณ , ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ํ•œ ์—์ด์ „ํŠธ๊ฐ€ ์ง€์†์ ์œผ๋กœ ๋นš์–ด๋‚ด๋ฉฐ ๋‹ค๋“ฌ๊ฒŒ ํ•˜์ž๋Š” ์ฒ ํ•™์  ์ „ํ™˜์ด์—ˆ์Šต๋‹ˆ๋‹ค.

Ralph Wiggum ๋น„์œ ์˜ ์˜๋ฏธ

์ด๋ฆ„์˜ ์–ด์›์€ ์‹ฌ์Šจ ๊ฐ€์กฑ์— ๋“ฑ์žฅํ•˜๋Š” ์บ๋ฆญํ„ฐ Ralph Wiggum์ž…๋‹ˆ๋‹ค. Ralph๋Š” ์˜๋ฆฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฐ™์€ ๋ฏธ๋„๋Ÿผํ‹€์—์„œ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์ ํ”„ํ•˜๊ณ , ๊ฐ™์€ ์‹ค์ˆ˜๋ฅผ ๋ฐ˜๋ณตํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋†€์ดํ„ฐ์— "๋ฏธ๋„๋Ÿผํ‹€์—์„œ๋Š” ๋‚ด๋ ค์™€๋ผ, ์ ํ”„ํ•˜์ง€ ๋ง๋ผ, ์ฃผ์œ„๋ฅผ ๋‘˜๋Ÿฌ๋ณด๋ผ"๋Š” ํ‘œ์ง€ํŒ์„ ์ถฉ๋ถ„ํžˆ ์„ธ์›Œ๋‘๋ฉด, Ralph๋Š” ๊ฒฐ๊ตญ ์•ˆ์ „ํ•˜๊ฒŒ ๋†€์ดํ„ฐ๋ฅผ ๋น ์ ธ๋‚˜์˜ต๋‹ˆ๋‹ค.

LLM ํ•œ ๋ฒˆ์˜ ํ˜ธ์ถœ์€ ๋น„๊ฒฐ์ •์ ์ž…๋‹ˆ๋‹ค. ๊ฐ™์€ ํ”„๋กฌํ”„ํŠธ๋„ ๋งค๋ฒˆ ๋‹ค๋ฅธ ๋‹ต์„ ๋‚ด๋†“๊ณ , ์ข…์ข… ํ—›์†Œ๋ฆฌ๋ฅผ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ฃจํ”„ ์ž์ฒด๊ฐ€ ๊ฒฐ์ •์ ์ด๊ณ , ๋งค ๋ฐ˜๋ณต๋งˆ๋‹ค ๊นจ๋—ํ•œ ์ปจํ…์ŠคํŠธ๋กœ ์‹œ์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ถฉ๋ถ„ํžˆ ์ข‹์€ ํ‘œ์ง€ํŒ(ํ”„๋กฌํ”„ํŠธ, ๋ช…์„ธ, ํ…Œ์ŠคํŠธ)์ด ์žˆ์œผ๋ฉด ์ถœ๋ ฅ์€ ์ ์ง„์ ์œผ๋กœ ์ •๋‹ต์— ์ˆ˜๋ ดํ•ฉ๋‹ˆ๋‹ค. ๋˜‘๋˜‘ํ•œ ํ•œ ๋ฒˆ์˜ ํ˜ธ์ถœ์ด ์•„๋‹ˆ๋ผ ๋ฉ์ฒญํ•œ ๋ฐฑ ๋ฒˆ์˜ ํ˜ธ์ถœ์ด ์ž‘๋™์˜ ํ† ๋Œ€๊ฐ€ ๋œ๋‹ค๋Š”, ์ง๊ด€์— ๋‹ค์†Œ ๋ฐ˜ํ•˜๋Š” ์ฃผ์žฅ์ด ๋น„์œ ์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

Why Does It Work?: Context Rot

Ralph Loop๊ฐ€ ์ž‘๋™ํ•˜๋Š” ์ด์œ ๋ฅผ ์ดํ•ดํ•˜๋ ค๋ฉด LLM์˜ ๋ณธ์งˆ์  ํ•œ๊ณ„์ธ Context Rot ํ˜„์ƒ์„ ๋จผ์ € ๋ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์–ธ์–ด ๋ชจ๋ธ์˜ ์ปจํ…์ŠคํŠธ ์œˆ๋„์šฐ๋Š” ๋‘ ๊ตฌ์—ญ์œผ๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค.

  • Smart Zone (์•ž์ชฝ ~40%): ๋ชจ๋ธ์ด ์˜ˆ๋ฆฌํ•˜๊ณ , ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ์™€ ์š”๊ตฌ์‚ฌํ•ญ์„ ์™„๋ฒฝํžˆ ์ดํ•ดํ•˜๋ฉฐ, ์•„ํ‚คํ…์ฒ˜์ ์œผ๋กœ ๊ฑด์ „ํ•œ ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๋Š” ๊ตฌ๊ฐ„
  • Dumb Zone (๋‚˜๋จธ์ง€ ~60%): ๋””๋ฒ„๊น… ๋กœ๊ทธ, ์ปดํŒŒ์ผ ์˜ค๋ฅ˜, ์ด์ „ ํ„ด์˜ ์‹คํŒจ ํ”์ ์ด ๋ˆ„์ ๋˜๋ฉด์„œ ์–ดํ…์…˜์ด ํ•ต์‹ฌ ๋ชฉํ‘œ์—์„œ ๋ถ„์‚ฐ๋˜๋Š” ๊ตฌ๊ฐ„. ๊ฐ™์€ ์‹ค์ˆ˜๋ฅผ ๋ฐ˜๋ณตํ•˜๊ณ  ํ™˜๊ฐ์ด ๋Š˜์–ด๋‚˜๋ฉฐ ๋ฌด์˜๋ฏธํ•œ ์ฝ”๋“œ๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๊ต์ฐฉ ์ƒํƒœ์— ๋น ์ง

Huntley๊ฐ€ ๊ฐ€์žฅ ๊ธธ๊ฒŒ ๊ฐ•์กฐํ•˜๋Š” ํ•œ ์ค„์€ ๋‹ค์Œ์ž…๋‹ˆ๋‹ค.

"The more you use the context window, the worse the outcomes you'll get."

๊ทผ๊ฑฐ๋Š” ๋‘ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค.

  1. ์ปจํ…์ŠคํŠธ ์œˆ๋„์šฐ ์ž์ฒด๊ฐ€ ํ•œ์ •๋œ ์ž์›์ž…๋‹ˆ๋‹ค. ํ•˜๋‚˜์˜ ์ปจํ…์ŠคํŠธ ์œˆ๋„์šฐ ์•ˆ์—์„œ ๋ช…์„ธยท๋„๊ตฌยท์ค‘๊ฐ„ ๊ฒฐ๊ณผยท์‹คํŒจ ํ”์ ์ด ๋ชจ๋‘ ๊ฒฝ์Ÿํ•ฉ๋‹ˆ๋‹ค.
  2. ํ•œ ์„ธ์…˜์ด ๊ธธ์–ด์งˆ์ˆ˜๋ก ๋ชจ๋ธ์€ ์ž์‹ ์ด ์•ž์„œ ๋งŒ๋“  ๊ฑฐ์ง“๋ง๊ณผ ์ž˜๋ชป๋œ ๊ฐ€์ •๊นŒ์ง€ ๋ˆ„์ ํ•ด ๋“ค๊ณ  ๊ฐ‘๋‹ˆ๋‹ค.

์ด ๋‘˜์ด ํ•ฉ์ณ์ง€๋ฉด ์„ธ์…˜์˜ ํ›„๋ฐ˜๋ถ€ ์ถœ๋ ฅ์ด ์ „๋ฐ˜๋ถ€๋ณด๋‹ค ์ผ๊ด€๋˜๊ฒŒ ๋” ๋‚˜๋น ์ง€๋Š” ํ˜„์ƒ์ด ์ƒ๊น๋‹ˆ๋‹ค.

์ข‹์€ ์ž์œจ ๋ฃจํ”„ ์‹œ์Šคํ…œ์˜ ๋ณธ์งˆ์€ ๋ชจ๋ธ์ด ์–ผ๋งˆ๋‚˜ ๋˜‘๋˜‘ํ•œ ์ฝ”๋“œ๋ฅผ ์งœ๋А๋ƒ๊ฐ€ ์•„๋‹ˆ๋ผ, ์ž‘์—… ๋‚ด๋‚ด ์–ด๋–ป๊ฒŒ ๋ชจ๋ธ์„ Smart Zone์— ๊ฐ•์ œ๋กœ ๋จธ๋ฌด๋ฅด๊ฒŒ ํ•  ๊ฒƒ์ธ๊ฐ€์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ฐ€์žฅ ๋‹จ์ˆœํ•˜๊ณ  ํšจ๊ณผ์ ์ธ ๋‹ต์ด โ€” ๋งค iteration๋งˆ๋‹ค ํ”„๋กœ์„ธ์Šค๋ฅผ ์ฃฝ์ด๊ณ  ์ƒˆ๋กœ ๋„์šฐ๊ธฐ์ž…๋‹ˆ๋‹ค.

flowchart TB
    subgraph Single["๋‹จ์ผ ์ปจํ…์ŠคํŠธ ๋ˆ„์  ๋ฐฉ์‹"]
        S1["iter1<br/>์ปจํ…์ŠคํŠธ 20%"] --> S2["iter2<br/>์ปจํ…์ŠคํŠธ 35%"] --> S3["iter3<br/>์ปจํ…์ŠคํŠธ 50%"] --> S4["iter4<br/>Dumb Zone ์ง„์ž…<br/>ํ’ˆ์งˆ ๊ธ‰๋ฝ"]
    end

    subgraph Fresh["Fresh Context ๋ฐฉ์‹ (Ralph)"]
        F1["iter1<br/>fresh AI"] --> FS1["ํŒŒ์ผ ์‹œ์Šคํ…œ<br/>(git, prd.json,<br/>progress.txt)"]
        FS1 --> F2["iter2<br/>fresh AI<br/>ํŒŒ์ผ์—์„œ ์ƒํƒœ ๋ณต์›"] --> FS1
        FS1 --> F3["iter3<br/>fresh AI<br/>ํŒŒ์ผ์—์„œ ์ƒํƒœ ๋ณต์›"] --> FS1
        FS1 --> F4["iterโˆž<br/>fresh AI<br/>ํ•ญ์ƒ Smart Zone"]
    end

    style Single fill:#ffebee,stroke:#e91e63
    style Fresh fill:#e8f5e9,stroke:#4caf50
    style S4 fill:#ffcdd2
    style FS1 fill:#fff9c4,stroke:#f57f17

์ง„ํ–‰ ์ƒํ™ฉ์„ LLM ์ปจํ…์ŠคํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ ํŒŒ์ผ๊ณผ git์— ์ €์žฅํ•˜๋ฉด, ์ปจํ…์ŠคํŠธ๊ฐ€ ์ฐจ์˜ค๋ฅผ ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค. ์ฐจ๋ฉด ์ƒˆ ์—์ด์ „ํŠธ๋ฅผ ๋„์šฐ๋ฉด ๋ฉ๋‹ˆ๋‹ค. freshํ•œ ์—์ด์ „ํŠธ๊ฐ€ ํŒŒ์ผ์‹œ์Šคํ…œ ์ƒํƒœ๋ฅผ ์ฝ๊ณ  ์ด์–ด์„œ ์ž‘์—…ํ•˜๋‹ˆ๊นŒ์š”. ๋ฉ”๋ชจ๋ฆฌ ์ฑ…์ž„์„ ๋น„์‹ผ ํ† ํฐ์—์„œ ๋ฌด๋ฃŒ์ธ ๋””์Šคํฌ๋กœ ์˜คํ”„๋กœ๋”ฉํ•œ ์…ˆ์ž…๋‹ˆ๋‹ค.

One Item Per Loop

๊ฐ™์€ ๋งฅ๋ฝ์—์„œ Huntley๊ฐ€ ํ•œ ๋ฒˆ ๋” ๋ชป ๋ฐ•๋Š” ๊ทœ์น™์ด "ํ•œ ๋ฃจํ”„์— ํ•œ ์ž‘์—…(One item per loop)" ์ž…๋‹ˆ๋‹ค.

"One item per loop. I need to repeat myself hereโ€”one item per loop. You may relax this restriction as the project progresses, but if it starts going off the rails, then you need to narrow it down to just one item."

์ž‘์—…์„ ์ž˜๊ฒŒ ์ชผ๊ฐค์ˆ˜๋ก ํ•œ ์ปจํ…์ŠคํŠธ ์œˆ๋„์šฐ ์•ˆ์—์„œ ๋๋‚ผ ์ˆ˜ ์žˆ๊ณ , ๋๋‚˜๋ฉด ๋‹ค์Œ ๋ฃจํ”„๊ฐ€ ๊นจ๋—ํ•œ ์ƒํƒœ๋กœ ๋‹ค์‹œ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒŒ fresh-context ํŒจํ„ด์ด ๋ˆ„์ -์„ธ์…˜ ๋ชจ๋ธ์„ ์ด๊ธฐ๋Š” ์‹ค์งˆ์ ์ธ ์ด๋“์ž…๋‹ˆ๋‹ค.

Spec Beats Execution

Ralph ์‚ฌ์ดํด์€ '์ฝ”๋“œ๋ฅผ ์ง ๋‹ค'๊ฐ€ ์•„๋‹ˆ๋ผ ์ƒ์„ฑ(Generate)๊ณผ ์—ญ์••(Backpressure)์˜ ๋‘ ํŽ˜์ด์ฆˆ๋กœ ์งœ์ž…๋‹ˆ๋‹ค. ์ƒ์„ฑ ๋‹จ๊ณ„์—์„œ LLM์ด ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค๊ณ , ์—ญ์•• ๋‹จ๊ณ„์—์„œ ๋ช…์„ธยทํ…Œ์ŠคํŠธยท์šฐ์„ ์ˆœ์œ„ ํ๊ฐ€ ๊ทธ ์ฝ”๋“œ๋ฅผ ๊ฑธ๋Ÿฌ๋ƒ…๋‹ˆ๋‹ค.

"Generating code is now cheap, and the code that Ralph generates is within your complete control through your technical standard library and your specifications."

"As code generation is easy now, what is hard is ensuring that Ralph has generated the right thing."

์ฝ”๋“œ ์ž‘์„ฑ์€ ๋” ์ด์ƒ ๋ณ‘๋ชฉ์ด ์•„๋‹™๋‹ˆ๋‹ค. ์ง„์งœ ์ผ์€ ๋ฌด์—‡์„ ์งค์ง€๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ์ ์–ด๋‘๋Š” ์ผ โ€” ์ฆ‰ ๋ช…์„ธ(Spec)๋ฅผ ์“ฐ๋Š” ์ผ์ด ๋ฉ๋‹ˆ๋‹ค. ์ด ํŒจํ„ด์—์„œ ๋ช…์„ธ๊ฐ€ ์‹คํ–‰์„ ์ด๊ธด๋‹ค๋Š” ๋ง์€ "๋ฃจํ”„๋Š” ๋‹จ์ˆœํ• ์ˆ˜๋ก ์ข‹๊ณ , ๋‹จ์ˆœํ•œ ๋ฃจํ”„๊ฐ€ ๋งค๋ฒˆ ์ฝ์–ด๊ฐ€๋Š” ์™ธ๋ถ€ ์ƒํƒœ(๋ช…์„ธยท์šฐ์„ ์ˆœ์œ„ยท์ง„ํ–‰ ๋กœ๊ทธ)์— ๋ชจ๋“  ์ง€๋Šฅ์„ ๋ฐ•์•„๋‘๋ผ"๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค.

Huntley๋Š” LLM ๋ฉ”๋ชจ๋ฆฌ ๋Œ€์‹  ์„ธ ์ข…๋ฅ˜์˜ ํŒŒ์ผ์— ์ง„์‹ค์„ ์ ์–ด ๋‘ก๋‹ˆ๋‹ค.

  • specs/ โ€” ํ•ฉ์˜๋œ ์‚ฌ์–‘. "Specs are formed through a conversation with the agent at the beginning phase of a project."
  • fix_plan.md โ€” ์šฐ์„ ์ˆœ์œ„ ํ. "The TODO list is what I'm watching like a hawk. And I throw it out often."
  • AGENT.md โ€” ๋Ÿฐํƒ€์ž„์— ๋ฐœ๊ฒฌํ•œ ํ•™์Šต. "When you learn something new ... make sure you update @AGENT.md using a subagent but keep it brief."

๋‹ค์Œ ์ ˆ์—์„œ ๋ณผ snarktank/ralph๋Š” ์ด ์…‹์„ ๊ทธ๋Œ€๋กœ ์ž๊ธฐ ํฌ๋งท์— ํก์ˆ˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

ghuntley ์—์„ธ์ด snarktank/ralph ๊ตฌํ˜„
specs/* ์‚ฌ์–‘ ๋””๋ ‰ํ„ฐ๋ฆฌ prd.json.userStories[].acceptanceCriteria
fix_plan.md ์šฐ์„ ์ˆœ์œ„ ํ prd.json.userStories[].priority + passes
AGENT.md ๋Ÿฐํƒ€์ž„ ํ•™์Šต progress.txt (append-only)
while :; do cat PROMPT.md | claude-code ; done scripts/ralph/ralph.sh์˜ for i in $(seq 1 $MAX_ITERATIONS) ๋ฃจํ”„
"until specs.md is satisfied" ์ข…๋ฃŒ <promise>COMPLETE</promise> ํ† ํฐ grep์œผ๋กœ ์ข…๋ฃŒ

ํ‘œ์˜ ๋งˆ์ง€๋ง‰ ์ค„์ด ๋‘ ๋„๊ตฌ๋ฅผ ๊ฐ€๋ฅด๋Š” ์ž‘์€ ์ฐจ์ด์ž…๋‹ˆ๋‹ค. ghuntley์˜ ํ•œ ์ค„์งœ๋ฆฌ ๋ฃจํ”„๋Š” ์‚ฌ๋žŒ์ด Ctrl+C๋กœ ๋ฉˆ์ถœ ๋•Œ๊นŒ์ง€ ๋„๋Š” ๋ฐ˜๋ฉด, snarktank/ralph๋Š” ๋ชจ๋ธ์ด ๋ชจ๋“  acceptance criteria๋ฅผ ํ†ต๊ณผ์‹œ์ผฐ๋‹ค๊ณ  ํŒ๋‹จํ•˜๋ฉด stdout์œผ๋กœ ํ•ฉ์˜๋œ ํ† ํฐ <promise>COMPLETE</promise>์„ ์ถœ๋ ฅํ•˜๊ณ , ralph.sh๊ฐ€ ์ด ๋ฌธ์ž์—ด์„ grep์œผ๋กœ ์žก์•„ ์ •์ƒ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•œ ์ค„์ด "์‚ฌ๋žŒ์ด ์ง์ ‘ ๋๋งบ๋Š” ๋„๊ตฌ" ๋ฅผ "์Šค์Šค๋กœ ๋๋งบ๋Š” ๋„๊ตฌ" ๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

snarktank/ralph

Ralph ํŒจํ„ด์˜ ์ฒ ํ•™์„ ๊ฐ€์žฅ ์ถฉ์‹คํ•˜๊ฒŒ ์‹ค์šฉ ์ฝ”๋“œ๋กœ ์˜ฎ๊ธด ์˜คํ”ˆ์†Œ์Šค๊ฐ€ snarktank/ralph์ž…๋‹ˆ๋‹ค.

๋‹จ์ˆœํ•œ bash ํ•œ ์ค„์„ PRD ๊ธฐ๋ฐ˜์˜ ์ž์œจ ๊ฐœ๋ฐœ ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ํ™•์žฅํ•œ ํ˜•ํƒœ์ด๋ฉฐ, ์‚ฌ์šฉ์ž๊ฐ€ ์†์œผ๋กœ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์€ ์‚ฌ์‹ค์ƒ prd.json ํ•œ ํŒŒ์ผ ๋ฟ์ด๊ณ  ๋‚˜๋จธ์ง€ โ€” ๋ฃจํ”„ ์Šคํฌ๋ฆฝํŠธ์™€ ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ โ€” ๋Š” ์ €์žฅ์†Œ์—์„œ ๊ทธ๋Œ€๋กœ ๋ณต์‚ฌํ•ด ์”๋‹ˆ๋‹ค.

Prerequisites
  • Claude Code(npm install -g @anthropic-ai/claude-code) ๋˜๋Š” Amp
  • jq โ€” ralph.sh๊ฐ€ PRD JSON์—์„œ branchName์„ ์ฝ์„ ๋•Œ ์”๋‹ˆ๋‹ค (brew install jq)
  • git ์ €์žฅ์†Œ โ€” ๋งค ๋ฐ˜๋ณต์ด ์ปค๋ฐ‹์„ ๋‚จ๊ธฐ๊ธฐ ๋•Œ๋ฌธ์— ๊นจ๋—ํ•œ ์›Œํ‚น ํŠธ๋ฆฌ์—์„œ ์‹œ์ž‘ํ•˜๋Š” ๊ฒŒ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค
Installation

๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ์˜ต์…˜์€ ์ €์žฅ์†Œ๋ฅผ ํด๋ก ํ•œ ๋’ค ralph.sh์™€ ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ์„ ์ž‘์—… ์ค‘์ธ ํ”„๋กœ์ ํŠธ๋กœ ๋ณต์‚ฌํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

# 1) Ralph ์ €์žฅ์†Œ ํด๋ก 
git clone https://github.com/snarktank/ralph.git /tmp/ralph

# 2) ์ž‘์—… ์ค‘์ธ ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ๋กœ ์ด๋™
cd /path/to/your-project
mkdir -p scripts/ralph

# 3) ๋ฃจํ”„ ์Šคํฌ๋ฆฝํŠธ + ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ ๋ณต์‚ฌ
cp /tmp/ralph/ralph.sh scripts/ralph/
cp /tmp/ralph/CLAUDE.md scripts/ralph/    # Claude Code์šฉ
# ๋˜๋Š”
cp /tmp/ralph/prompt.md scripts/ralph/    # Amp์šฉ

# 4) ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ
chmod +x scripts/ralph/ralph.sh

์‹คํ–‰์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

# Amp (๊ธฐ๋ณธ)
./scripts/ralph/ralph.sh [max_iterations]

# Claude Code
./scripts/ralph/ralph.sh --tool claude [max_iterations]

๊ธฐ๋ณธ ๋ฐ˜๋ณต ํšŸ์ˆ˜๋Š” 10ํšŒ์ด๋ฉฐ ์ธ์ˆ˜ ํ•œ ๊ฐœ๋กœ ๋ฎ์–ด์”๋‹ˆ๋‹ค. ๋งค ๋ฐ˜๋ณต๋งˆ๋‹ค === Ralph Iteration N of M === ํ—ค๋”๊ฐ€ stderr์— ์ฐํžˆ๊ณ , ๋ชจ๋ธ์ด ๋ชจ๋“  acceptanceCriteria๋ฅผ ์ถฉ์กฑํ–ˆ๋‹ค๊ณ  ํŒ๋‹จํ•˜๋ฉด stdout์— <promise>COMPLETE</promise> ํ† ํฐ์„ ํ˜๋ ค ์ข…๋ฃŒ๋ฅผ ํ•ฉ์˜ํ•ฉ๋‹ˆ๋‹ค. ์ฒซ ์‹คํ–‰์€ ํ•œ ์ž๋ฆฟ์ˆ˜ ๋ฐ˜๋ณต์œผ๋กœ ๋™์ž‘ ํ™•์ธ๋ถ€ํ„ฐ ํ•˜๋Š” ๊ฒŒ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

Core Logic

scripts/ralph/ralph.sh:84-108 (6c53cb0)์˜ ๋ณธ๋ฌธ์€ ๋งค์šฐ ๋‹จ์ˆœํ•ฉ๋‹ˆ๋‹ค. ๊ธธ์–ด ๋ณด์ด์ง€๋งŒ ์ธ์ž ํŒŒ์‹ฑ๊ณผ archive ์ฒ˜๋ฆฌ ๋นผ๋ฉด ์‚ฌ์‹ค์ƒ for ๋ฃจํ”„ ํ•œ ๋ฉ์–ด๋ฆฌ์ž…๋‹ˆ๋‹ค.

for i in $(seq 1 $MAX_ITERATIONS); do
  echo ""
  echo "==============================================================="
  echo "  Ralph Iteration $i of $MAX_ITERATIONS ($TOOL)"
  echo "==============================================================="

  # Run the selected tool with the ralph prompt
  if [[ "$TOOL" == "amp" ]]; then
    OUTPUT=$(cat "$SCRIPT_DIR/prompt.md" | amp --dangerously-allow-all 2>&1 | tee /dev/stderr) || true
  else
    # Claude Code: use --dangerously-skip-permissions for autonomous operation, --print for output
    OUTPUT=$(claude --dangerously-skip-permissions --print < "$SCRIPT_DIR/CLAUDE.md" 2>&1 | tee /dev/stderr) || true
  fi

  # Check for completion signal
  if echo "$OUTPUT" | grep -q "<promise>COMPLETE</promise>"; then
    echo ""
    echo "Ralph completed all tasks!"
    echo "Completed at iteration $i of $MAX_ITERATIONS"
    exit 0
  fi

  echo "Iteration $i complete. Continuing..."
  sleep 2
done

ํ•ต์‹ฌ์€ ์„ธ ์ค„์ž…๋‹ˆ๋‹ค. ๋””์ŠคํŒจ์น˜ ๋‘ ์ค„์ด ๋งค ๋ฐ˜๋ณต๋งˆ๋‹ค amp ๋˜๋Š” claude CLI๋ฅผ ์ƒˆ ํ”„๋กœ์„ธ์Šค๋กœ ๋„์›Œ โ€” ๋”ฐ๋ผ์„œ ์ปจํ…์ŠคํŠธ๊ฐ€ ์ž๋™์œผ๋กœ ๊นจ๋—ํ•˜๊ฒŒ ๋ฆฌ์…‹๋œ ์ฑ„ โ€” prompt.md ๋˜๋Š” CLAUDE.md๋ฅผ stdin์œผ๋กœ ํ˜๋ ค๋ณด๋‚ด๊ณ , grep -q "<promise>COMPLETE</promise>"์ด stdout ํ•œ ์ค„๋กœ LLM๊ณผ ์…ธ์ด "๋๋‚ฌ๋‹ค" ๋ฅผ ํ•ฉ์˜ํ•˜๋Š” ๋‹จ์ผ ์ธํ„ฐํŽ˜์ด์Šค์ด๋ฉฐ, ๊ทธ๊ฒŒ ์žกํžˆ์ง€ ์•Š์œผ๋ฉด sleep 2๋ฅผ ๊ฑฐ์ณ for ๋ฃจํ”„๊ฐ€ ๋‹ค์Œ ๋ฐ˜๋ณต์œผ๋กœ ์ž๋™ ์ง„์ž…ํ•ฉ๋‹ˆ๋‹ค. 25์ค„ ์•ˆ์—์„œ ๋””์ŠคํŒจ์น˜ยท์บก์ฒ˜ยท์ข…๋ฃŒ ํŒ์ •ยทํŽ˜์ด์‹ฑ์ด ๋ชจ๋‘ ๋‹ซํž™๋‹ˆ๋‹ค.

prd.json

๋งค iteration์—์„œ "๋‚ด๊ฐ€ ์ด๋ฒˆ์— ๋ฌด์—‡์„ ํ•ด์•ผ ํ•˜๋Š”๊ฐ€" ๋Š” prd.json ํ•œ ํŒŒ์ผ์— ๋‹ค ๋“ค์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•œ user story์˜ ์ตœ์†Œ ํ˜•ํƒœ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

{
  "project": "MyApp",
  "branchName": "ralph/task-priority",
  "description": "Task Priority System - Add priority levels to tasks",
  "userStories": [
    {
      "id": "US-001",
      "title": "Add priority field to database",
      "description": "As a developer, I need to store task priority so it persists across sessions.",
      "acceptanceCriteria": [
        "Add priority column to tasks table: 'high' | 'medium' | 'low' (default 'medium')",
        "Generate and run migration successfully",
        "Typecheck passes"
      ],
      "priority": 1,
      "passes": false,
      "notes": ""
    }
  ]
}

passes: false์ธ ์Šคํ† ๋ฆฌ๋งŒ ํ์— ๋‚จ๊ณ , ๋ชจ๋‘ true๊ฐ€ ๋˜๋ฉด <promise>COMPLETE</promise>๊ฐ€ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

ํ•œ ์Šคํ† ๋ฆฌ๊ฐ€ ๊ฐ€์ง€๋Š” ํ•„๋“œ๋Š” idยทtitleยทdescriptionยทacceptanceCriteria[]ยทpriority(์ž‘์€ ๊ฐ’์ด ์šฐ์„ )ยทpassesยทnotes ์ผ๊ณฑ ๊ฐœ๋ฟ์ด๋ฉฐ, ์—์ด์ „ํŠธ๋Š” ๋งค ๋ฐ˜๋ณต์—์„œ passes: false์ธ ๊ฐ€์žฅ ๋†’์€ ์šฐ์„ ์ˆœ์œ„ ํ•ญ๋ชฉ ํ•˜๋‚˜๋งŒ ๊ณจ๋ผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. AI๊ฐ€ ๋ณด๊ธฐ์— prd.json์€ TODO ๋ฆฌ์ŠคํŠธ + ์ •๋‹ต์ง€ ์—ญํ• ์„ ๋™์‹œ์— ํ•ฉ๋‹ˆ๋‹ค. ํ•ต์‹ฌ์€ "acceptanceCriteria๊ฐ€ ๊ณง LLM์— ํ˜๋Ÿฌ๊ฐˆ ์‚ฌ์–‘" ์ด๋ผ๋Š” ์ ์ž…๋‹ˆ๋‹ค.

"Each PRD item should be small enough to complete in one context window. If a task is too big, the LLM runs out of context before finishing and produces poor code."

ํ•œ ํ•ญ๋ชฉ์ด ํ•œ ์ปจํ…์ŠคํŠธ ์œˆ๋„์šฐ ์•ˆ์—์„œ ๋๋‚ผ ์ˆ˜ ์žˆ์„ ๋งŒํผ ์ž‘์•„์•ผ ํ•˜๊ณ , ๋ชจํ˜ธํ•œ ํ‘œํ˜„ ๋Œ€์‹  ์ž๋™ ๊ฒ€์ฆ ๊ฐ€๋Šฅํ•œ ํ•ญ๋ชฉ(์˜ˆ: "ํƒ€์ž…์ฒดํฌ ํ†ต๊ณผ", "ํ…Œ์ŠคํŠธ ํ†ต๊ณผ")์„ ์ ์–ด๋‘๋Š” ๊ฒŒ ๊ถŒ์žฅ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.

progress.txt

progress.txt๋Š” ๋” ๋‹จ์ˆœํ•ฉ๋‹ˆ๋‹ค. append-only ํ…์ŠคํŠธ ํŒŒ์ผ์ž…๋‹ˆ๋‹ค. ๋งค iteration์˜ fresh AI๊ฐ€ ์ด ํŒŒ์ผ์— ๊ทธ๋‚  ์•Œ์•„๋‚ธ ์ฝ”๋“œ๋ฒ ์ด์Šค ์ •๋ณด๋ฅผ ํ•œ๋‘ ์ค„์”ฉ ๊ธฐ๋กํ•˜๊ณ , ๋‹ค์Œ iteration์˜ fresh AI๊ฐ€ ์‹œ์ž‘ํ•  ๋•Œ ์ด๊ฑธ ์ฝ์–ด์„œ ๊ฐ™์€ ์ง€๋ขฐ๋ฅผ ๋‹ค์‹œ ๋ฐŸ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

# Ralph Progress Log
Started: Tue May 6 23:18:00 KST 2026
---
[iter 3] ๋ผ์šฐํŒ…์ด app/router.go์— ๋ชจ์—ฌ์žˆ๊ณ , ๋ชจ๋“  ํ•ธ๋“ค๋Ÿฌ๋Š” RegisterRoutes๋กœ ๋“ฑ๋กํ•ด์•ผ ํ•จ
[iter 4] FTS ์ธ๋ฑ์Šค๊ฐ€ search_index ํ…Œ์ด๋ธ”์— ์ด๋ฏธ ์กด์žฌ - ์ƒˆ๋กœ ๋งŒ๋“ค์ง€ ๋ง ๊ฒƒ
[iter 6] make test ๊ฐ€ ํ™˜๊ฒฝ๋ณ€์ˆ˜ TEST_DATABASE_URL ์—†์œผ๋ฉด panic. .env.test ์ฐธ๊ณ 
[iter 7] legacy/ ๋””๋ ‰ํ† ๋ฆฌ๋Š” PRD์—์„œ ๋ช…์‹œ์ ์œผ๋กœ ๊ฑด๋“œ๋ฆฌ์ง€ ๋ง๋ผ๊ณ  ํ•จ

์ด๊ฒŒ Ralph ํŒจํ„ด์˜ ์™ธ์žฅ ๋‘๋‡Œ(external brain) ์ž…๋‹ˆ๋‹ค. LLM ์ปจํ…์ŠคํŠธ ์œˆ๋„์šฐ๊ฐ€ ๋น„์‹ผ ํœ˜๋ฐœ์„ฑ ๋ฉ”๋ชจ๋ฆฌ๋ผ๋ฉด, progress.txt๋Š” ๋ฌด๋ฃŒ์˜ ์˜๊ตฌ ์ €์žฅ์†Œ์ž…๋‹ˆ๋‹ค.

CLAUDE.md / prompt.md

ralph.sh๊ฐ€ ๋งค๋ฒˆ ์ž์‹ ํ”„๋กœ์„ธ์Šค์— ํŒŒ์ดํ”„ํ•˜๋Š” ๊ทธ ํ”„๋กฌํ”„ํŠธ ํŒŒ์ผ์ด CLAUDE.md(๋˜๋Š” Amp์˜ prompt.md)์ž…๋‹ˆ๋‹ค. ์ด ํŒŒ์ผ์ด Ralph์˜ "agent ํ—Œ๋ฒ•" ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. snarktank/ralph ์˜ CLAUDE.md:7-16 (6c53cb0)์˜ verbatim์ž…๋‹ˆ๋‹ค.

1. Read the PRD at `prd.json` (in the same directory as this file)
2. Read the progress log at `progress.txt` (check Codebase Patterns section first)
3. Check you're on the correct branch from PRD `branchName`. If not, check it out or create from main.
4. Pick the **highest priority** user story where `passes: false`
5. Implement that single user story
6. Run quality checks (e.g., typecheck, lint, test - use whatever your project requires)
7. Update CLAUDE.md files if you discover reusable patterns (see below)
8. If checks pass, commit ALL changes with message: `feat: [Story ID] - [Story Title]`
9. Update the PRD to set `passes: true` for the completed story
10. Append your progress to `progress.txt`

์ด ์งง์€ markdown ํŒŒ์ผ์ด ๋ชจ๋“  ๋งˆ๋ฒ•์˜ ์ง„์›์ง€์ž…๋‹ˆ๋‹ค. ์ˆœ์„œ(read state โ†’ pick โ†’ implement โ†’ verify โ†’ commit โ†’ log)๋ฅผ ๋ชป๋ฐ•๊ณ , ์™ธ์žฅ ๋‘๋‡Œ ๊ฐฑ์‹  ์˜๋ฌด(progress.txt append)๋ฅผ ๊ฐ•์ œํ•˜๊ณ , ๋ฌด์—‡์ด "learning for future iterations" ์œผ๋กœ ๊ฐ€์น˜ ์žˆ๋Š”์ง€๋ฅผ ๊ฐ€๋ฅด์นฉ๋‹ˆ๋‹ค.

4๋‹จ๊ณ„("์ตœ์šฐ์„  passes:false ๊ณจ๋ผ๋ผ")์™€ 9๋‹จ๊ณ„("๋๋‚ฌ์œผ๋ฉด passes:true๋กœ ๊ฐฑ์‹ ํ•ด๋ผ")๋Š” ์œ„ PRD JSON๊ณผ ์ •ํ™•ํžˆ ๊ฐ™์€ ํ‚ค๋ฅผ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค. ์ง€๋Šฅ์€ ์ด ๋งˆํฌ๋‹ค์šด๊ณผ JSON ์•ˆ์— ๋ฐ•ํ˜€ ์žˆ๊ณ , ralph.sh๋Š” ๊ทธ์ € ๋‘˜์„ ๋น„๊ฒฐ์ •์ ์ธ LLM์— ๋งค๋ฒˆ ์ƒˆ๋กœ ํ˜๋ ค๋„ฃ๋Š” ๊ฒฐ์ •์  ์…ธ ๋ฃจํ”„ ์—ญํ• ๋งŒ ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฑธ ๋งค iteration์˜ ๋ฐฑ์ง€ ์ƒํƒœ AIํ•œํ…Œ ์ฃผ์ž…ํ•จ์œผ๋กœ์จ "๋ฉ์ฒญํ•˜์ง€๋งŒ ์ผ๊ด€๋˜๊ฒŒ ๋˜‘๊ฐ™์€ ์ผ์„ ์ž˜ ํ•˜๋Š”" Ralph๊ฐ€ ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค. OpenClaw๊ฐ€ ๋งŒ๋“  "soul document" ๋ผ๋Š” ๊ฐœ๋…๋„ ์ด๊ฒƒ์˜ ๋” ์ •๊ตํ•œ ๋ฒ„์ „์ž…๋‹ˆ๋‹ค โ€” ์—์ด์ „ํŠธ์˜ ์„ฑ๊ฒฉ, ํ–‰๋™ ์›์น™, ๊ธˆ์ง€ ์‚ฌํ•ญ์„ markdown ํ•œ ์žฅ์— ๋ฌถ์–ด ๋งค ์„ธ์…˜์— ์ฃผ์ž…ํ•˜๋Š” ๋ฐฉ์‹์ด์ฃ .

Flowchart of One Iteration

์Šคํฌ๋ฆฝํŠธ ์ž์ฒด๋Š” ์ด๋ ‡๊ฒŒ ๋‹จ์ˆœํ•˜์ง€๋งŒ, ์‹ค์ œ ํ•œ iteration ์•ˆ์—์„œ ์ผ์–ด๋‚˜๋Š” ์ผ์€ ์ธ๊ฐ„ ๊ฐœ๋ฐœ์ž์˜ ์ž‘์—… ํ๋ฆ„์„ ๊ฑฐ์˜ ๊ทธ๋Œ€๋กœ ๋ชจ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

flowchart TD
    Start([iteration ์‹œ์ž‘<br/>fresh AI]) --> Read["prd.json + progress.txt + git history ์ฝ๊ธฐ"]
    Read --> Branch{"feature ๋ธŒ๋žœ์น˜<br/>์กด์žฌ?"}
    Branch -->|no| Create["prd.json์˜ branchName์œผ๋กœ<br/>๋ธŒ๋žœ์น˜ ์ƒ์„ฑ"]
    Branch -->|yes| Pick
    Create --> Pick

    Pick["passes:false ์ธ ๊ฐ€์žฅ<br/>์šฐ์„ ์ˆœ์œ„ ๋†’์€ ๋‹จ์ผ ์Šคํ† ๋ฆฌ ์ฑ„ํƒ"] --> Implement["์ฝ”๋“œ ์ž‘์„ฑ/์ˆ˜์ •"]
    Implement --> QA["ํ’ˆ์งˆ ๊ฒ€์‚ฌ<br/>(typecheck / lint / test)"]
    QA -->|fail| LogFail["progress.txt์— ํ•™์Šต append<br/>(๋‹ค์Œ iter์—์„œ ์ฐธ๊ณ )"]
    LogFail --> Exit1([iteration ์ข…๋ฃŒ])

    QA -->|pass| Commit["git commit"]
    Commit --> UpdatePRD["prd.json ํ•ด๋‹น ์Šคํ† ๋ฆฌ<br/>passes:true ๋กœ ์—…๋ฐ์ดํŠธ"]
    UpdatePRD --> AppendProg["progress.txt ํ•™์Šต append"]
    AppendProg --> AllDone{"๋ชจ๋“  ์Šคํ† ๋ฆฌ<br/>passes:true?"}
    AllDone -->|yes| Promise["์ถœ๋ ฅ์—<br/>&lt;promise&gt;COMPLETE&lt;/promise&gt;"]
    AllDone -->|no| Exit2([iteration ์ข…๋ฃŒ])
    Promise --> Done([ralph.sh exit 0])

    style Start fill:#e3f2fd,stroke:#2196f3
    style Done fill:#e8f5e9,stroke:#4caf50
    style Promise fill:#fff9c4,stroke:#f57f17
    style LogFail fill:#ffebee,stroke:#e91e63

์ด ๋‹ค์ด์–ด๊ทธ๋žจ์˜ ์ง„๊ฐ€๋Š” ์ƒํƒœ๊ฐ€ ์–ด๋”” ์žˆ๋Š”๊ฐ€์— ์žˆ์Šต๋‹ˆ๋‹ค. AI๋Š” ๋งค๋ฒˆ ์ฃฝ๊ณ  ๋‹ค์‹œ ๋œจ์ง€๋งŒ, prd.json์˜ passes ํ”Œ๋ž˜๊ทธ, git commit history, append-only์ธ progress.txt, ๊ทธ๋ฆฌ๊ณ  ๋งค๋ฒˆ ์ƒˆ๋กœ ์ฃผ์ž…๋˜๋Š” CLAUDE.md โ€” ์ด ๋„ค ๊ฐ€์ง€๊ฐ€ ๋ˆ„์ ๋œ ์ง„ํ–‰ ์ƒํ™ฉ๊ณผ ํ–‰๋™ ๊ทœ์น™์„ ๋ณด์กดํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ iteration์˜ ๋ฐฑ์ง€ AI๋Š” ์ด๊ฑธ ์ฝ๊ณ  "๋‚ด๊ฐ€ ์–ด๋””๊นŒ์ง€ ํ–ˆ๊ณ , ๋ฌด์—‡์„ ์•Œ์•„๋ƒˆ๊ณ , ์–ด๋–ป๊ฒŒ ํ–‰๋™ํ•ด์•ผ ํ•˜๋Š”์ง€" ๋ฅผ ์—ญ์„ค๊ณ„ํ•ด์„œ ๊ทธ๋Œ€๋กœ ์ด์–ด์„œ ์ž‘์—…ํ•ฉ๋‹ˆ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ LLM์ด ์•„๋‹ˆ๋ผ ๋””์Šคํฌ์— ์žˆ๋‹ค๋Š” ๊ฒŒ ์ด๋Ÿฐ ์˜๋ฏธ์ž…๋‹ˆ๋‹ค.

Notes

์œ„ ํ๋ฆ„์„ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ ๊ตด๋ฆฌ๊ธฐ ์ „์— ๋‹ค์Œ ๋„ค ๊ฐ€์ง€๋Š” ์งš๊ณ  ๋„˜์–ด๊ฐ€์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ๊ถŒํ•œ ์šฐํšŒ ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ผœ์ ธ ์žˆ๋‹ค. ralph.sh๋Š” Amp์—๋Š” --dangerously-allow-all, Claude์—๋Š” --dangerously-skip-permissions๋ฅผ ๋ถ™์—ฌ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰ LLM์ด rm -rf ๊ฐ™์€ ๋ช…๋ น์„ ๋˜์ ธ๋„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฌป์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ปจํ…Œ์ด๋„ˆยทVMยทDevcontainer ๋“ฑ ๊ฒฉ๋ฆฌ ํ™˜๊ฒฝ์—์„œ ๋Œ๋ฆฌ๋Š” ๊ฒŒ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.
  • CI/ํ”ผ๋“œ๋ฐฑ ๋ฃจํ”„๊ฐ€ ์—†์œผ๋ฉด ๋ฌด์šฉ์ง€๋ฌผ์ด๋‹ค. Ralph๋Š” ๋งค ๋ฐ˜๋ณต๋งˆ๋‹ค ์ƒˆ ์ปจํ…์ŠคํŠธ๋กœ ๋“ค์–ด์˜ค๊ธฐ ๋•Œ๋ฌธ์—, ์ง์ „ ๋ฐ˜๋ณต์ด ์ž˜๋ชป ์ง  ์ฝ”๋“œ๋Š” typecheckยทํ…Œ์ŠคํŠธ๊ฐ€ ์žก์ง€ ๋ชปํ•˜๋ฉด ๋‹ค์Œ ๋ฐ˜๋ณต์œผ๋กœ ๊ทธ๋Œ€๋กœ ๋ˆ„์ ๋ฉ๋‹ˆ๋‹ค. README๋Š” "Ralph only works if there are feedback loops" ๋ผ๊ณ  ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค.
  • ํ•œ ์‚ฌ์ดํด์— ํ•œ ์Šคํ† ๋ฆฌ. PRD๋ฅผ ์ž˜๊ฒŒ ์ชผ๊ฐœ์•ผ ํ•˜๋Š” ์ด์œ ๋Š” ๊ฐ€๋…์„ฑ์ด ์•„๋‹ˆ๋ผ ์ปจํ…์ŠคํŠธ ์œˆ๋„์šฐ๊ฐ€ ์œ ํ•œํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ํ•œ ์Šคํ† ๋ฆฌ๊ฐ€ ํ•œ ๋ฐ˜๋ณต ์•ˆ์—์„œ ๋๋‚˜์ง€ ์•Š์œผ๋ฉด ๋‹ค์Œ ๋ฐ˜๋ณต์€ ๋ฏธ์™„์„ฑ ์ƒํƒœ์—์„œ ์‹œ์ž‘ํ•ด ๋” ๋ง๊ฐ€์ง€๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.
  • ๊ฒฐ์ œ์™€ ํ† ํฐ ์†Œ๋ชจ. ํ•œ ๋ฒˆ ๋Œ๋ฆฌ๋ฉด LLM CLI๋ฅผ ์ˆ˜์‹ญ ๋ฒˆ ์ƒˆ ํ”„๋กœ์„ธ์Šค๋กœ ํ˜ธ์ถœํ•˜๋ฏ€๋กœ API ์‚ฌ์šฉ๋Ÿ‰์ด ๋น ๋ฅด๊ฒŒ ์Œ“์ž…๋‹ˆ๋‹ค. MAX_ITERATIONS๋ฅผ ์ž‘๊ฒŒ ์žก๊ณ  prd.json์˜ ์ฒซ ์Šคํ† ๋ฆฌ๋ถ€ํ„ฐ ๊ฒ€์ฆํ•˜๋Š” ๊ฒŒ ์ข‹์Šต๋‹ˆ๋‹ค.

Claude Code Ralph Loop Plugin

Ralph ํŒจํ„ด์˜ ์ธ๊ธฐ์— ํŽธ์Šนํ•ด Anthropic์ด ๊ณต์‹ ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ ์ •์‹ ์ถœ์‹œํ•œ ๊ฒƒ์ด Ralph Loop ํ”Œ๋Ÿฌ๊ทธ์ธ์ž…๋‹ˆ๋‹ค. ๊ฐ™์€ ์‚ฌ์ƒ์„ Claude Code ์„ธ์…˜ ๋‚ด๋ถ€๋กœ ๊ฐ€์ ธ์™€, ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์…ธ ๋ฃจํ”„๋ฅผ ์งœ์ง€ ์•Š์•„๋„ ๊ฐ™์€ ์ฑ„ํŒ… ์ฐฝ ์•ˆ์—์„œ ์ž๊ธฐ์ฐธ์กฐ ๋ฃจํ”„๊ฐ€ ๋Œ๋„๋ก ํŒจํ‚ค์ง•ํ•œ ๊ฒฐ๊ณผ๋ฌผ์ž…๋‹ˆ๋‹ค. ์™ธ๋ถ€ ์…ธ ๋ฃจํ”„ ๋Œ€์‹  Claude Code์˜ Stop Hook์ด ์ข…๋ฃŒ๋ฅผ ๊ฐ€๋กœ์ฑ„ ๋™์ผ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋‹ค์Œ iteration์œผ๋กœ ๋˜๋Œ๋ ค ๋ณด๋‚ด๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

Installation

Claude Code์˜ ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ์€ ๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค๋ฅผ ๋“ฑ๋กํ•œ ๋’ค ๊ทธ ์•ˆ์˜ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์น˜ํ•˜๋Š” ๋‘ ๋‹จ๊ณ„ ํ๋ฆ„์ž…๋‹ˆ๋‹ค. ์ฒ˜์Œ ์‚ฌ์šฉ ์‹œ์ ์— ๋‹ค์Œ ๋‘ ์ค„์„ ์ฐจ๋ก€๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

/plugin marketplace add claude-plugins-official
/plugin install ralph-loop@claude-plugins-official

์„ค์น˜๊ฐ€ ๋๋‚˜๋ฉด ์Šฌ๋ž˜์‹œ ์ปค๋งจ๋“œ 3์ข…(/ralph-loop, /cancel-ralph, /help)๊ณผ Stop Hook(hooks/hooks.json โ†’ hooks/stop-hook.sh)์ด ์ž๋™์œผ๋กœ ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ ํŒŒ์ผ์€ ~/.claude/plugins/cache/claude-plugins-official/ralph-loop/1.0.0/ ๊ฒฝ๋กœ์— ์บ์‹œ๋ฉ๋‹ˆ๋‹ค.

๋ฃจํ”„๋Š” ์Šฌ๋ž˜์‹œ ์ปค๋งจ๋“œ ํ•œ ์ค„๋กœ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

/ralph-loop "Build a todo API" --completion-promise "DONE" --max-iterations 20

ํ”Œ๋ž˜๊ทธ๋Š” ๋‘ ๊ฐœ๋ฟ์ž…๋‹ˆ๋‹ค.

  • --max-iterations: ์•ˆ์ „ ์ƒํ•œ
  • --completion-promise: ์—์ด์ „ํŠธ ์‘๋‹ต์— ์ด ๋ฌธ์ž์—ด์ด ํฌํ•จ๋˜๋ฉด ๋ฃจํ”„ ์ข…๋ฃŒ. snarktank/ralph๊ฐ€ <promise>COMPLETE</promise>๋ฅผ ํ•ฉ์˜๋œ ํ† ํฐ์œผ๋กœ ์“ฐ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ํŒจํ„ด์ด์ง€๋งŒ, ํ˜ธ์ถœ๋งˆ๋‹ค ์ž์œ ๋กญ๊ฒŒ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค โ€” Stop Hook์€ <promise>X</promise> ํ˜•ํƒœ๊ฐ€ ์ •ํ™•ํžˆ ๋‚˜ํƒ€๋‚  ๋•Œ๋งŒ ๋ฃจํ”„๋ฅผ ๋๋ƒ…๋‹ˆ๋‹ค

--max-iterations๋ฅผ ์ƒ๋žตํ•˜๋ฉด ๊ธฐ๋ณธ๊ฐ’์ด ๋ฌด์ œํ•œ์ด๋ผ ๋‘ ์˜ต์…˜์„ ๋ชจ๋‘ ์ง€์ •ํ•˜๊ณ  ์‹œ์ž‘ํ•˜๋Š” ๊ฒŒ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค. ์ง„ํ–‰ ์ค‘์ธ ๋ฃจํ”„ ์ค‘๋‹จ์€ ๊ฐ™์€ ์„ธ์…˜์—์„œ /cancel-ralph๋กœ ํ•ฉ๋‹ˆ๋‹ค โ€” ๋‚ด๋ถ€์ ์œผ๋กœ .claude/ralph-loop.local.md ์ƒํƒœ ํŒŒ์ผ์„ ์‚ญ์ œํ•˜๋Š” ๊ฒƒ๋ฟ์ด๋ฉฐ, ๋‹ค์Œ Stop ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํ›…์ด "ํ™œ์„ฑ ๋ฃจํ”„ ์—†์Œ" ์œผ๋กœ ํŒ๋‹จํ•ด ์ •์ƒ ์ข…๋ฃŒ๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

How Does It Work?

์›๋ณธ Ralph๊ฐ€ ์™ธ๋ถ€ bash ์Šคํฌ๋ฆฝํŠธ๋กœ ์ž์‹ ํ”„๋กœ์„ธ์Šค๋ฅผ ๋งค๋ฒˆ ์ƒˆ๋กœ ๋„์šฐ๋Š” ๊ฒƒ๊ณผ ๋‹ฌ๋ฆฌ, ๊ณต์‹ ํ”Œ๋Ÿฌ๊ทธ์ธ์€ Claude Code ๋‚ด๋ถ€์˜ Stop Hook ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ด์šฉํ•ด ๊ฐ™์€ ์„ธ์…˜ ์•ˆ์—์„œ ๋ฃจํ”„๋ฅผ ํ‰๋‚ด๋ƒ…๋‹ˆ๋‹ค.

flowchart TD
    User["/ralph-loop ํ˜ธ์ถœ"] --> Session["Claude Code ์„ธ์…˜ ์‹œ์ž‘<br/>(๋‹จ์ผ ์„ธ์…˜, ๋‹จ์ผ ์ปจํ…์ŠคํŠธ)"]
    Session --> Agent["์—์ด์ „ํŠธ๊ฐ€ ์ž‘์—… ์ˆ˜ํ–‰"]
    Agent --> StopAttempt["์—์ด์ „ํŠธ๊ฐ€ ์ •์ƒ ์ข…๋ฃŒ(Stop) ์‹œ๋„"]
    StopAttempt --> Hook["plugin์˜ Stop Hook ๊ฐ€๋กœ์ฑ”"]
    Hook --> Parse["์‘๋‹ต ํ…์ŠคํŠธ ํŒŒ์‹ฑ"]
    Parse --> Check{"completion-promise<br/>๋ฌธ์ž์—ด ํฌํ•จ?"}
    Check -->|yes| Done([์ •์ƒ ์ข…๋ฃŒ])
    Check -->|no| Exit2["exit code 2 ๋ฐ˜ํ™˜<br/>= ์ข…๋ฃŒ ๊ฑฐ๋ถ€"]
    Exit2 --> ReFeed["์›๋ณธ ํ”„๋กฌํ”„ํŠธ<br/>๊ฐ™์€ ์„ธ์…˜์— ์žฌ์ฃผ์ž…"]
    ReFeed --> Agent
    Agent -.->|์ปจํ…์ŠคํŠธ ๋ˆ„์ | ContextGrow[("์ด์ „ ์‹œ๋„<br/>+ ์—๋Ÿฌ ๋กœ๊ทธ<br/>+ ๋””๋ฒ„๊น… ์ถœ๋ ฅ<br/>๋ชจ๋‘ ๊ฐ™์€ ์ปจํ…์ŠคํŠธ์— ์Œ“์ž„")]

    style ContextGrow fill:#ffebee,stroke:#e91e63
    style Exit2 fill:#fff9c4
    style Done fill:#e8f5e9

README๋Š” ํ•œ ๋ฌธ์žฅ์œผ๋กœ ๋ชป ๋ฐ•์Šต๋‹ˆ๋‹ค โ€” "The loop happens inside your current session - you don't need external bash loops. The Stop hook in hooks/stop-hook.sh creates the self-referential feedback loop by blocking normal session exit."

์•ˆ์ „์žฅ์น˜ ๋ฉด์—์„œ๋Š” ๋™์‹œ์— ์—ฌ๋Ÿฌ Claude Code ์„ธ์…˜์„ ๋„์›Œ๋‘” ์ƒํ™ฉ์—์„œ๋„ ๋‹ค๋ฅธ ์„ธ์…˜์˜ Stop Hook์ด ์ž˜๋ชป๋œ ์ƒํƒœ ํŒŒ์ผ์„ ๊ฑด๋“œ๋ฆฌ์ง€ ๋ชปํ•˜๋„๋ก session_id๋ฅผ ๋น„๊ตํ•˜๋Š” ๊ฐ€๋“œ๊ฐ€ ๋“ค์–ด ์žˆ์–ด, raw Ralph์—๋Š” ์—†๋Š” ๋ฉ€ํ‹ฐ ์„ธ์…˜ ๊ฒฉ๋ฆฌ๊ฐ€ ๊ธฐ๋ณธ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

Single Context

๋ฌธ์ œ๋Š” ๋งค iteration์˜ ๋ชจ๋“  ์ž‘์—… ๋‚ด์—ญ์ด ๊ฐ™์€ ์ปจํ…์ŠคํŠธ ์œˆ๋„์šฐ์— ๋ฌดํ•œ์ • ๋ˆ„์ ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋งค๋ฒˆ ํ”„๋กœ์„ธ์Šค๋ฅผ ๊นจ๋—ํ•˜๊ฒŒ ์ฃฝ์ด๊ณ  ์ƒˆ๋กœ ๋„์›Œ์„œ fresh context๋ฅผ ํ™•๋ณดํ•˜๋˜ snarktank/ralph ์ฒ ํ•™๊ณผ ์ •๋ฐ˜๋Œ€์ž…๋‹ˆ๋‹ค. aihero.dev์˜ ์ธก์ •์— ๋”ฐ๋ฅด๋ฉด:

  • iter1: ์ปจํ…์ŠคํŠธ ~20% ์‚ฌ์šฉ
  • iter2: ~35%
  • iter3: ~50% โ€” ์ด๋ฏธ ์ ˆ๋ฐ˜์ด ๊ณผ๊ฑฐ์˜ ์‹คํŒจ ๋กœ๊ทธ์™€ cruft
  • iter4 ์ดํ›„: ๋ชจ๋ธ์ด ๋น ๋ฅด๊ฒŒ Dumb Zone์œผ๋กœ ์ง„์ž…, ํ™˜๊ฐ๊ณผ ๊ฐ™์€ ์‹ค์ˆ˜ ๋ฐ˜๋ณต ํญ์ฆ

๋ฃจํ”„๊ฐ€ ๊ธธ์–ด์งˆ์ˆ˜๋ก ํ† ํฐ ๋น„์šฉ์€ ์ง์„ ์ ์œผ๋กœ ๋Š˜์–ด๋‚˜๋Š”๋ฐ ๊ฒฐ๊ณผ ํ’ˆ์งˆ์€ ๊ฑฐ๊พธ๋กœ ๋–จ์–ด์ง‘๋‹ˆ๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ Claude Code ๋‚ด๋ถ€์˜ ์ปจํ…์ŠคํŠธ ์ž๋™ ์••์ถ•(compaction)๊ณผ Stop Hook์ด ์ถฉ๋Œํ•˜๋Š” ์ผ€์ด์Šค๊ฐ€ ๋ณด๊ณ ๋˜๊ณ  ์žˆ์–ด, ์‚ฌ์šฉ์ž๊ฐ€ ์ค‘๊ฐ„์— ๊ฐœ์ž…ํ•ด ์ˆ˜๋™์œผ๋กœ ์••์ถ•ํ•ด์ค˜์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด ๋นˆ๋ฒˆํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฐ ์ด์œ ๋กœ aihero.dev ๊ฐ™์€ ๋น„ํŒ์  ๋ฆฌ๋ทฐ๋Š” "๊ณต์‹ ํ”Œ๋Ÿฌ๊ทธ์ธ ๋Œ€์‹  ์›๋ณธ bash ํŒจํ„ด์„ ์“ฐ๋ผ" ๊ณ  ๊ถŒํ•ฉ๋‹ˆ๋‹ค. ์„ค์น˜๋Š” ํ•œ ์ค„๋กœ ํŽธํ•˜์ง€๋งŒ ๊ทธ ํŽธ์˜์„ฑ์ด ๊ณง ์•„ํ‚คํ…์ฒ˜์  ํ•œ๊ณ„๋กœ ์ง๊ฒฐ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๊ธ€ ํ•œ ํŽธ ์“ฐ๋Š” ์ •๋„์˜ task์ฒ˜๋Ÿผ 5~10 iter ์•ˆ์— ์ถฉ๋ถ„ํžˆ ๋๋‚˜๋Š” ์ž‘์—…์ด๋ผ๋ฉด Dumb Zone์— ๋„๋‹ฌํ•˜๊ธฐ ์ „์— ๋งˆ๋ฌด๋ฆฌ๋˜๋ฏ€๋กœ ์‹ค์šฉ์ ์œผ๋กœ๋Š” ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค โ€” ๋ฉฐ์น ์งœ๋ฆฌ ์ฝ”๋“œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด๋ผ๋ฉด ์™ธ๋ถ€ ์…ธ ๋ฃจํ”„ํ˜• ๊ตฌํ˜„์ด ๋” ์˜ˆ์ธก ๊ฐ€๋Šฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

OpenAI Codex Goal Command

๋กœ์ปฌ/์ปจํ…Œ์ด๋„ˆ ๊ธฐ๋ฐ˜์˜ Ralph ๊ณ„์—ด ๋„๊ตฌ๋“ค๊ณผ ๋‹ค๋ฅธ ๊ฒฐ๋กœ ๋“ฑ์žฅํ•œ ๊ฒƒ์ด OpenAI Codex CLI์˜ /goal ๋ช…๋ น์–ด์ž…๋‹ˆ๋‹ค. 2026๋…„ 4์›” v0.128.0์— ์ถ”๊ฐ€๋˜์—ˆ๊ณ , ์ž‘์„ฑ ์‹œ์ ์—๋Š” goals feature flag๊ฐ€ under development๋กœ ๋…ธ์ถœ๋œ ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค. ๊ณต์‹ ๋ฌธ์„œ๊ฐ€ ์•„์ง ๋”ฐ๋ผ์˜ค๋Š” ์ค‘์ด๋ฏ€๋กœ, ์—ฌ๊ธฐ์„œ๋Š” ๋‹จ์ˆœํ•œ ์ž์œจ ๋ฃจํ”„๋ฅผ ๋„˜์–ด Codex ์„ธ์…˜ ์•ˆ์—์„œ ์žฅ๊ธฐ ๋ชฉํ‘œ๋ฅผ ๊ณ„์† ์ถ”์ ํ•˜๋Š” lifecycle ๊ธฐ๋Šฅ์œผ๋กœ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

snarktank/ralph๊ฐ€ ์™ธ๋ถ€ bash ๋ฃจํ”„๋กœ ๋งค๋ฒˆ ์ƒˆ LLM ํ”„๋กœ์„ธ์Šค๋ฅผ ๋„์šฐ๊ณ  Claude ralph-loop๊ฐ€ ๊ฐ™์€ ์„ธ์…˜ ์•ˆ์—์„œ Stop Hook์œผ๋กœ ์ž๊ธฐ์ฐธ์กฐ ๋ฃจํ”„๋ฅผ ๋งŒ๋“ ๋‹ค๋ฉด, /goal์€ ํ•˜๋‚˜์˜ Codex thread ๋‚ด๋ถ€์—์„œ ๋งค ํ„ด ์ข…๋ฃŒ ์งํ›„ ์‹œ์Šคํ…œ์ด ์ž๋™์œผ๋กœ audit ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฃผ์ž…ํ•ด ๋ชจ๋ธ ์Šค์Šค๋กœ "์ •๋ง ๋‹ค ๋๋‚ฌ๋Š”๊ฐ€" ๋ฅผ ํŒ๋‹จํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค.

Activation

/goal์€ 0.128.0 ์ดํ›„์˜ Codex CLI์— ๋“ค์–ด์˜จ ๊ธฐ๋Šฅ์ด์ง€๋งŒ ๊ธฐ๋ณธ ํ™œ์„ฑ ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋ผ Feature::Goals ํ”Œ๋ž˜๊ทธ๋กœ ๊ฒŒ์ดํŠธ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ํ† ๊ธ€ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‘ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค.

# ~/.codex/config.toml
[features]
goals = true
# ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๋ช…๋ น ํ•œ ์ค„๋กœ ์ ์šฉ
codex features enable goals
How to Use

ํ™œ์„ฑํ™” ์ดํ›„์˜ ํ˜ธ์ถœ ๋ฌธ๋ฒ•์€ ์Šฌ๋ž˜์‹œ ์ปค๋งจ๋“œ ํ•œ ์ค„์ž…๋‹ˆ๋‹ค. ์†Œ์Šค๊ฐ€ ์ง์ ‘ ์ •์˜ํ•˜๋Š” ์‚ฌ์šฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Usage: /goal <objective>
Example: /goal improve benchmark coverage

๋ฃจํ”„๊ฐ€ ์‹œ์ž‘๋œ ๋’ค์—๋Š” ๊ฐ™์€ ์Šฌ๋ž˜์‹œ ์ปค๋งจ๋“œ ํŒจ๋ฐ€๋ฆฌ๋กœ ์ƒํƒœ๋ฅผ ์กฐ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

/goal pause
/goal resume
/goal clear

๋‚ด๋ถ€ ThreadGoalStatus enum์€ ์ •ํ™•ํžˆ 4๊ฐœ ์ƒํƒœ(Active / Paused / BudgetLimited / Complete)์ด๋ฉฐ, ์œ„ ์Šฌ๋ž˜์‹œ ์ปค๋งจ๋“œ์™€ ์ž๋™ ์ „์ด๊ฐ€ ์ด enum์„ ๊ฐฑ์‹ ํ•ฉ๋‹ˆ๋‹ค.

System Prompt Injection

/goal์˜ ํ•ต์‹ฌ์€ ๋งค ํ„ด ์ข…๋ฃŒ ์‹œ ๋ฐฑ์—”๋“œ๊ฐ€ ์ž๋™์œผ๋กœ ์ฃผ์ž…ํ•˜๋Š” ๋‘ ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ์ž…๋‹ˆ๋‹ค.

ํ…œํ”Œ๋ฆฟ ์—ญํ• 
goals/continuation.md ์ด์ „ ํ–‰๋™ ๊ฒฐ๊ณผ + ๋””๋ ‰ํ† ๋ฆฌ ์ƒํƒœ ๋ถ„์„ โ†’ ๋ชฉํ‘œ ๋‹ฌ์„ฑ ์—ฌ๋ถ€ ์ž๊ฐ€ ํ‰๊ฐ€ โ†’ ๋‹ค์Œ ๋‹จ๊ณ„ ๊ณ„ํš
goals/budget_limit.md ์‚ฌ์šฉ์ž ์‚ฌ์ „ ์ •์˜ ํ† ํฐ ์˜ˆ์‚ฐ ์ž„๊ณ„์น˜ ๋„๋‹ฌ ์‹œ ๋ฐœ๋™ โ†’ ๋ชฉํ‘œ ๋ฏธ์™„๋ฃŒ๋ผ๋„ ์ฆ‰์‹œ ๋ชจ๋“  ํ›„์† ๋ฃจํ”„ ์ฐจ๋‹จ, BudgetLimited ์ƒํƒœ๋กœ ๋™๊ฒฐ

continuation.md๊ฐ€ ๋ชป ๋ฐ•๋Š” ํ•ต์‹ฌ ํ•œ ์ค„์€ ๋‹ค์Œ์ž…๋‹ˆ๋‹ค.

"Treat uncertainty as not achieved; do more verification or continue the work."

๊ฐ™์€ ํ…œํ”Œ๋ฆฟ์ด ์ด์–ด์„œ ๊ฐ•์กฐํ•˜๋Š” ๋˜ ํ•œ ์ค„์€ update_goal ๋„๊ตฌ์˜ ํ˜ธ์ถœ ๊ถŒํ•œ์ด audit ํ†ต๊ณผ์—๋งŒ ์˜์กดํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์ž…๋‹ˆ๋‹ค.

"Do not call update_goal unless the goal is complete. Do not mark a goal complete merely because the budget is nearly exhausted or because you are stopping work."

์ข…๋ฃŒ๋Š” ๋ชจ๋ธ์ด update_goal(status="complete") ๋„๊ตฌ๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ์ผ์–ด๋‚˜๋ฉฐ, ๊ทธ ๋„๊ตฌ์˜ ๋จธ๋ฆฌ ์ฃผ์„์€ "create_goal starts an active objective, while update_goal can only mark the existing goal complete" ๋ผ๊ณ  ์˜๋„๋ฅผ ๋ชป ๋ฐ•์Šต๋‹ˆ๋‹ค โ€” ๋ฃจํ”„๋ฅผ ๋๋‚ผ ๊ถŒํ•œ ์ž์ฒด๊ฐ€ ์˜๋„์ ์œผ๋กœ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ ํ•˜๋‚˜์˜ ์ข…๋ฃŒ ๊ฒฝ๋กœ๋Š” ํ† ํฐ ์˜ˆ์‚ฐ์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ create_goal์— token_budget ์ธ์ž๋ฅผ ํ•จ๊ป˜ ๋„˜๊ธฐ๋ฉด ํ† ํฐ์ด ์†Œ์ง„๋˜๋Š” ์‹œ์ ์— BudgetLimited ์ƒํƒœ๋กœ ๊ฐ•์ œ ์ „์ด๋˜๊ณ , goals/budget_limit.md ํ…œํ”Œ๋ฆฟ์ด 1ํšŒ ์ฃผ์ž…๋˜์–ด "do not start new substantive work for this goal. Wrap up this turn soon" ๋ผ๊ณ  ๋ชจ๋ธ์—๊ฒŒ ๋งˆ๋ฌด๋ฆฌ๋ฅผ ์ง€์‹œํ•ฉ๋‹ˆ๋‹ค. ์žฅ๊ธฐ ์‹คํ–‰ ์ž์œจ ์—์ด์ „ํŠธ์˜ ๊ฐ€์žฅ ํฐ ๊ณตํฌ๋Š” "๊ต์ฐฉ ์ƒํƒœ์— ๋น ์ ธ์„œ ์ฒœ๋ฌธํ•™์  ํ† ํฐ ์ฒญ๊ตฌ์„œ๋ฅผ ๋งŒ๋“ค์–ด๋‚ด๋Š”" ์‹œ๋‚˜๋ฆฌ์˜ค์ธ๋ฐ, ์ด ํ…œํ”Œ๋ฆฟ์ด ๊ฐ•์ œ ์•ˆ์ „์žฅ์น˜ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

Meta Prompting

/goal์„ ์‹ค์ œ๋กœ ์ž˜ ๋Œ๋ฆฌ๋Š” ํ•ต์‹ฌ ๊ธฐ๋ฒ•์€ ๋ฉ”ํƒ€ ํ”„๋กฌํ”„ํŒ…(Meta Prompting) ์ž…๋‹ˆ๋‹ค. Aditya Bawankule์˜ ๊ฐ€์ด๋“œ๋Š” ์งง์€ ๋ชฉํ‘œ๊ฐ€ ์˜๋„์ ์œผ๋กœ ์‹คํŒจํ•˜๊ธฐ ์‰ฝ๋‹ค๊ณ  ์ง€์ ํ•ฉ๋‹ˆ๋‹ค.

"the agent fills in the blanks itself, and the blanks compound" โ€” "We're talking hours of continuous work."

์ธ๊ฐ„์ด ์ง์ ‘ ์ž„๋ฌด ์ง€์‹œ์„œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋Œ€์‹ , Claude๋‚˜ ChatGPT ๊ฐ™์€ ๋‹ค๋ฅธ ๊ฐ•๋ ฅํ•œ ์ถ”๋ก  ๋ชจ๋ธ์—๊ฒŒ "์žฅ๊ธฐ ์‹คํ–‰ ์—์ด์ „ํŠธ๊ฐ€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์™„๋ฒฝํ•œ ์ž„๋ฌด ์ง€์‹œ์„œ๋ฅผ ์ž‘์„ฑํ•˜๋ผ" ๊ณ  ์œ„์ž„ํ•ฉ๋‹ˆ๋‹ค. ์ข‹์€ /goal ํ”„๋กฌํ”„ํŠธ๊ฐ€ ๊ฐ€์ ธ์•ผ ํ•  5๊ฐ€์ง€๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. Scope โ€” ์˜ํ–ฅ ๋ฒ”์œ„, ์–ด๋–ค ํŒŒ์ผ/๋ชจ๋“ˆ
  2. Constraints โ€” ์กฐ์ž‘ํ•ด์„  ์•ˆ ๋˜๋Š” ๋ถ€๋ถ„, ๊ธฐ์ˆ /์Šคํƒ€์ผ ์ œ์•ฝ
  3. Files in play โ€” ๊ตฌ์ฒด์  ํŒŒ์ผ ๊ฒฝ๋กœ ๋ช…์‹œ
  4. Definition of Done โ€” ๋…ผ๋ž€ ์—†๋Š” ์™„๋ฃŒ ์ •์˜
  5. Validation โ€” ์ž๋™ํ™”๋œ ์ฒดํฌ๋ฆฌ์ŠคํŠธ, ํ…Œ์ŠคํŠธ, lint, ์‚ฌ๋žŒ ๋ฆฌ๋ทฐ

ํ•œ ์ค„์งœ๋ฆฌ ๋ชจํ˜ธํ•œ ์ง€์‹œ("๋ฆฌํŒฉํ„ฐ ์ข€ ํ•ด์ค˜")๋กœ /goal์„ ๋„์šฐ๋ฉด ๋ฉฐ์น ์น˜ ์ปดํ“จํŒ…์„ ํƒœ์›Œ๋จน๊ณ  ์—‰๋šฑํ•œ PR์ด ๋‚˜์˜ต๋‹ˆ๋‹ค. ๋ฐ˜๋Œ€๋กœ ์œ„ 5์š”์†Œ๊ฐ€ ์ด˜์ด˜ํžˆ ์ •์˜๋œ ๋ฉ”ํƒ€ ํ”„๋กฌํ”„ํŠธ๊ฐ€ ๋“ค์–ด๊ฐ€๋ฉด, ์ธ๊ฐ„ ๊ฐ๋… ์—†์ด ๋ฐค์ƒˆ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ๋๋‚ด๊ณ  ์•„์นจ์— PR์„ ์ œ์ถœํ•ฉ๋‹ˆ๋‹ค.

์‹ค์ œ ๋ฉ”ํƒ€ ํ”„๋กฌํ”„ํŠธ๋Š” ๋Œ€๋žต ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค.

/goal

## Scope
- packages/api/src/handlers ํ•˜์œ„ ๋ชจ๋“  Express ๋ผ์šฐํŠธ๋ฅผ Hono ๋ผ์šฐํ„ฐ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜
- ์™ธ๋ถ€ contract(URL, request/response ํ˜•ํƒœ)๋Š” ๋ณ€๊ฒฝ ๊ธˆ์ง€

## Constraints
- packages/web, packages/shared ๋Š” ์ ˆ๋Œ€ ์ˆ˜์ • ๊ธˆ์ง€
- ์˜์กด์„ฑ ์ถ”๊ฐ€ ๊ธˆ์ง€ (์ด๋ฏธ hono@4.x ๊ฐ€ package.json์— ์žˆ์Œ)
- ์ƒˆ ๋ฏธ๋“ค์›จ์–ด ๋ฐœ๋ช… ๊ธˆ์ง€, ๊ธฐ์กด packages/api/middleware/* ๋งŒ ํ™œ์šฉ

## Files in play
- packages/api/src/handlers/**/*.ts
- packages/api/src/server.ts (๋ผ์šฐํ„ฐ ๋งˆ์šดํŠธ ์ง€์ )
- packages/api/test/**/*.spec.ts (๋ชจ๋“  ๊ธฐ์กด ํ…Œ์ŠคํŠธ๋Š” ๊ทธ๋Œ€๋กœ ํ†ต๊ณผํ•ด์•ผ ํ•จ)

## Definition of Done
- pnpm --filter api test ํ†ต๊ณผ (1๊ฐœ๋„ ๋น ์ง์—†์ด)
- pnpm --filter api typecheck 0 error
- pnpm --filter api lint 0 warning
- ์ˆ˜์ •๋œ ํŒŒ์ผ์— `import { Router } from 'express'` ๊ฐ€ 0๊ฑด

## Validation
1. ์œ„ 4๊ฐœ ๋ช…๋ น ๋ชจ๋‘ 0์œผ๋กœ ์ข…๋ฃŒ
2. git diff ๊ฐ€ packages/api/ ๋งŒ ๊ฑด๋“œ๋ฆผ์„ ํ™•์ธ
3. ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ (test/integration/api.spec.ts) ์˜ ๋ชจ๋“  ์ผ€์ด์Šค ํ†ต๊ณผ

์ด๋Ÿฐ ํ”„๋กฌํ”„ํŠธ๊ฐ€ ๋“ค์–ด๊ฐ€๋ฉด Codex๋Š” continuation.md๋ฅผ ๋งค ํ„ด ์ž๊ฐ€๊ฒ€์ฆํ•˜๋ฉด์„œ "5๋ฒˆ validation ์ค‘ 3๋ฒˆ์ด ์•„์ง ์•ˆ ๋๋„ค, ๊ณ„์†ํ•ด์•ผ์ง€"์™€ ๊ฐ™์€ ํŒ๋‹จ์„ ์Šค์Šค๋กœ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ† ํฐ ์˜ˆ์‚ฐ์ด ๋‹คํ•˜๋ฉด budget_limit.md๊ฐ€ ๊ฐ•์ œ ์ข…๋ฃŒ์‹œํ‚ค๋ฏ€๋กœ ๋น„์šฉ๋„ ํ†ต์ œ๋ฉ๋‹ˆ๋‹ค.

Goal vs. Ralph

์ข…๋ฃŒ ์ „๋žต ํ•œ ์ค„ ๋น„๊ต์ž…๋‹ˆ๋‹ค. snarktank/ralph๋Š” ๋งค ๋ฐ˜๋ณต๋งˆ๋‹ค ์ƒˆ Claude ํ”„๋กœ์„ธ์Šค๋ฅผ ๋„์›Œ(์ฆ‰ ์ปจํ…์ŠคํŠธ๊ฐ€ ํ•ญ์ƒ ๊นจ๋—ํ•˜๊ฒŒ ์‹œ์ž‘) ์ถœ๋ ฅ์— ํ•ฉ์˜๋œ ํ† ํฐ <promise>COMPLETE</promise>์ด ๋“ฑ์žฅํ•˜๋Š”์ง€ grep๋กœ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค โ€” ์ข…๋ฃŒ ํŒ์ •์˜ ๊ถŒํ•œ์ด ์™ธ๋ถ€ ์…ธ ์Šคํฌ๋ฆฝํŠธ์— ์žˆ์Šต๋‹ˆ๋‹ค.

Codex /goal์€ ๋ฐ˜๋Œ€๋กœ ํ•œ thread ์•ˆ์—์„œ ์ปจํ…์ŠคํŠธ๋ฅผ ๋ˆ„์ ์‹œํ‚ค๋˜ ๋งค ํ„ด ๋๋งˆ๋‹ค ์‹œ์Šคํ…œ์ด audit ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฃผ์ž…ํ•ด ๋ชจ๋ธ ์Šค์Šค๋กœ "๋๋‚ฌ๋Š”๊ฐ€" ๋ฅผ ๊ฒฐ์ •ํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค โ€” ์ข…๋ฃŒ ํŒ์ •์ด ๋ชจ๋ธ์˜ ๋„๊ตฌ ํ˜ธ์ถœ(update_goal(status="complete"))์— ์žˆ๊ณ , ๊ทธ ๋„๊ตฌ๋Š” audit์ด ํ†ต๊ณผํ–ˆ์„ ๋•Œ๋งŒ ํ˜ธ์ถœํ•˜๋ผ๋Š” ์ž์—ฐ์–ด ๊ณ„์•ฝ์œผ๋กœ ๋ณดํ˜ธ๋ฉ๋‹ˆ๋‹ค.

Comparison

์—ฌ๊ธฐ๊นŒ์ง€ ๋ณธ ๋„๊ตฌ๋“ค์„ ํ•œ ํ‘œ์— ์ •๋ฆฌํ•˜๋ฉด ์ฐจ์ด๊ฐ€ ๋˜๋ ทํ•ด์ง‘๋‹ˆ๋‹ค. ์ข…๋ฃŒ ํŒ์ •์˜ ๊ถŒํ•œ์ด ์–ด๋”” ์žˆ๋Š”๊ฐ€๊ฐ€ ๊ฐ€์žฅ ํฐ ๋ถ„๊ธฐ์ ์ž…๋‹ˆ๋‹ค.

๋น„๊ต ์ถ• snarktank/ralph Claude Plugin Ralph Loop Codex /goal
๊ณ„ํ†ต PRD ๊ธฐ๋ฐ˜ ์™ธ๋ถ€ Bash ๋ฃจํ”„ Anthropic ๊ณต์‹ Claude Code ํ”Œ๋Ÿฌ๊ทธ์ธ OpenAI Codex ๋ชฉํ‘œ lifecycle
๊ตฌ๋™ ํ™˜๊ฒฝ ๋กœ์ปฌ Git repo + bash Claude Code ๋‹จ์ผ ์„ธ์…˜ ๋‚ด๋ถ€ Codex CLI ์„ธ์…˜ / ์‹คํ–‰ ํ™˜๊ฒฝ
ํŠธ๋ฆฌ๊ฑฐ ./scripts/ralph/ralph.sh --tool claude N ์Šฌ๋ž˜์‹œ /ralph-loop "<prompt>" ์Šฌ๋ž˜์‹œ /goal <objective>
๋ฃจํ”„ ์ œ์–ด ์™ธ๋ถ€ for ๋ฃจํ”„, ์ž์‹ ํ”„๋กœ์„ธ์Šค ๊ฐ•์ œ ๋ฆฌ์…‹ Stop Hook + exit code 2 (์„ธ์…˜ ๊ฐ€๋กœ์ฑ„๊ธฐ) ๋งค ํ„ด continuation.md / budget_limit.md ์ž๋™ ์ฃผ์ž…
์ปจํ…์ŠคํŠธ ๋ชจ๋ธ Fresh Context (๋งค iter ๋ฐฑ์ง€) Single Context ๋ˆ„์  (Dumb Zone ์œ„ํ—˜) Persistent Goal (๊ธด ์œˆ๋„์šฐ + self-audit)
์ƒํƒœ ๋ณด์กด git + prd.json + progress.txt ์„ธ์…˜ ๋‚ด๋ถ€ ์ปจํ…์ŠคํŠธ + ์ƒํƒœ ํŒŒ์ผ thread goal state + token/time accounting
์ข…๋ฃŒ ์กฐ๊ฑด <promise>COMPLETE</promise> grep --completion-promise ๋งค์นญ ๋˜๋Š” max-iterations update_goal(status="complete") ํ˜ธ์ถœ ๋˜๋Š” ํ† ํฐ ์˜ˆ์‚ฐ ํ•œ๊ณ„
์ข…๋ฃŒ ํŒ์ • ๊ถŒํ•œ ์™ธ๋ถ€ ์…ธ ์„ธ์…˜ ๋‚ด Stop Hook ๋ชจ๋ธ ์ž์‹ ์˜ ๋„๊ตฌ ํ˜ธ์ถœ
์•ˆ์ „์žฅ์น˜ max iterations, CI ์Šคํฌ๋ฆฝํŠธ max-iterations ํ† ํฐ ์˜ˆ์‚ฐ ๊ฐ•์ œ ์ค‘๋‹จ, ์‹คํ–‰ ํ™˜๊ฒฝ ๊ฒฉ๋ฆฌ
์ž˜ ๋งž๋Š” ์ž‘์—… PRD๋กœ ์ชผ๊ฐ  ๋‹ค๋‹จ๊ณ„ ์ •ํ˜• ํ”„๋กœ์ ํŠธ ์ž‘์€ ๋ฐ˜๋ณต ์ˆ˜์ •, ๋ช…ํ™•ํ•œ ์„ฑ๊ณต ๋ฌธ์ž์—ด ๋ฉฐ์น ์งœ๋ฆฌ ๋น„๋™๊ธฐ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜, ์ธํ”„๋ผ ์ „ํ™˜
์•ฝ์  ๋ชจ๋‹ˆํ„ฐ๋ง UI ๋ถ€์กฑ, ํ…์ŠคํŠธ ๊ฐ€์‹œ์„ฑ๋งŒ Dumb Zone ์กฐ๊ธฐ ์ง„์ž… โ€” ์ปจํ…์ŠคํŠธ ๋ˆ„์  ๋ฉ”ํƒ€ํ”„๋กฌํ”„ํŒ… ์ž‘์„ฑ ๋Šฅ๋ ฅ ์š”๊ตฌ, ๊ณต์‹ docs ๋ฏธ๋น„

Conclusion

Ralph๋ฅ˜ ๋ฃจํ”„๋ฅผ ์‹ค์ œ๋กœ ๋Œ๋ฆด ๋•Œ ์ œ์ผ ์ค‘์š”ํ•œ ๊ฒƒ์€ "๋ฐ˜๋ณต" ์ด ์•„๋‹ˆ๋ผ "์ข…๋ฃŒ" ์ž…๋‹ˆ๋‹ค. ๋ฌดํ•œ ๋ฃจํ”„๋Š” ๋ˆ„๊ตฌ๋‚˜ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์–ด๋ ค์šด ๊ฒƒ์€ ๋๋‚ฌ๋‹ค๋Š” ์ฆ๊ฑฐ๋ฅผ ์ •์˜ํ•˜๋Š” ์ผ์ž…๋‹ˆ๋‹ค. ์ข‹์€ ์ข…๋ฃŒ ์กฐ๊ฑด์€ ์ด๋Ÿฐ ๋ชจ์–‘์ž…๋‹ˆ๋‹ค.

  • pnpm test๊ฐ€ ํ†ต๊ณผํ•œ๋‹ค
  • pnpm typecheck๊ฐ€ ํ†ต๊ณผํ•œ๋‹ค
  • ํŠน์ • API ์‘๋‹ต์ด fixture์™€ ์ผ์น˜ํ•œ๋‹ค
  • Playwright๊ฐ€ ์ฃผ์š” UI ํ”Œ๋กœ์šฐ๋ฅผ ํ†ต๊ณผํ•œ๋‹ค
  • prd.json์˜ ๋ชจ๋“  story๊ฐ€ passes=true๋‹ค
  • ์ตœ์ข… ๋ฌธ์„œ๊ฐ€ ์ง€์ •๋œ ํŒŒ์ผ ๊ฒฝ๋กœ์— ์กด์žฌํ•œ๋‹ค

๋‚˜์œ ์ข…๋ฃŒ ์กฐ๊ฑด์€ ์ด๋Ÿฐ ๋ชจ์–‘์ž…๋‹ˆ๋‹ค.

  • "๊ดœ์ฐฎ์•„์งˆ ๋•Œ๊นŒ์ง€"
  • "์ตœ๋Œ€ํ•œ ์ข‹๊ฒŒ"
  • "์•Œ์•„์„œ ์™„์„ฑ"
  • "์ „์ฒด๋ฅผ ๋ฆฌํŒฉํ„ฐ๋ง"

์—์ด์ „ํŠธ ๋ฃจํ”„๋Š” ๋ชฉํ‘œ๊ฐ€ ์• ๋งคํ•˜๋ฉด ๋น„์šฉ๊ณผ ์‹œ๊ฐ„์„ ํƒœ์›๋‹ˆ๋‹ค. ๋ฐ˜๋Œ€๋กœ ๋ชฉํ‘œ๊ฐ€ ์ž‘๊ณ  ๊ฒ€์ฆ์ด ๋น ๋ฅด๋ฉด, ๊ต‰์žฅํžˆ ๋ฌด์‹ํ•œ ๋ฐ˜๋ณต๋ฌธ๋„ ๊ฝค ๊ฐ•๋ ฅํ•œ ์ž๋™ํ™”๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

์šด์šฉ ์‹œ ์ถ”๊ฐ€๋กœ ์ฑ™๊ธฐ๋ฉด ์ข‹์€ ๊ฒƒ๋“ค์ž…๋‹ˆ๋‹ค.

  • ํ…Œ์ŠคํŠธ๊ฐ€ ๋น ๋ฅด๊ฒŒ ๋Œ์•„์•ผ ํ•œ๋‹ค. Ralph๋Š” ์‹คํŒจ๋ฅผ ๋ฐ˜๋ณต์œผ๋กœ ํก์ˆ˜ํ•˜๋Š” ๊ตฌ์กฐ์ด๋ฏ€๋กœ, ๊ฒ€์ฆ ๋ฃจํ”„๊ฐ€ ๋А๋ฆฌ๋ฉด ์ „์ฒด ์‹œ์Šคํ…œ์ด ๋А๋ ค์ง‘๋‹ˆ๋‹ค. Rust์ฒ˜๋Ÿผ ์ปดํŒŒ์ผ์ด ๋ฌด๊ฑฐ์šด ํ”„๋กœ์ ํŠธ๋ผ๋ฉด ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ๋ฒ”์œ„๋ฅผ ์ž˜๊ฒŒ ๋‚˜๋ˆ„๊ณ , ํ”„๋ก ํŠธ์—”๋“œ๋ผ๋ฉด ํƒ€์ž…์ฒดํฌ์™€ ํ•ต์‹ฌ ๋ธŒ๋ผ์šฐ์ € ๊ฒ€์ฆ์„ ๋ถ„๋ฆฌํ•˜๋Š” ํŽธ์ด ๋‚ซ์Šต๋‹ˆ๋‹ค.
  • progress.txt์™€ AGENTS.md๊ฐ€ ์ค‘์š”ํ•˜๋‹ค. ๋งค ๋ฐ˜๋ณต์ด ์ƒˆ ์ปจํ…์ŠคํŠธ๋ผ๋ฉด, ๋‹ค์Œ ์—์ด์ „ํŠธ๊ฐ€ ๊ผญ ์•Œ์•„์•ผ ํ•˜๋Š” ์‹คํ–‰๋ฒ•, ๊ธˆ์ง€์‚ฌํ•ญ, ํ”„๋กœ์ ํŠธ ๊ด€๋ก€๋Š” ํŒŒ์ผ๋กœ ๋‚จ๊ฒจ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฒฉ๋ฆฌ ํ™˜๊ฒฝ์—์„œ ๋Œ๋ฆฌ์ž. --dangerously-* ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ผœ์ ธ ์žˆ์œผ๋ฏ€๋กœ ์ปจํ…Œ์ด๋„ˆยทVMยทDevcontainerยทDocker Sandbox ์•ˆ์—์„œ ์šด์šฉํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

์ƒํ™ฉ๋ณ„ ๋„๊ตฌ ์„ ํƒ์€ ๋Œ€๋žต ์ด๋ ‡์Šต๋‹ˆ๋‹ค.

  • ๋ฏธ๋ฆฌ ๋ถ„ํ•ดํ•ด ๋‘” ์—ฌ๋Ÿฌ ๋‹จ๊ณ„์˜ ์ •ํ˜• ํ”„๋กœ์ ํŠธ๋ฅผ ๋งค๋ฒˆ fresh ์ปจํ…์ŠคํŠธ๋กœ ์ฐจ๋ก€์ฐจ๋ก€ ๋๋‚ด๊ณ  ์‹ถ๋‹ค๋ฉด โ†’ snarktank/ralph
  • ํ•œ ์ค„์งœ๋ฆฌ PROMPT.md๋งŒ์œผ๋กœ Ralph ํŒจํ„ด์„ ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ํ˜•ํƒœ๋กœ ์ฒดํ—˜ํ•ด๋ณด๊ณ  ์‹ถ๋‹ค๋ฉด โ†’ ghuntley ์›ํ˜• Ralph Loop (๊ต์œกยท์‹œ์—ฐ์šฉ)
  • ์ด๋ฏธ Claude Code ์ฑ„ํŒ…์ฐฝ ์•ˆ์—์„œ ์ž‘์—… ์ค‘์ด๊ณ  ์™ธ๋ถ€ ํ„ฐ๋ฏธ๋„ ์—†์ด ํ˜„์žฌ ์„ธ์…˜์—์„œ ๊ทธ๋Œ€๋กœ ์ž๋™ ๋ฐ˜๋ณต์„ ๋Œ๋ฆฌ๊ณ  ์‹ถ๋‹ค๋ฉด โ†’ Claude ralph-loop ํ”Œ๋Ÿฌ๊ทธ์ธ
  • Codex CLI ์‚ฌ์šฉ์ž๊ฐ€ ์ˆ˜ ์‹œ๊ฐ„ ๋ˆ„์  ์ปจํ…์ŠคํŠธ๋กœ ๋‹จ์ผ ๋ชฉํ‘œ๋ฅผ ๋๊นŒ์ง€ ์ถ”๊ตฌํ•˜๋ฉด์„œ ๋ชจ๋ธ์ด ์Šค์Šค๋กœ ์ข…๋ฃŒ๋ฅผ ํŒ์ •ํ•ด์ฃผ๊ธธ ์›ํ•œ๋‹ค๋ฉด โ†’ Codex /goal

LLM์„ ๋” ๋˜‘๋˜‘ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์ด ์•„๋‹ˆ๋ผ, LLM์ด ์ค‘๊ฐ„์— ์žŠ๊ณ  ์ฐฉ๊ฐํ•˜๊ณ  ๋Œ€์ถฉ ๋๋‚ด๋Š” ํŠน์„ฑ์„ ์ธ์ •ํ•œ ๋’ค ๊ทธ ๋ฐ”๊นฅ์— ์˜ค๋ž˜๋œ ์—”์ง€๋‹ˆ์–ด๋ง ์žฅ์น˜๋ฅผ ๋‘๋ฅด๋Š” ๋ฐฉ๋ฒ• โ€” Ralph๋ผ๋Š” ์ด๋ฆ„์ด ๋ถ™์€ ๊ฒƒ๋“ค์˜ ๊ณตํ†ต์ ์€ ์—ฌ๊ธฐ์— ์žˆ์Šต๋‹ˆ๋‹ค. ํŒŒ์ผ ์‹œ์Šคํ…œ์€ ๊ธฐ์–ต์ด๊ณ , Git์€ ํƒ€์ž„๋ผ์ธ์ด๊ณ , ํ…Œ์ŠคํŠธ๋Š” ๋ฐ˜๋Œ€ ์••๋ ฅ์ด๊ณ , PRD/task list๋Š” ์ƒํƒœ ๋จธ์‹ ์ž…๋‹ˆ๋‹ค. ์—์ด์ „ํŠธ์˜ ๋ง์„ ๋ฏฟ์ง€ ๋ง๊ณ , ์ƒํƒœ์™€ ๊ฒ€์ฆ์„ ๋ฏฟ๋Š” ๊ฒƒ. ๊ทธ๋ฆฌ๊ณ  ๋๋‚ฌ๋‹ค๋Š” ์ฆ๊ฑฐ๊ฐ€ ๋‚˜์˜ฌ ๋•Œ๊นŒ์ง€ ๋ฃจํ”„๋ฅผ ๋Œ๋ฆฌ๋Š” ๊ฒƒ. ์ดˆ์•ˆ์ด ํ•œ ๋ฒˆ์— ์™„์„ฑ๋˜์ง€ ์•Š๋”๋ผ๋„, ๋‹ค์Œ iteration์ด ํŒŒ์ผ์— ๋‚จ์€ ์ƒํƒœ๋ฅผ ์ฝ๊ณ  ๋‹ค์‹œ ๋ฐ€๊ณ  ๊ฐ‘๋‹ˆ๋‹ค. ๋๋‚ฌ๋‹ค๋Š” ์ฆ๊ฑฐ๊ฐ€ ๋‚˜์˜ฌ ๋•Œ๊นŒ์ง€์š”.

References