Raffinare a cicli e mettere Claude Code in pipeline sembrano due argomenti distanti. Sono lo stesso problema a due velocità: la qualità dipende da quanto presto un errore diventa visibile e da chi lo vede. In sessione lo vedi tu; in CI deve vederlo un exit code.
Senza un controllo eseguibile non esiste iterazione
La documentazione è netta: «Claude stops when the work looks done». Se non esiste un controllo che Claude possa lanciare da solo, «sembra fatto» resta l’unico segnale e la verifica ricade su di te, un errore alla volta. Il controllo è qualunque cosa restituisca un segnale leggibile nella conversazione: una suite di test, l’exit code di una build, un linter, uno screenshot del browser confrontato con il design.
Resta da decidere quanto duramente il controllo blocca la chiusura del turno. Quattro livelli, con setup crescente:
- nel prompt: chiedi di eseguire il controllo e iterare nello stesso messaggio;
- sulla sessione: imposti la condizione come
/goale un valutatore separato la ricontrolla dopo ogni turno; - come gate deterministico: un hook
Stopesegue il tuo script e impedisce al turno di chiudersi finché non passa. Con un limite dichiarato: «Claude Code overrides the hook and ends the turn after 8 consecutive blocks». Un hookStopnon è un lucchetto; - come secondo parere: un subagent di verifica in contesto fresco, che vede il diff e i criteri ma non il ragionamento che ha prodotto la modifica.
La guida avverte di un effetto collaterale che si paga in produzione: un revisore a cui chiedi di trovare lacune ne riporterà comunque, anche quando il lavoro è solido. Inseguirle tutte porta a over-engineering. Vincola il revisore a segnalare solo ciò che tocca correttezza o requisiti dichiarati.
Correggere presto, e riconoscere quando ripartire
«The best results come from tight feedback loops». In sessione il ciclo si stringe con Esc per fermare l’azione conservando il contesto, Esc + Esc o /rewind per ripristinare conversazione e codice a un checkpoint, e /clear fra task scollegati.
La decisione difficile è quando smettere. La soglia suggerita è due: dopo due correzioni fallite sullo stesso punto il contesto è pieno di tentativi sbagliati, e una sessione pulita con un prompt migliore batte quasi sempre una sessione lunga carica di correzioni. È lo stesso motivo per cui il pattern Writer/Reviewer su due sessioni funziona: un contesto fresco non è affezionato al codice che ha appena scritto.
Un limite prima di promettere «rollback» a un team: i checkpoint tracciano solo le modifiche fatte con gli strumenti di edit di Claude, non quelle da comandi Bash o processi esterni. Non sostituiscono git.
Lo stesso ciclo in pipeline, con claude -p
In CI l’invocazione è non interattiva: -p con il prompt e le opzioni CLI necessarie. «Claude Code exits with code 0 on success and a non-zero code when the run fails», quindi lo script può ramificare sullo stato di uscita.
claude --bare -p "Summarize README.md" --allowedTools "Read"
Il flag che conta davvero in pipeline è --bare: salta l’auto-discovery di hook, skill, comandi, subagent, plugin, server MCP, memoria automatica e CLAUDE.md. Non è solo latenza risparmiata: senza --bare, una sessione -p esegue gli hook del .claude/settings.json del progetto e si connette ai server del suo .mcp.json anche in una cartella di cui non ti sei mai fidato, senza dialogo di trust e senza approvazione per server. La doc dichiara la direzione: «--bare is the recommended mode for scripted and SDK calls, and will become the default for -p in a future release». In bare mode Claude Code non legge credenziali OAuth né il keychain di sistema, quindi devi impostare ANTHROPIC_API_KEY.
Per il perimetro dei permessi in CI, --permission-mode dontAsk nega tutto ciò che non è nelle tue regole permissions.allow o nel set di comandi di sola lettura. Con --output-format json la risposta porta result, session_id e il costo stimato lato client. Un gate utile e poco noto: nell’evento system/init la chiave mcp_server_errors è omessa quando non ci sono errori, quindi la pipeline può fallire su un array non vuoto invece di proseguire con un server MCP silenziosamente assente.
Le due integrazioni, e i loro modi di rompersi
Su GitHub l’azione anthropics/claude-code-action deduce come girare dalla configurazione: senza input prompt è interactive mode e attende la frase di trigger, @claude per default; con prompt è automation mode e parte su qualunque evento, cron incluso. In automation mode i risultati finiscono nel log del run, non in un commento, a meno che il prompt lo chieda e ci sia uno strumento per farlo.
Due controlli precedono ogni run: su eventi issue e pull request l’attore deve avere accesso in scrittura, e un attore bot viene rifiutato se non elencato in allowed_bots. Anche i run pianificati passano da qui, perché GitHub li attribuisce a un utente del repository.
Il modo di rompersi più costoso è silenzioso: «GitHub doesn’t trigger workflows on commits made with the default GITHUB_TOKEN». Se passi quel token, la CI non gira sui commit di Claude e il tuo ciclo di verifica non esiste più.
Su GitLab l’integrazione «is currently in beta» ed «is maintained by GitLab»: un job in .gitlab-ci.yml, ANTHROPIC_API_KEY come variabile mascherata, e lo stesso claude -p. Su entrambe il costo ha la stessa forma: minuti di runner più token, contenuti con --max-turns e timeout di job.