Agentic Workshop
Modulo 07 · 50 min

Hooks e automazione locale controllata

Gli hook inseriscono controlli automatici nel momento in cui un agente usa uno strumento. Sono potenti perché agiscono sempre; proprio per questo devono avere condizioni strette, timeout e log comprensibili.

Lezioni

Cosa affronti, passo dopo passo

7.1

Il ciclo di vita di un evento

Un hook è una regola automatica collegata a un evento, per esempio prima o dopo l'uso di uno strumento. Per progettarlo devi disegnare la sequenza: che cosa lo attiva, quale informazione riceve, quale decisione produce e che cosa accade dopo.

Un evento nasce da un’azione richiesta. `PreToolUse` può consentire, bloccare o chiedere approvazione prima dell’esecuzione. Se lo strumento riesce interviene `PostToolUse`; se fallisce esiste l’evento distinto `PostToolUseFailure`. Ogni fase riceve un payload e deve produrre uno stato comprensibile.

Disegna prima la timeline. Se non sai se una regola deve prevenire un danno o verificare un risultato, non sai ancora in quale fase implementarla.

  1. Nomina l’evento e il payload minimo che lo descrive.
  2. Decidi se la regola deve prevenire l’azione o verificarne l’esito.
  3. Definisci condizioni, decisione ed exit code osservabili.
  4. Esegui lo strumento soltanto se il controllo preventivo consente.
  5. Registra l’esito e impedisci che l’hook riattivi se stesso.
Per capirlo nel concreto · Controllare una richiesta di deploy in staging

Un agente può avviare il deploy di staging. Vuoi impedire branch non approvati e registrare l'esito senza automatizzare la produzione.

  1. Definisci l'evento `deploy.requested` con branch, commit, ambiente e identità richiedente; escludi token e variabili segrete dal payload.
  2. Nel pre-hook consenti soltanto `staging`, verifica che il commit esista e richiedi approvazione se il branch non segue la convenzione di release.
  3. Se consentito, lo strumento avvia il deploy e restituisce ID, commit distribuito e stato; un errore mantiene distinto `tool_failed` da `blocked`.
  4. Il post-hook legge quell'esito, interroga una sola volta il controllo di salute e registra `verified`, `failed` o `timeout`; non avvia mai un deploy di produzione.

Risultato: La timeline mostra chi ha chiesto cosa, perché l'azione è stata consentita e quale versione è stata realmente verificata. Un blocco non viene confuso con un guasto del deploy.

7.2

Pre-hook: proteggere il confine

Un pre-hook è una barriera posta prima di un'azione. Funziona bene quando la regola dipende da fatti che puoi misurare: percorso risolto, tipo di operazione, ambiente o presenza di un'approvazione valida.

Un pre-hook è adatto a bloccare scritture su file sensibili, comandi distruttivi o operazioni fuori ambito. La decisione dovrebbe dipendere da proprietà osservabili come percorso e tipo di azione, non da parole vaghe nel prompt.

Il messaggio di blocco deve spiegare quale regola è scattata e come procedere in sicurezza. Un blocco senza rimedio trasforma una protezione in un ostacolo opaco. Il pre-hook aggiunge un controllo, ma non sostituisce e non può aggirare la politica dei permessi.

Per capirlo nel concreto · Bloccare una cancellazione fuori dalla cartella temporanea

Un workflow può eliminare artefatti sotto `/workspace/tmp/build-42`, ma non deve toccare sorgenti, home directory o percorsi derivati da variabili vuote.

  1. Il pre-hook riceve operazione e bersaglio, rifiuta input vuoto o glob non risolto e calcola il percorso canonico senza eseguire la cancellazione.
  2. Confronta il bersaglio con la radice esplicita `/workspace/tmp` e vieta la radice stessa; consente soltanto un discendente non simbolico identificato per quella build.
  3. Mostra una preview dei file e richiede l'ID dell'approvazione per quantità o dimensioni oltre la soglia; non stampa il contenuto dei file.
  4. Prova directory valida, `../src`, collegamento verso home e variabile vuota. Solo il primo caso deve raggiungere lo strumento; gli altri spiegano il percorso risolto e il rimedio.

