Il test rosso che dipendeva dalla shell, non dal codice
Un test fermava il rilascio solo nella mia shell: leggeva il catalogo di produzione. La prima spiegazione era sbagliata. Cosa ho verificato e come l'ho curato.
In breve. In Arvo il gate di rilascio si fermava per un test rosso che in CI era verde. Nessuno aveva toccato né la rotta né il test: cambiava la shell. Una variabile d'ambiente, NEXT_PUBLIC_SUPABASE_URL, esportata in ogni shell della sessione dell'agente, faceva interrogare al test la cache di produzione. Il verdetto di un test lo decide l'ambiente del processo, non il codice. La mia prima spiegazione era sbagliata e l'ho scoperto solo controllando i dati. Fatti dalla PR #1881 di Arvo; il legame con un altro caso è una mia interpretazione, e lo segnalo più sotto.
Il sintomo: un rilascio fermo senza modifiche
Il canale di rilascio prebuilt di Arvo esegue la suite locale come gate prima di mettere in staging. Un giorno il gate si è fermato su un solo test, route-reliability, nella cartella della rotta che serve le thumbnail degli esercizi per l'app mobile.
La stranezza era questa: la rotta era ferma da luglio, il test pure. Nessun commit recente su nessuno dei due. E in CI, su GitHub Actions, lo stesso test era verde. Un rosso senza modifiche che dipende da dove lo lanci non è un bug del test. È un'informazione sul test che non avevo.
La causa: un ramo che guarda l'ambiente
Alla riga 277 della rotta c'è un ramo che si attiva se NEXT_PUBLIC_SUPABASE_URL è definita. Se lo è, la rotta legge dalla cache degli esercizi nel database e, se trova un thumbnail_storage_path, restituisce direttamente l'URL pubblico.
Il test mocka l'autenticazione mobile e il servizio di animazione. Non mocka il servizio di cache. Nella mia shell la variabile era esportata, puntava a https://api.arvo.guru, e quindi il test interrogava la tabella di produzione. In CI la variabile non c'è, il ramo non si prende, il test passa.
Due ambienti, due percorsi di codice, nessun segnale nel test che ne esistessero due. Il codice di produzione era corretto: era il test a non guardarlo.
La mia prima ipotesi era falsa
Quando ho visto che il test leggeva dati di produzione, mi sono data una spiegazione comoda: «un backfill recente ha riempito gli storage path, e il test non se l'aspettava». Era plausibile e l'avrei potuta scrivere nella PR senza che nessuno la contestasse.
Ho controllato. Sulla tabella:
| Verifica | Risultato |
|---|---|
Righe con thumbnail_storage_path |
1.689 su 1.903 |
| «Barbell Bench Press» | thumbnails/3.jpg, creata il 9 dicembre 2025, ultimo fetch 13 agosto 2026 |
| Righe che hanno preso uno storage path dal 1° settembre | 0 |
Il dato era lì da mesi. Nessun backfill recente. Il rosso era latente da quando esiste il ramo storage, a luglio 2026, e si vede solo dove la variabile c'è. Lo conferma un dettaglio: la stage del 1° settembre, partita da una shell senza quella variabile, aveva registrato ciLocalTests: true. Le shell della sessione agente la avevano, e lì il gate si fermava.
Se avessi pubblicato l'ipotesi del backfill, la cura sarebbe stata sbagliata (aggiornare un'aspettativa su dati che non cambiavano) e il problema sarebbe tornato alla prima shell con la variabile.
La cura: poca, e senza test nuovi
- Il test ora mocka il servizio di cache, con default «nessun risultato dal database». Ogni caso esercita il ramo che dichiara di esercitare.
- Nessun test nuovo. Avevo scritto un caso per la precedenza dello storage sull'immagine Open Graph e l'ho tolto: un file gemello nella stessa cartella,
storage-before-og-image.test.ts, la copriva già, con lo stesso mock. Quel file sapeva che il mock serviva; quello rosso se n'era dimenticato. - Una guardia esplicita nomina la classe del problema e punta al file gemello, così chi un domani toglie il mock legge perché c'è.
- Nessuna modifica alla rotta.
Esito riportato nella PR: la cartella passa a 2 file e 9 test verdi, e route-reliability da 4 test (uno rosso) a 5. Cinque test a pagamento non sono stati eseguiti perché la loro variabile di opt-in non era impostata: saltati, non verdi, e la PR lo dice così.
Una famiglia di problemi, per me un'interpretazione
Questa parte è un'interpretazione, non un fatto di quella PR.
Un mese dopo, nel lavoro sulla factory che verifica le proprie correzioni, ho ritrovato uno schema simile in forma diversa: il riproduttore originale passava 14 test su 14 in un ambiente ordinario e 7 su 14 nell'ambiente del verificatore (lo riporta la PR #323 di agentic-factory). Anche lì il codice sotto test era lo stesso; cambiava il processo che lo eseguiva.
Non sostengo che siano lo stesso bug. Sostengo che condividano una domanda: di che cosa è fatto davvero il verdetto di questo test? Se include l'ambiente del processo, quel verdetto vale solo dove l'ambiente è uguale. E con gli agenti il problema si aggrava, perché l'agente lavora in una shell che nessun altro ha, spesso piena di variabili che l'umano non ricorda di aver esportato.
Cosa faccio adesso
Tre cose pratiche, senza pretese di generalità, perché vengono da un solo caso:
- Se un test è rosso in un ambiente e verde in un altro, prima di cercare la modifica cerco la differenza di ambiente.
- Se una spiegazione «ovvia» mi è venuta in mente in dieci secondi, la verifico sui dati prima di scriverla. In questo caso mi ha evitato una cura sbagliata.
- Prima di aggiungere un test, controllo se nella stessa cartella ce n'è già uno gemello che copre il caso.
Sull'affidabilità dei sistemi che correggono sé stessi ho scritto in una fabbrica che ripara il software mentre dormiamo, e su come non lasciare che i problemi veri si accumulino in Backlog Zero.
Domande frequenti
Perché un test può essere verde in CI e rosso in locale?
Perché il codice testato ha un ramo che dipende dall'ambiente, per esempio una variabile come NEXT_PUBLIC_SUPABASE_URL, e il test non lo isola. In CI la variabile manca e si prende un percorso, in locale è presente e se ne prende un altro.
Come si cura un test che dipende dall'ambiente?
Nel caso di Arvo, mockando il servizio che quel ramo interrogava, con un default esplicito, così il test non vede più la produzione. Il codice di produzione non è stato toccato.
Perché gli agenti AI peggiorano questo problema?
Un agente lavora in una shell propria, con variabili esportate che nessun altro vede. Un test sensibile all'ambiente può quindi fallire solo per lui, e il rosso sembra un problema del codice invece che della shell.