Ralph is a technique for running AI coding agents in a loop. You run the same prompt repeatedly. The AI picks its own tasks from a PRD. It commits after each feature. You come back later to working code.
From AI Hero
I built MediaJanitor, a cleanup tool for the Jellyfin server I share with friends, mostly while I slept. Claude Code wrote the code overnight in a loop, and I spent my mornings checking what it had done. Over the whole project, that came to 350+ commits, 150+ user stories and 10 to 15 hours of my own time. This is the setup, adapted from Ralph.
There's also a visual flowchart of how Ralph works.
Quick start
# 1. Write your PRD with user stories
vim PRD.md
# 2. Convert it to a machine-readable format
/ralph-init
# 3. Run one iteration (human-in-the-loop)
./ralph-once.sh
# 4. Or let it run on its own overnight
./afk-ralph.sh 10
The loop reads and writes four files:
PRD.md → the user stories, written for humans, with [ ] checkboxes
prd.json → the same stories, in a format Ralph works through
CLAUDE.md → project context, plus what Claude learned along the way
progress.txt → a log of finished work
The problem is the context window
A long conversation with an LLM drifts. Errors pile up, the model loses track of earlier decisions, and at some point the session breaks.
Ralph's answer is to start every iteration from zero. Each iteration picks one small story from prd.json, builds it test-first inside a single context window, commits, and exits. Progress lives in files, prd.json and CLAUDE.md, rather than in the model's memory. So instead of one long session that falls apart, you get many short ones that don't depend on each other.
Feedback loops
Writing code doesn't tell the model whether the code works. It has to check, at every step. Ralph's checks:
- Unit tests with pytest and vitest, which must pass before any commit
- Type checking with mypy and TypeScript strict mode
- Docker, to run the actual app and not only the tests
- Puppeteer screenshots for UI changes
- The logs, for runtime errors
A story is done when the tests pass, Docker runs and the browser shows what the story asked for. Without these checks you get code that looks plausible. With them, most of it works.
A PRD Ralph can work from
Ralph is only as good as the PRD it reads. I follow two rules.
Stories small enough for one context window. When a story is too big, I split it by layer:
- Data first: models, migrations
- Backend logic: services, APIs
- Frontend: pages, components
- Integration: wiring the pieces together
Acceptance criteria you can check. "Works correctly" can't be tested. These can:
| Vague | Testable |
| "Handles errors properly" | "Returns 401 when token is missing" |
| "Good UX" | "Shows loading spinner while fetching" |
| "Is secure" | "Passwords hashed with bcrypt" |
Every story also ends with two criteria, "Typecheck passes" and "Unit tests pass". With a vague PRD, Ralph goes around in circles. With a good one, I wake up to finished stories.
The daily loop
┌─────────────────────────────────────────────────────────────────┐
│ │
│ EVENING NIGHT MORNING DAY │
│ ──────── ───── ─────── ─── │
│ │
│ Start Ralph → AI executes → Human reviews → ↵ │
│ ./afk-ralph.sh autonomously /morning-routine │
│ (10+ stories) │
│ │
│ ←─────────────────── repeat daily ────────────────────────────│
│ │
└─────────────────────────────────────────────────────────────────┘
| Task | Me | AI (Ralph) |
| Write the PRD with acceptance criteria | ✓ | |
| Build the stories test-first | | ✓ |
| Run the automated checks | | ✓ |
| Commit the code | | ✓ |
| Review the finished work | ✓ | |
| Triage suggestions and prioritize bugs | ✓ | |
| Test by hand in the browser | ✓ | |
| Write what it learned to CLAUDE.md | | ✓ |
In short, I write the stories and review the work. Ralph does the rest.
The morning routine
After a night of Ralph, I go through six steps. /morning-routine walks me through them, or I do them by hand:
/prd-sync → Review → /prd-archive → /review-suggestions → Manual QA → /ralph-init
1. /prd-sync
Copies the stories finished in prd.json back to the checkboxes in PRD.md. Skip it if nothing got done overnight.
2. Review what was done
tail -50 progress.txt # What Ralph completed
git log --oneline -20 # Scan commits for surprises
I look for unusually large commits, odd messages and changes to files the story had no reason to touch.
3. /prd-archive
Moves fully finished epics to ARCHIVED_PRD.md. Skip it if no epic is complete.
4. /review-suggestions
Goes through each item in SUGGESTIONS.md, with three choices:
- Remove: not needed, or already done
- Promote to PRD: a real issue that needs a user story
- Skip: valid, but for later
5. Manual QA
I use the app. I click around and try the edge cases. No automated test replaces a person looking at the product. For the items I promoted in step 4, /prd writes proper user stories.
6. /ralph-init
Generates a fresh prd.json with only the unfinished stories. Skip it if Ralph isn't running tonight.
Then commit
git add PRD.md prd.json SUGGESTIONS.md progress.txt
git commit -m "chore: morning routine sync and cleanup"
What Ralph does at night
Once I run ./afk-ralph.sh N, Claude repeats this loop on its own:
- Picks the next story with
passes: false in prd.json.
- Writes a failing test, then the code.
- Runs the checks: pytest, mypy, npm test, npm run check.
- Runs the app in Docker, because unit tests alone aren't enough.
- Checks UI changes in a browser.
- Sets
passes: true in prd.json.
- Commits with a clear message.
- Writes what it learned to progress.txt and CLAUDE.md.
- Starts over, until N iterations are done or no story is left.
Every 3 to 5 iterations, it also runs /exploratory-qa. That pass looks across the whole app for inconsistent navigation, gaps in error handling and security issues, and writes what it finds to SUGGESTIONS.md.
Why it works
Every change has to pass the existing tests, so what already works keeps working, as far as the tests cover it. Precise acceptance criteria keep Ralph from wandering off. CLAUDE.md collects what each session learned, and the next session reads it. The parts that need judgment, like writing the PRD, triaging bugs and setting priorities, stay with me.
MediaJanitor in numbers
Here's what came out of letting Ralph build MediaJanitor:
| Metric | Value |
| Commits | 350+ |
| User stories completed | 150+ |
| My time | 10 to 15 hours |
That's 10 to 15 hours for the whole project, not per week.
My part was:
- Writing and refining the PRD: defining features, splitting stories, writing acceptance criteria
- The morning reviews: checking what Ralph built and triaging its suggestions
- Fixing the occasional bug Ralph couldn't solve
- External setup: Slack notifications, SMTP2GO for emails, deployment configs
Ralph wrote everything else: the backend APIs, the database models, the frontend pages, the test suites and the Docker configs.
What I got wrong
When Ralph fails, it's usually my fault.
Vague PRDs waste iterations. Early on, I wrote stories like "Add user settings page." Ralph would build something, but not what I had in mind. Now I write "Settings page with email notification toggles. Saves to user_preferences table. Shows success toast on save."
It takes shortcuts. When Ralph can't verify something, say a flaky browser test or Docker not running, it sometimes marks the story done anyway. That's why the morning review matters.
Skipping the review doesn't work. I once let Ralph run 20+ iterations without checking and came back to a mess. Now I cap it at 10 iterations and review every morning.
Files
| File | What it's for |
PRD.md | User stories for humans, with [ ] checkboxes |
prd.json | The version Ralph works through |
progress.txt | Log of finished tasks |
CLAUDE.md | Project context and what Claude learned |
SUGGESTIONS.md | QA observations with [P1]/[P2]/[P3] priorities |
prompt.md | The prompt Ralph follows on every iteration |
Skills
Planning, before Ralph runs
/prd helps write a PRD with right-sized stories and acceptance criteria you can check.
/ralph-init converts PRD.md to prd.json. It checks story sizes and orders the stories so dependencies run first.
Review, after Ralph runs
/morning-routine walks through the six steps of the morning routine: sync, review, archive, triage, QA and re-init.
/review-suggestions goes through SUGGESTIONS.md one item at a time.
/exploratory-qa runs every 3 to 5 iterations and checks navigation consistency, error handling, auth guards, UI coherence and security.
Maintenance, when needed
/prd-sync copies passes: true from prd.json back to the PRD.md checkboxes.
/prd-archive moves finished epics to ARCHIVED_PRD.md, so the active PRD stays short.
Last thoughts
This is my version of Ralph by AI Hero, adapted to Claude Code.
The main thing I learned is that coding agents do much better with structure. Give them a clear PRD, checks at every step and a fresh context for each story, and they ship real features overnight.
It's not hands-off. I still debug, review and think about the product. What changes is the split. I spend my time deciding what to build, and very little of it typing.
MediaJanitor exists because of this workflow.
Ralph est une technique pour faire tourner des agents de code IA en boucle. On relance le même prompt, encore et encore. L'IA choisit elle-même ses tâches dans un PRD. Elle commit après chaque fonctionnalité. Vous revenez plus tard, et le code marche.
D'après AI Hero
J'ai construit MediaJanitor, un outil de nettoyage pour le serveur Jellyfin que je partage avec des amis, en grande partie pendant que je dormais. Claude Code écrivait le code la nuit, en boucle, et je passais mes matinées à vérifier ce qu'il avait fait. Sur tout le projet, ça fait plus de 350 commits, plus de 150 user stories et 10 à 15 heures de mon temps. Voici la méthode, adaptée de Ralph.
Il existe aussi un schéma du fonctionnement de Ralph.
Démarrage rapide
# 1. Écrire le PRD avec les user stories
vim PRD.md
# 2. Le convertir dans un format lisible par la machine
/ralph-init
# 3. Lancer une itération (human-in-the-loop)
./ralph-once.sh
# 4. Ou le laisser tourner seul pendant la nuit
./afk-ralph.sh 10
La boucle lit et écrit quatre fichiers :
PRD.md → les user stories, lisibles par un humain, avec des cases [ ]
prd.json → les mêmes stories, dans un format que Ralph parcourt
CLAUDE.md → le contexte du projet et ce que Claude a appris en route
progress.txt → le journal du travail terminé
Le problème, c'est la fenêtre de contexte
Une longue conversation avec un LLM dérive. Les erreurs s'accumulent, le modèle perd le fil des décisions prises plus tôt, et à un moment la session casse.
La réponse de Ralph, c'est de repartir de zéro à chaque itération. Chaque itération prend une petite story dans prd.json, la construit en commençant par les tests dans une seule fenêtre de contexte, commit et s'arrête. L'avancement vit dans des fichiers, prd.json et CLAUDE.md, et pas dans la mémoire du modèle. Au lieu d'une longue session qui finit par s'effondrer, on a plein de sessions courtes et indépendantes.
Les boucles de feedback
Écrire du code ne dit pas au modèle si ce code marche. Il doit vérifier, à chaque étape. Les vérifications de Ralph :
- Tests unitaires avec pytest et vitest, qui doivent passer avant chaque commit
- Vérification des types avec mypy et le mode strict de TypeScript
- Docker, pour lancer la vraie app et pas seulement les tests
- Des captures Puppeteer pour les changements d'interface
- Les logs, pour les erreurs à l'exécution
Une story est finie quand les tests passent, que Docker tourne et que le navigateur affiche ce que la story demandait. Sans ces vérifications, on obtient du code qui a l'air plausible. Avec, la plupart du code marche.
Un PRD dont Ralph peut se servir
Ralph ne vaut que ce que vaut le PRD qu'il lit. Je suis deux règles.
Des stories qui tiennent dans une fenêtre de contexte. Quand une story est trop grosse, je la découpe par couche :
- Les données d'abord : modèles, migrations
- La logique backend : services, APIs
- Le frontend : pages, composants
- L'intégration : brancher les morceaux entre eux
Des critères d'acceptation vérifiables. « Fonctionne correctement » ne se teste pas. Ceux-là, si :
| Flou | Vérifiable |
| « Gère bien les erreurs » | « Renvoie 401 quand le token manque » |
| « Bonne UX » | « Affiche un spinner pendant le chargement » |
| « Est sécurisé » | « Mots de passe hashés avec bcrypt » |
Chaque story se termine aussi par deux critères, « le typecheck passe » et « les tests unitaires passent ». Avec un PRD flou, Ralph tourne en rond. Avec un bon, je me réveille avec des stories terminées.
La boucle quotidienne
┌─────────────────────────────────────────────────────────────────┐
│ │
│ SOIR NUIT MATIN JOUR │
│ ──── ──── ───── ──── │
│ │
│ Lancer Ralph → L'IA exécute → Relecture → ↵ │
│ ./afk-ralph.sh en autonomie /morning-routine │
│ (10+ stories) │
│ │
│ ←─────────────────── répéter chaque jour ─────────────────────│
│ │
└─────────────────────────────────────────────────────────────────┘
| Tâche | Moi | IA (Ralph) |
| Écrire le PRD et ses critères d'acceptation | ✓ | |
| Construire les stories en commençant par les tests | | ✓ |
| Lancer les vérifications automatiques | | ✓ |
| Commiter le code | | ✓ |
| Relire le travail terminé | ✓ | |
| Trier les suggestions et prioriser les bugs | ✓ | |
| Tester à la main dans le navigateur | ✓ | |
| Noter ce qu'il a appris dans CLAUDE.md | | ✓ |
En résumé, j'écris les stories et je relis le travail. Ralph fait le reste.
La routine du matin
Après une nuit de Ralph, je passe par six étapes. /morning-routine me guide, ou je les fais à la main :
/prd-sync → Relecture → /prd-archive → /review-suggestions → QA manuelle → /ralph-init
1. /prd-sync
Recopie les stories terminées dans prd.json vers les cases de PRD.md. Inutile si rien n'a été fini dans la nuit.
2. Relire ce qui a été fait
tail -50 progress.txt # Ce que Ralph a terminé
git log --oneline -20 # Repérer les commits surprenants
Je cherche les commits anormalement gros, les messages bizarres et les fichiers modifiés qui n'avaient rien à voir avec la story.
3. /prd-archive
Déplace les epics terminées dans ARCHIVED_PRD.md. Inutile si aucune epic n'est complète.
4. /review-suggestions
Passe en revue chaque élément de SUGGESTIONS.md, avec trois choix :
- Supprimer : inutile, ou déjà fait
- Passer dans le PRD : un vrai problème, qui mérite une user story
- Garder pour plus tard : valable, mais pas maintenant
5. QA manuelle
J'utilise l'app. Je clique partout et j'essaie les cas limites. Aucun test automatique ne remplace quelqu'un qui regarde le produit. Pour les éléments passés dans le PRD à l'étape 4, /prd écrit de vraies user stories.
6. /ralph-init
Régénère prd.json avec seulement les stories pas encore finies. Inutile si Ralph ne tourne pas ce soir.
Ensuite, commit
git add PRD.md prd.json SUGGESTIONS.md progress.txt
git commit -m "chore: morning routine sync and cleanup"
Ce que Ralph fait la nuit
Une fois ./afk-ralph.sh N lancé, Claude répète cette boucle tout seul :
- Prend la prochaine story marquée
passes: false dans prd.json.
- Écrit un test qui échoue, puis le code.
- Lance les vérifications : pytest, mypy, npm test, npm run check.
- Lance l'app dans Docker, parce que les tests unitaires ne suffisent pas.
- Vérifie les changements d'interface dans un navigateur.
- Passe la story à
passes: true dans prd.json.
- Commit avec un message clair.
- Note ce qu'il a appris dans progress.txt et CLAUDE.md.
- Recommence, jusqu'à N itérations ou jusqu'à ce qu'il ne reste plus de story.
Toutes les 3 à 5 itérations, il lance aussi /exploratory-qa. Ce passage parcourt toute l'app à la recherche de navigation incohérente, d'erreurs mal gérées et de problèmes de sécurité, et note ce qu'il trouve dans SUGGESTIONS.md.
Pourquoi ça marche
Chaque changement doit passer les tests existants, donc ce qui marche continue de marcher, dans la limite de ce que les tests couvrent. Des critères d'acceptation précis empêchent Ralph de partir dans tous les sens. CLAUDE.md garde ce que chaque session a appris, et la suivante le lit. Tout ce qui demande du jugement, comme écrire le PRD, trier les bugs et fixer les priorités, reste chez moi.
MediaJanitor en chiffres
Voici ce qu'a donné Ralph sur MediaJanitor :
| Mesure | Valeur |
| Commits | 350+ |
| User stories terminées | 150+ |
| Mon temps | 10 à 15 heures |
Ça fait 10 à 15 heures pour tout le projet, pas par semaine.
Ma part :
- Écrire et affiner le PRD : définir les fonctionnalités, découper les stories, écrire les critères d'acceptation
- Les relectures du matin : vérifier ce que Ralph a construit et trier ses suggestions
- Corriger les rares bugs que Ralph n'arrivait pas à résoudre
- La configuration externe : notifications Slack, SMTP2GO pour les emails, configs de déploiement
Ralph a écrit tout le reste : les APIs backend, les modèles de base de données, les pages frontend, les suites de tests et les configs Docker.
Ce que j'ai mal fait
Quand Ralph échoue, c'est généralement ma faute.
Un PRD flou gaspille des itérations. Au début, j'écrivais des stories comme « Ajouter une page de paramètres utilisateur ». Ralph construisait quelque chose, mais pas ce que j'avais en tête. Maintenant j'écris « Page de paramètres avec des toggles de notifications email. Sauvegarde dans la table user_preferences. Affiche un toast de succès à l'enregistrement. »
Il prend des raccourcis. Quand Ralph ne peut pas vérifier quelque chose, un test navigateur instable ou Docker qui ne tourne pas, il lui arrive de marquer la story comme finie quand même. C'est pour ça que la relecture du matin compte.
Sauter la relecture ne marche pas. Une fois, j'ai laissé Ralph enchaîner plus de 20 itérations sans regarder, et je suis revenu sur un chantier. Maintenant je plafonne à 10 itérations et je relis chaque matin.
Les fichiers
| Fichier | À quoi il sert |
PRD.md | Les user stories pour les humains, avec des cases [ ] |
prd.json | La version que Ralph parcourt |
progress.txt | Le journal des tâches terminées |
CLAUDE.md | Le contexte du projet et ce que Claude a appris |
SUGGESTIONS.md | Les observations de QA, avec des priorités [P1]/[P2]/[P3] |
prompt.md | Le prompt que Ralph suit à chaque itération |
Les skills
Pour préparer, avant Ralph
/prd aide à écrire un PRD avec des stories de la bonne taille et des critères d'acceptation vérifiables.
/ralph-init convertit PRD.md en prd.json. Il vérifie la taille des stories et les ordonne pour que les dépendances passent en premier.
Pour relire, après Ralph
/morning-routine guide à travers les six étapes de la routine du matin : sync, relecture, archivage, tri, QA et ré-init.
/review-suggestions passe en revue SUGGESTIONS.md, un élément à la fois.
/exploratory-qa tourne toutes les 3 à 5 itérations et vérifie la cohérence de la navigation, la gestion des erreurs, la protection des routes, la cohérence de l'interface et la sécurité.
Pour l'entretien, au besoin
/prd-sync recopie les passes: true de prd.json vers les cases de PRD.md.
/prd-archive déplace les epics terminées dans ARCHIVED_PRD.md, pour que le PRD actif reste court.
Pour finir
C'est ma version de Ralph d'AI Hero, adaptée à Claude Code.
La principale leçon, c'est que les agents de code s'en sortent bien mieux avec de la structure. Donnez-leur un PRD clair, des vérifications à chaque étape et un contexte neuf pour chaque story, et ils livrent de vraies fonctionnalités pendant la nuit.
Ce n'est pas sans effort. Je débugge encore, je relis encore, je réfléchis encore au produit. C'est la répartition qui change. Je passe mon temps à décider quoi construire, et presque plus à le taper.
MediaJanitor existe grâce à cette méthode.