Risultato: L'automazione pulisce il proprio artefatto senza trasformare un errore di percorso in una cancellazione ampia. I casi negati sono comprensibili e testabili.

7.3

Post-hook: osservare ciò che è già avvenuto

Un post-hook lavora su ciò che è davvero accaduto. Può rendere immediato un controllo ripetitivo, ma non dovrebbe nascondere una correzione sostanziale o far sembrare riuscita un'azione che invece ha lasciato errori.

Dopo una modifica riuscita, un hook può formattare il file, eseguire un controllo mirato, sostituire l’output mostrato al modello o registrare l’evento. Il punto cruciale è temporale: lo strumento ha già agito, quindi un `PostToolUse` non può annullare una scrittura o prevenire una chiamata di rete già avvenuta.

Per impedire un’azione usa `PreToolUse`. Per reagire a un errore dello strumento usa `PostToolUseFailure`. In entrambi i casi limita il controllo ai file coinvolti e restituisci contesto, durata e messaggio senza nascondere all’agente la causa reale.

Per capirlo nel concreto · Verificare un endpoint TypeScript appena modificato

L'agente cambia `src/orders/route.ts`. Il progetto vuole formattazione coerente e un controllo tipi del solo pacchetto API.

  1. Il post-hook si attiva solo dopo una scrittura riuscita su file `.ts` del pacchetto API e riceve l'elenco esatto dei file toccati.
  2. Esegue il formatter su quei file, registra se il diff è cambiato e non apre altri file per una pulizia generale.
  3. Lancia `npm run typecheck --workspace api` con timeout definito; conserva exit code, durata e prime righe diagnostiche prive di dati sensibili.
  4. Se il controllo fallisce, restituisce `written_but_unverified` e il messaggio; non modifica tipi o test in silenzio e non annulla la scrittura dell'utente.

Risultato: Hai una formattazione ripetibile e una prova pertinente. Un errore di tipo resta visibile e attribuito al controllo successivo, senza essere confuso con il fallimento della scrittura.

7.4

Idempotenza, ricorsione e fallimento

Un hook affidabile deve comportarsi bene anche quando viene ripetuto, si sovrappone a un'altra esecuzione o incontra un servizio in errore. Questi casi non sono eccezioni remote: sono il normale costo dell'automazione.

Un hook idempotente può essere ripetuto senza moltiplicare effetti. Un formatter lanciato da `PostToolUse` non genera automaticamente un nuovo evento; il ciclo nasce se lo script provoca proprio l’evento che osserva o chiede nuove chiamate allo stesso strumento. Nomina quell’evento e usa un marcatore di esecuzione o un comando che non lo riattivi.

Definisci timeout e politica di errore. Per una protezione critica può essere corretto fallire chiuso; per una metrica non essenziale può essere preferibile registrare il problema e proseguire.

Per capirlo nel concreto · Rigenerare l'indice della documentazione senza creare un ciclo

Dopo una modifica in `docs/`, un hook aggiorna `docs/index.json`. Il primo prototipo si riattiva quando salva proprio l'indice e duplica le voci.

  1. Cambia il generatore: legge i documenti sorgente, ordina le voci e riscrive l'intero indice soltanto se il contenuto calcolato è diverso.
  2. Escludi `docs/index.json` dai file che attivano l'hook e aggiungi un lock breve per impedire a due salvataggi simultanei di scrivere insieme.
  3. Esegui due volte lo stesso evento: il primo aggiornamento produce il nuovo indice, il secondo non cambia byte né aggiunge voci. Poi simula un processo interrotto e verifica il rilascio del lock.
  4. Se la generazione fallisce, conserva l'indice precedente, marca la documentazione come non verificata e mostra il comando manuale; non pubblicare un JSON parziale.

Risultato: Retry e salvataggi ravvicinati producono un solo indice valido. Il file generato non riattiva il proprio hook e un guasto lascia disponibile l'ultima versione completa.

Esempio guidato

Protezione e formattazione

Task Notes contiene `.env` e file Python formattabili.

PRE write: se path termina con .env → blocca e spiega
TOOL write: applica la modifica autorizzata
POST write: se path termina con .py → formatta quel file
LOG: evento, decisione, durata, exit code; mai il contenuto del segreto