--- name: project-monitoraggio-gmail description: "Storia completa del filtro Gmail personale di Mauro (04-09/09/2026): dalla sentinella persistente --bg (poi abbandonata per consumo token quadratico) fino a servizio-gmail-check, l'architettura meccanica finale oggi in produzione." metadata: type: progetto --- # Monitoraggio Gmail personale Idea di Mauro (04/09/2026): monitoraggio continuo della posta Gmail personale, con filtro delle cose realmente importanti — propedeutico a un'estensione futura analoga sulla posta di lavoro (non nello scope ora, solo da non chiudersi la strada nel design). ## Primo tentativo — delega completa a una slave, bloccato subito Blocco tecnico reale, non di design. - Lanciata una slave con mandato ampio (trova problemi/soluzioni, decidi tu i criteri, produci un canvas completo) — stesso test di delega già fatto con successo sulla migrazione Frank la notte stessa. Istruita a verificare PRIMA di tutto l'accesso ai tool Gmail e fermarsi subito se assente, invece di disegnare un'architettura su una base che non regge. - Si è fermata correttamente al primo passo: nessun tool Gmail esposto/caricabile nella sessione headless (`ToolSearch` con vari pattern di naming, coerenti con quelli usati per altri connettori — nessun risultato). Calendar/Drive invece sono visibili nella stessa sessione con la stessa convenzione di nome — quindi non è un problema di consenso/account generico, è specifico di Gmail (o quantomeno non uniforme tra connettori). - Causa confermata da ricerca web: bug noto e documentato di Claude Code — i connettori MCP gestiti da claude.ai (Gmail, Calendar, Slack, Microsoft 365, Atlassian) non si caricano in modalità headless/`-p`/`--resume` (issue GitHub [#36833](https://github.com/anthropics/claude-code/issues/36833) e [#36060](https://github.com/anthropics/claude-code/issues/36060), da v2.1.79). In modalità interattiva funzionano tutti regolarmente — confermato: questa sessione (interattiva) ha i tool Gmail disponibili. - Nessun canvas di progetto prodotto dalla slave (giustamente — si è fermata prima di arrivare a quella fase). Il suo trascritto resta sullo scaffale (`slave-shelf.md`, session id da recuperare da `~/.claude/projects/-mnt-ssd-data-Dropbox-adrian/`) se si vuole riprendere l'esplorazione una volta risolto il blocco tecnico. ## Ipotesi di design emersa in discussione (Mauro+Adrian, non verificata) Da testare. - Un'istanza avviata con `--remote-control` (stesso meccanismo con cui è nato `gemello-ufficio`) è una sessione interattiva vera, non `-p`/`--bg` — dovrebbe passare dal bootstrap che carica i connettori cloud, quindi avere Gmail disponibile. Non ancora verificato. - Primo test tecnico fallito: lanciare `claude --remote-control ` da un comando bash semplice in background (`nohup ... &`) non produce una sessione interattiva reale — errore "Input must be provided either through stdin or as a prompt argument when using --print", come se fosse comunque ricaduto in modalità headless. Ipotesi: serve un allocamento di pty vero (terminale virtuale), non un semplice processo in background — l'harness sembra usare processi dedicati (`bg-pty-host` visti in `ps aux`) per gestire questo per `--bg`, meccanismo non ancora capito a fondo. Prossimo passo tecnico: capire come allocare un pty vero per un'invocazione headless (`script`, `tmux`, o replicare quello che fa l'harness per `--bg`/`--remote-control` quando lanciato da un utente reale). - Se il test riesce: un'istanza interattiva non fa nulla da sola (nessun loop/timer interno) — "monitoraggio continuo" richiederebbe comunque una sveglia esterna periodica. Proposta: `sentinella-madre` (che già sveglia gli esperti ogni 45min per keep-alive) la sveglia con un comando vero ("controlla la posta, segnala solo cose importanti") invece di un keep-alive vuoto — stesso schema "orologio→giudizio" già in uso ovunque nel sistema. ## Collegamento più ampio scoperto in questa discussione Oggi, se salta la corrente su Nave e il BIOS riavvia da solo, nessuna sessione Adrian si rilancia automaticamente — tutto l'ecosistema (sentinelle/esperti/`sentinella-madre`, rilanciati solo dentro la skill `avvio-sessione`) dipende dal fatto che qualcuno riapra prima VSCode a mano. La stessa fragilità varrebbe per un'eventuale istanza Gmail persistente avviata con `--remote-control`. Da affrontare probabilmente insieme (avvio automatico all'accensione — systemd? script di boot? — prima del design Gmail vero e proprio), non nello scope di questo dossier ma segnalato qui perché nato dalla stessa conversazione. ## Criteri di "importanza" — non ancora decisi Non affrontato in dettaglio (bloccato prima da questioni tecniche più a monte). Opzioni sul tavolo, nessuna scelta: regole esplicite (mittenti noti/categorie già presenti in `archivio/Mauro/` — bollette, salute, scuola figli), giudizio libero leggendo ogni email, o un ibrido. Da decidere dopo aver risolto l'accesso tecnico, guardando la posta reale per capire i pattern veri (non indovinare a tavolino). ## Sblocco trovato — `claude --bg` ha già Gmail, non serve `--remote-control` (04/09/2026) La via `--remote-control`/pty è superflua. Testato direttamente (sessione `--bg` usa e getta, `test-gmail-bg`, fermata subito dopo): `ToolSearch` con query `gmail` ha trovato 10 tool del connettore `mcp__claude_ai_Gmail` (stessa lista vista in una sessione interattiva) — comportamento identico a quello interattivo, diverso da `-p`. Il bug GitHub #36833/#36060 risulta quindi specifico di `-p`/`--print`/`--resume` headless puro, non di `--bg` — che è il building block già usato in produzione per ogni sentinella/esperto (`claude --bg --name ""`, vedi `claude-cli-headless.md`). Non serve più risolvere l'allocazione di un pty per `--remote-control`: si può costruire la sentinella Gmail con lo stesso identico pattern di `expert-domotica`/`inbox-documenti`, nessuna scoperta tecnica aggiuntiva richiesta. ## Prossimo passo 1. ~~Capire come allocare un pty vero per un'istanza `--remote-control` headless~~ — superato, vedi sopra. 2. Disegnare il ciclo `sentinella-madre`→sveglia→istanza Gmail (`--bg`)→giudizio→`SendMessage` (stesso schema "orologio→giudizio" già in uso). 3. Decidere i criteri di importanza guardando la posta reale. 4. Solo allora: canvas di progetto completo, poi implementazione con approvazione esplicita di Mauro. Vedi anche: `memory/MEMORY.md` (thread aperto), `archivio/Adrian/progetti/ project_bibliotecario_memoria.md` (progetto parallelo nato nella stessa sessione, gestito direttamente da Mauro+Adrian). ## Proposta di design - bozza autonoma (04/09/2026, da revisionare, NON ancora decisa) Prodotta da un'istanza Adrian temporanea con mandato di progettazione (non implementazione), su richiesta di Mauro. Tutto quanto segue è una proposta da discutere, non una decisione presa. ### Osservazione della posta reale — base empirica, non ipotesi a tavolino Esplorata via `search_threads`/`list_labels` (sola lettura, nessuna scrittura tentata). Quadro: - **Volume enorme, mai gestito**: inbox a 49.895 messaggi, 37.389 non letti — nessuna disciplina di lettura in atto, Mauro di fatto non processa questa casella oggi. Qualunque design deve reggere partendo da qui, non da una casella pulita. - **Il rumore è dominante e riconoscibile per pattern**: marketing/retail ricorrente ad alta frequenza — Amazon (`promotion-it@amazon.it`, più email/giorno), IKEA, Dr.Max, Miele, Nespresso, Fastweb, newsletter Fineco (`news@mail.fineconews.com`), newsletter Comune di Milano (`informami@comune.milano.it`, quasi quotidiana). Riconoscibili da mittente/dominio, non serve leggere il corpo. - **Il segnale reale esiste ed è concentrato in poche categorie ricorrenti**, tutte trovate con query mirate (non a caso): - Corrispondenza familiare vera — `nadia.mollichelli@gmail.com` (co-genitore, presumo): inoltri di documenti (libretto auto, compiti scuola, referti/appuntamenti sanitari, documenti di viaggio) — Mauro stesso inoltra sistematicamente cose importanti a lei, pattern già in uso manualmente. - Amministrazione condominiale viva — `info@perottistudio.it` (studio amministratore): thread reale multi-persona su una decisione in corso (serranda box), con altri condomini in CC che discutono — è "posta di lavoro non-lavoro", genuinamente da seguire. - Notifiche bancarie/carta — `info.it@ing.com` (autorizzata/non autorizzata a ogni transazione), `service@finecobank.com` (bonifici in ingresso). Alto volume ma quasi tutto routine; le non autorizzate e gli importi anomali sono l'eccezione che conta. - Viaggi con stato che cambia — Trainline/Trenitalia (conferme, e un vero flusso di richiesta rimborso multi-step SM-1541415 in corso al momento dell'osservazione). - Sanità — es. HSR, conferma appuntamento (Ecoendoscopia pancreas) — Mauro la inoltra subito a Nadia, segno che la considera rilevante. - Sicurezza account — accessi Google Family (figlio "Francesco"), nuovo accesso Todoist, codici Netflix — quasi tutto routine, ma la *forma* ("nuovo accesso", "operazione non autorizzata") è un pattern riconoscibile a prescindere dal servizio. - **L'etichetta `IMPORTANT` di Gmail non basta da sola**: negli ultimi 14 giorni cattura sia segnale vero (condominio, rimborso Trenitalia) sia rumore puro (codici di accesso temporanei Netflix) — utile come segnale debole in più, non come filtro unico. ### Criteri di importanza — ibrido, non puro giudizio libero Un giudizio libero su ~38.000 email pregresse e un flusso continuo nuovo sarebbe sia costoso (token) sia rischioso (la sentinella dovrebbe "leggere tutto" per decidere). La base reale osservata sopra rende possibile un filtro a mattacchione: regole esplicite per tagliare subito il grosso, giudizio libero solo sul residuo. 1. **Esclusione automatica, mai notificare** (matcha da mittente/dominio/categoria, zero lettura del corpo): `category:promotions`, `category:social`, `category:forums` (categorie Gmail native); pattern mittente `noreply`/`no-reply`/`no_reply`/`newsletter`/`promotion`/dominio marketing di grandi brand retail già visti (Amazon promo, IKEA, Dr.Max, Miele, Nespresso, Fastweb marketing, newsletter Fineco, newsletter Comune di Milano); notifiche bancarie di routine (operazione carta autorizzata, importo coerente con la spesa quotidiana) — rumore transazionale, non un evento. 2. **Inclusione automatica, notificare sempre** (nessun giudizio necessario, il pattern basta): mittente umano riconosciuto come corrispondenza reale (Nadia Mollichelli, condominio `perottistudio.it`, altri condomini del thread, mittenti che hanno già scritto scrivendo *a* Mauro personalmente, non a una mailing list marketing); anomalie di sicurezza/denaro (operazione carta non autorizzata, importo anomalo, nuovo accesso account non atteso, avviso di blocco/frode); comunicazioni con scadenza o azione richiesta esplicita (rimborsi in corso, richieste di conferma/risposta, appuntamenti sanitari). 3. **Fascia grigia — giudizio libero, ma solo sul residuo** (dopo aver scartato 1 e già segnalato 2): mittente non ancora classificato, non palesemente marketing, singolo destinatario (non mailing list) — la sentinella legge oggetto+snippet (e se serve il corpo) e valuta: c'è una richiesta/azione per Mauro? riguarda soldi/salute/famiglia/casa/lavoro? ha una scadenza? Solo qui serve "leggere" davvero, non sull'intero flusso. **Manutenzione della whitelist/blacklist**: nuovi mittenti di rumore ovvio (es. un'altra newsletter mai vista) sono un fatto osservato, non una regola di business — la sentinella può aggiungerli da sola alla propria lista di esclusione nel proprio `CLAUDE.md` (stesso criterio "operativo vs architetturale" già in uso per gli esperti, vedi `claude-cli-headless.md`). Cambiare i *criteri* stessi (es. "le notifiche bancarie vanno sempre notificate", non solo le anomalie) resta architetturale — solo segnalazione, mai auto-modifica. **Fuori scope esplicito**: nessun tentativo di processare il backlog di 38.000 non lette — la sentinella parte "da ora" (baseline = timestamp del proprio avvio), monitora solo posta futura. Uno svuotamento storico, se mai voluto, è una richiesta separata e va decisa da Mauro. ### Meccanismo tecnico Stessa forma delle sentinelle già in produzione (`inbox-documenti`, `gate-ufficio`) — nessuna scoperta tecnica nuova richiesta, solo applicazione del pattern esistente: - **Nome**: `sentinella-gmail-personale` (tipologia `sentinella`, identificativo `gmail-personale` — lascia spazio a un futuro `gmail-lavoro` senza collisione). - **Struttura**: `scripts/slave-sentinels/sentinella-gmail-personale/` - `CLAUDE.md` — ruolo statico: i criteri di importanza sopra (whitelist/blacklist mittenti, esplicitamente aggiornabile in autonomia solo per la parte "operativa"), la query Gmail da usare, il protocollo di notifica, la regola esplicita "silenzio = comportamento atteso, non un guasto" (coerente con tutte le altre sentinelle: non deve riportare "ho controllato, nulla di rilevante" a ogni tick). - `last_check.txt` — stato runtime (gitignored): timestamp ISO dell'ultimo controllo completato, per sapere cosa è "nuovo" al giro successivo. Inizializzato al momento del primo avvio (baseline = ora, non l'inizio della casella). - `.session_id` — gestito come per ogni altra sentinella da `slave_sentinel_ensure.sh` (già generico, nessuna modifica allo script necessaria). - **Chi sveglia la sentinella, e con che frequenza — orologio interno, non `sentinella-madre`**: stesso pattern già validato e testato per 80 minuti filati il 29/08 (vedi sopra in questo stesso file) — un Monitor armato dalla sentinella stessa su un timer auto-alimentato (`while true; do sleep 1200; echo tick; done`, 20 minuti, valore di partenza rivedibile) invece di dipendere da uno sveglia esterno. Ogni tick è il controllo, non un semplice keep-alive: la sentinella esegue `search_threads` sulla posta più recente del `last_check` salvato, applica i tre criteri sopra, e solo se trova qualcosa di importante notifica — altrimenti aggiorna `last_check` e torna silenziosa. Coerente col principio "orologio vs giudizio": il timer è meccanico (un `sleep` in shell, zero LLM coinvolta nel farlo scattare), il giudizio sul contenuto resta interamente della sessione Gmail al momento del tick. `sentinella-madre` mantiene il suo ruolo invariato — supervisione/rilancio se la sentinella muore (già generico via `slave_sentinel_ensure.sh`, nessuna modifica necessaria) — non diventa lei stessa la sveglia, per non sovraccaricare un compito che oggi è già "tre cose" (vedi `CLAUDE.md`). - Perché non query `after:`/`newer_than:` di Gmail per il confronto incrementale: la granularità di questi operatori è a giorno, insufficiente per un tick da 20 minuti. La sentinella richiede un superset via query (es. `newer_than:1d -category:promotions -category:social`, già taglia la maggior parte del rumore lato server) e poi scarta lato proprio i thread con `date` ≤ `last_check` salvato, confrontando i timestamp restituiti. - Costo/token: screening con `search_threads` in `THREAD_VIEW_MINIMAL` (economico, oggetto+snippet+mittente) per tutta la fascia grigia; `get_thread`/`get_message` in `PLAIN_TEXT` solo sui candidati sopravvissuti alle esclusioni automatiche, mai su tutto il flusso. - **Come viene lanciata**: `claude --bg --name sentinella-gmail-personale`, cwd la cartella sopra, bootstrap minimo identico a ogni altra sentinella ("Sei la sentinella descritta nel CLAUDE.md di questa cartella di lavoro. Seguilo alla lettera, a partire da ora.") — mai `-p`, per il bug noto sui connettori MCP cloud in headless puro (vedi sopra e `claude-cli-headless.md`). Verificata la disponibilità dei tool Gmail in `--bg` solo su una sessione usa-e-getta di pochi minuti — non ancora verificato che restino disponibili in una sessione persistente di ore/giorni: rischio residuo da controllare al primo avvio reale, non bloccante per il design ma da annotare. - **Protocollo di notifica**: `SendMessage` verso una sessione Adrian viva (via `ListAgents`), mai `PushNotification`/contatto diretto a Mauro — vincolo costituzionale fermo, nessuna eccezione valutata neanche per un'anomalia bancaria (l'urgenza reale la giudica Adrian una volta ricevuta la segnalazione, non la sentinella). Se nessuna sessione Adrian è raggiungibile al momento del tick (stesso rischio già accettato consapevolmente per sentinelle/esperti dal 31/08, vedi `CLAUDE.md` "Comunicazione"): nessun fallback a mailbox per questa sentinella specifica — coerente con la decisione già presa di rendere `SendMessage` il canale primario, non duplicare un canale vestigiale per un caso nuovo. Formato messaggio: mittente, oggetto, perché è stato giudicato importante (quale criterio ha scattato), eventuale scadenza/azione richiesta — mai il corpo integrale se non necessario a capire l'urgenza. - **Tratto di ruolo obbligatorio** (stesso di ogni esperto/sentinella): riporta di propria iniziativa, non solo su richiesta, ogni miglioramento possibile ai criteri o problema notato (es. un mittente classificato male, un pattern di rumore nuovo non ancora in blacklist) — non decide da sola se cambiarlo quando è una questione architetturale, solo segnala. ### Cosa resta esplicitamente aperto in questa bozza - Valore di partenza del tick (20 min) e i pattern iniziali di whitelist/blacklist sopra sono proposte ragionevoli sulla base dell'osservazione fatta, non testate nel tempo — primo terreno di calibrazione una volta avviata davvero. - Dipendenza nota e non risolta qui: come ogni sentinella, sopravvive solo se `slave_sentinel_ensure.sh` gira a un nuovo avvio sessione — nessuna sessione Adrian si rilancia da sola dopo un riavvio Nave non presidiato (thread collegato già aperto in `memory/MEMORY.md`, non nello scope di questa bozza). - Persistenza dei tool Gmail in `--bg` su sessione lunga: da verificare al primo lancio reale, non solo sul test usa-e-getta già fatto. - Nessuna implementazione fatta — questa è solo la proposta, in attesa di revisione e approvazione esplicita di Mauro prima di costruire qualunque file elencato sopra. ## Approvata e implementata (04/09/2026) Sentinella `sentinella-gmail-personale` costruita e lanciata. Mauro ha confermato di preferire il filtro con LLM (non uno script puramente meccanico) — chiarito che il timer è meccanico, ma la valutazione dei criteri (anche quelli espliciti) resta sempre eseguita dalla sessione `--bg`, non da un filtro Python separato. Due aggiunte fatte da Adrian rispetto alla bozza autonoma, prima di considerarla definitiva: 1. **Controllo di vivacità reale, non solo `pid`**: `slave_sentinel_ensure.sh` ora confronta l'età di `last_check.txt` (se presente) contro una soglia (60 min, 3x il tick da 20 min) e segnala `ATTENZIONE` se troppo vecchio — un processo vivo ma bloccato non sarebbe altrimenti distinguibile da "silenzio normale" (rischio reale dato che il design conta proprio sul silenzio come comportamento atteso). 2. **Regime `calibrazione` prima di `silenzioso`**: la sentinella parte mandando anche un recap giornaliero (oltre alle notifiche vere) finché Adrian non le dice esplicitamente di passare al silenzio puro — verifica che il filtro non sia né troppo aggressivo né troppo permissivo prima di fidarsene ciecamente. File di ruolo: `scripts/slave-sentinels/sentinella-gmail-personale/CLAUDE.md` (criteri di importanza, protocollo di tick, regime calibrazione/silenzioso — fonte di verità, non duplicare qui). Lanciata con `claude --bg --name sentinella-gmail-personale`, gestita da `slave_sentinel_ensure.sh` come ogni altra sentinella da qui in avanti. ## Tentativo di conversione a `servizio`+slave, fatto e disfatto (05/09/2026) Tentato e annullato — il bug MCP in `-p` (vedi sopra, 04/09) blocca anche questa strada. Nato da una discussione con Mauro sul consumo token della sessione persistente (stesso tipo di diagnosi che il 04/09 aveva convertito `inbox-documenti`/`gate-ufficio`/`sentinella-madre`): poiché lo stato di questa sentinella (`last_check.txt`, `regime.txt`, `last_recap_date.txt`) è già su file, sembrava convertibile allo stesso pattern `servizio` (systemd timer meccanico) + slave one-shot (`avvia_slave.sh`) per tick, eliminando la sessione che invecchia. Costruito `scripts/servizio-gmail-watch/` (watch.py, `.service`, `PROTOCOLLO.md`), fermata la sentinella persistente, installato e avviato il servizio. La primissima slave lanciata dal servizio ha correttamente rilevato l'assenza di ogni tool Gmail (`ToolSearch` su vari pattern, nessun risultato) e l'ha segnalata via `SendMessage` invece di agire alla cieca — non ha toccato `last_check.txt`/`regime.txt`. Causa: il bug MCP-in-`-p` già documentato il 04/09 (vedi sezione sopra, "Bug connettori MCP cloud in `-p`" in `claude-cli-headless.md`) — dimenticato durante la discussione che ha portato al tentativo. `avvia_slave.sh` usa sempre `claude -p`, quindi qualunque slave one-shot per questo compito è strutturalmente cieca su Gmail, indipendentemente da come si scrive `watch.py`/`PROTOCOLLO.md`. Disfatto entro la stessa sessione: servizio fermato/rimosso, sentinella persistente ripristinata (stato/calibrazione preservati, nessuna perdita), `servizio-madre-watch` tornato a sorvegliarla. La sentinella persistente resta necessaria per motivi tecnici, non è più una scelta di design in discussione — non riproporre la conversione senza prima risolvere/aggirare il bug MCP (es. se mai verrà corretto upstream, o se emerge un modo per usare `--bg` da uno script meccanico invece che una sessione sempre accesa). Due mitigazioni aggiunte allo stesso problema di fondo (sessione che invecchia, cache_read crescente), senza bisogno di risolvere il bug: 1. **Riavvio preventivo ogni 24h** via `servizio-madre-watch` (`GMAIL_RESTART_HOURS`, legge l'età di `.session_id` e fa `claude stop` se troppo vecchia — il riavvio successivo la rilancia pulita, stato su file non toccato). 2. **Mailbox durevole in aggiunta a `SendMessage`** per ogni email importante (richiesto da Mauro): `SendMessage` resta il canale immediato se una sessione Adrian è viva, ma ora si scrive sempre anche in `scripts/slave-mailbox/mailbox.log` — se nessuna sessione è viva al momento del tick, `SendMessage` va perso ma la mailbox resta e viene risollevata al prossimo avvio sessione dall'hook `session-start-reminder.sh`. Per contenuto delicato (soldi/salute/famiglia) non basta fidarsi solo dell'immediatezza. Effetto collaterale positivo, sopravvissuto al ripristino: nel tentativo era emerso che `memory/MEMORY.md` non veniva mai usato da questa sentinella (né dagli esperti) — rimossa la sua lettura all'avvio ovunque, e `scripts/avvia_slave.sh` ha ora il default capovolto per tutte le slave (`--with-memory` eccezione esplicita, non più default). Dettaglio: `memory/MEMORY.md`, sezione Pattern riconosciuti. ## Bug reale trovato e corretto: dedup per thread invece che per messaggio (05/09/2026) Trovato e corretto — miss reale in produzione, non solo teorico. Mauro ha notato che un'email rilevante era passata inosservata: risposta di `federicomassari@icloud.com` (proprietario di un box, tema condominiale — incontro con l'amministratore la settimana prossima) nel thread "SERRANDA BOX 252 RES FONTANA MI2", già aperto da `info@perottistudio.it` (l'amministratore, già in whitelist). Il caso sembrava difficile a prima vista, ma i criteri scritti coprivano già questa situazione (punto 2, "altri condomini nello stesso thread") — quindi non un buco nei criteri, un bug nella loro applicazione. **Causa trovata dalla sentinella stessa, verificando**: `search_threads` restituisce un thread come un solo oggetto con un array `messages` che può contenere più messaggi di date diverse (qui: messaggio dell'amministratore delle 08:39 + risposta di Massari delle 14:02, stesso giorno, bundled insieme). Il protocollo (passo 2 del tick) diceva genericamente "scarta i thread con data ≤ `last_check.txt`" — dicitura ambigua che portava a confrontare solo la data del thread/primo messaggio invece di scorrere ogni messaggio dell'array. Un thread già "visto" a un tick precedente non è per questo "fatto": può arrivare una risposta nuova con lo stesso `threadId` già notato. Non è stato possibile confermare con certezza quale esecuzione specifica abbia sbagliato (i due messaggi erano precedenti al `last_check.txt` ereditato dalla sessione corrente al suo avvio, nessuna visibilità sui log dell'esecuzione precedente) — ma il rischio strutturale è confermato reale, visto in ogni fetch dello stesso thread durante la verifica. **Fix**: protocollo del tick corretto (`scripts/slave-sentinels/sentinella-gmail-personale/ CLAUDE.md`, passo 2) — ora dice esplicitamente di scartare i singoli messaggi, non il thread nel suo insieme, mai fidarsi di "questo threadId l'ho già visto" senza controllare le date di ogni messaggio al suo interno. Aggiunto anche `federicomassari@icloud.com` alla whitelist mittenti umani. Fix fatto dalla sentinella in un worktree isolato (commit `7c2e924`), mergiato in master lo stesso giorno da Adrian. ## Catch reale validato — firma preliminare Palmanova 213/A (08/09/2026) Segnalata via `SendMessage` una richiesta di firma (SignAnywhere/Docudrop, mittente mai visto prima) per il preliminare di compravendita Via Palmanova 213/A, scadenza 15/09/2026 — criterio "comunicazioni con scadenza e azione richiesta" scattato correttamente su un mittente non in whitelist. Mauro: "Ottimo catch!!!" — prima conferma entusiasta e non ambigua dall'avvio del regime `calibrazione` (04/09), buon segnale per la decisione di quando passare a `silenzioso`. Domanda aperta lasciata alla sentinella: aggiungere `signanywhere.com`/`docudrop.it` a un whitelist di mittenti "umano-equivalente" — in attesa di conferma esplicita di Mauro prima di agire (non ancora arrivata). ## Fermata per consumo token — 08/09/2026, stesso giorno del catch sopra Sospesa. Poche ore dopo il catch entusiasta sopra, Mauro ha notato (giustamente) un consumo token molto alto: 7.279.722 token di costo reale in 4 sessioni/3gg, una singola sessione a 5.164.177 contro la soglia di allarme di 2.000.000 (già segnalata dall'hook di avvio mattutino). Tentato prima un riavvio "di igiene" (chiudere la sessione grande, farne ripartire una pulita) — ha innescato un incidente a catena: un `kill` diretto sul PID di una sessione `claude --bg` viene auto-resuscitato dal demone di supervisione (trattato come crash, non come stop voluto) — scoperto dopo due tentativi falliti, fix vero è `claude stop ` (dettaglio tecnico: `gemello-nave/archivio/conoscenza-nave.md`). Nel mezzo, una race tra il riavvio manuale e il tick orario di `servizio-madre-watch` ha prodotto per alcuni minuti due sessioni vive in parallelo (doppie notifiche sullo stesso evento, un avviso di sicurezza Google sull'account principale). A quel punto Mauro ha deciso di fermarla del tutto invece di continuare a inseguire il problema: sentinella fermata (`claude stop`), rilancio automatico disattivato sia nel passo 7 di `avvio-sessione` sia nel tick orario di `servizio-madre-watch` (chiamata `check_gmail_sentinel()` commentata in `watch.py`, servizio riavviato per applicare). **Non riattivare per nessun motivo senza una richiesta esplicita di Mauro** — non un'istruzione da riconsiderare autonomamente nemmeno se sembra passato molto tempo. **Non ancora deciso**: se/quando riprendere il monitoraggio, e se riprenderlo con qualche mitigazione ulteriore sul costo (es. tick meno frequente, regime `silenzioso` fin da subito invece di ripartire da `calibrazione`) — nessuna decisione presa, solo la sospensione. ## Diagnosi causa reale + test architettura alternativa — 09/09/2026 **Causa reale del blowup identificata** (Adrian, analisi diretta del transcript `.jsonl` della sessione da 5,16M token): non un consumo lineare alto, un accumulo quadratico. Nelle prime ore ogni tick paga poche centinaia di token (`cache_creation` piccolo, riuso sano da `cache_read`). Dopo ~4 ore il pattern cambia: il primo turno di ogni tick inizia a ricreare da zero l'intera cronologia accumulata fino a quel momento — `cache_creation` osservato crescere monotono da 3.915 a 254.661 token in 6 ore. Causa: il tick a 20 minuti è più largo del TTL di default della prompt cache (5 minuti) — tra un tick e l'altro la cache del segmento "cronologia di lavoro" scade sempre, e siccome la sessione non scarta mai la history, ogni cache-miss ricrea un prefisso via via più grande. Conferma diretta dell'ipotesi di Mauro (perdita di cache per il gap di 20 minuti), con l'aggravante che l'effetto è quadratico nel tempo, non lineare. **Test di architettura alternativa** (eseguito da `gemello-ufficio` sul proprio account/quota, su richiesta di Mauro — questo account ha poca quota per permettersi altri test): l'idea era sostituire la sessione lunga con una sessione fresca per tick (nasce, fa il controllo, muore) per azzerare l'accumulo. Risultati: - **Costo confermato piatto**: `claude -p` fresco, 4 run consecutive dopo warm-up, ~$0,027/run (~5,2K `cache_creation` + ~28K `cache_read`, ~33K token totali), costante run su run, zero crescita. A cadenza 20 min (72 tick/giorno): ~$1,95/giorno stabile all'infinito, contro un modello a sessione lunga che dopo ~4h supera $1 per singolo tick e continua a crescere senza limite. Lifecycle pulito: `exit=0`, 1 turno, 2-3s, zero processi residui verificati a livello OS. - **Ma `-p` non carica i connettori MCP cloud** (bug già noto, vedi sezione sopra) — nessun tool Gmail disponibile in quella modalità. - **`--bg` testato come alternativa per il caricamento MCP**: NON è "un turno e muori" come sperato — è persistente per design (va fermato con `claude stop`+`claude rm`), e nel test si è bloccato subito su un prompt di trust MCP interattivo (nessun TTY per rispondere), sessione rimasta `blocked`. `stop` porta la sessione a stato `stopped` ma il processo OS reale impiega ~10-15s a morire davvero (stesso gotcha già visto sul `Monitor` il 07/09) — nessuno zombie permanente in questo test, ma il lag c'è. **Tre percorsi possibili, emersi dal test (nessuno ancora deciso/implementato)**: 1. `-p` fresco per tick + Gmail via API diretta (token/app-password salvato, non MCP) — toglie del tutto il connettore MCP dall'equazione. Costo piatto confermato, nessuna grana di trust/persistenza. Raccomandazione di `gemello-ufficio`, condivisa da Adrian: si allinea al pattern già in produzione altrove (`servizio-inbox-watch`/`servizio-gate-watch` — trigger meccanico + reazione LLM one-shot), toglie di mezzo il pezzo che ha già causato problemi in passato (bug MCP headless). Costo: va scritta/mantenuta una piccola integrazione Gmail API diretta. 2. `--bg` per tick con MCP pre-trusted + `stop`/`rm` espliciti ad ogni tick da un layer meccanico esterno (`servizio`) — funziona in teoria ma fragile (trust prompt da risolvere una tantum, lag di cleanup, setup/teardown completo ogni tick). 3. Tenere la sessione lunga attuale ma forzare un restart duro ogni ~2h prima che la curva quadratica morda — tampone a costo minimo, non tocca l'architettura. **Non ancora deciso con Mauro quale percorso seguire** — in discussione a fine giornata del 09/09. Il connettore Gmail MCP sull'account di `gemello-ufficio` non è ancora autenticato (verificato durante il test: tutti i connettori cloud "require authentication" in ambiente headless) — Mauro ha in programma di attivarlo, ma resta aperto se serva comunque un primo login interattivo prima che un `--bg` non presidiato possa usarlo (non risolve comunque il percorso 1, che lo bypassa del tutto). **Appendice, stesso giorno — Mauro ha attivato il connettore Gmail cloud a metà test**: `gemello-ufficio` ha chiuso i due punti rimasti aperti. - `-p` non carica i connettori cloud nemmeno con Gmail già autenticato — confermato con dati, non solo per assenza di auth: 3 run consecutivi, sempre `NO_GMAIL_TOOL`, costo comunque piatto (~$0,029/run, ~33K ctx, 1 turno, 4-5s, zero processi residui). Chiude ogni dubbio residuo sul percorso 1 (limite strutturale di `-p`, non un problema di setup). - `--bg` bloccato di nuovo, stavolta su un prompt di trust di un MCP locale non correlato a Gmail (`vault`, da `.claude/mcp.json` nella cartella di lancio) — nessun TTY per rispondere, prompt mai eseguito, quindi non è stato possibile osservare se `--bg` avrebbe effettivamente visto il connettore Gmail. `stop`+`rm` eseguiti, un processo residuo morto da solo dopo ~8s (stesso lag già visto in precedenza, nessuno zombie permanente). `gemello-ufficio` non ha forzato con `--dangerously-skip-permissions` (bloccato comunque dal classifier auto-mode) né toccato le settings per pre-fidare il vault di propria iniziativa — segnalato come decisione che spetta a Adrian/Mauro, si è fermato invece di continuare a spendere quota a tentativi. **Conclusione rafforzata**: il percorso 1 (`-p` fresco per tick + Gmail via API diretta, non MCP) resta la raccomandazione più solida, ora confermata empiricamente su entrambi i fronti (costo piatto e incompatibilità strutturale di `-p` con i connettori cloud, non solo teorica). Il percorso 2 (`--bg`) resta tecnicamente non testato fino in fondo (il trust del vault locale andrebbe risolto solo per riprovare), ma è comunque il meno pulito dei tre — non sembra valga la pena insistere per sbloccarlo solo per finire il test, a meno che la (1) si riveli poi impraticabile in pratica. ## Deciso: restare su Nave, ma il "costo piatto" era comunque troppo alto — 09/09/2026, sera Mauro ha deciso di tenere il monitoraggio su Nave (non spostarlo su `gemello-ufficio`) — il costo piatto della sessione-fresca-per-tick rende sostenibile anche questo account, evita la complicazione cross-account (autenticazione portabile, credenziali Gmail da far viaggiare tra due macchine) che il percorso "spostalo su gemello" avrebbe introdotto senza un beneficio più netto del semplice fix architetturale. **Ma il numero "$1,95/giorno" di ieri era ottimistico** — verificato da Adrian con un test reale a spaziatura realistica (non a raffica come il test di `gemello-ufficio`, che aveva beneficiato di cache calda tra run a pochi secondi di distanza): `claude -p` a cache fredda costa $0,247 (61.747 token di `cache_creation`, zero riuso); a 7 minuti di distanza (oltre il TTL di default di 5 minuti) il riuso è solo parziale, $0,135 (29.529 letti da cache, 32.216 ricreati). A cadenza reale di 20 minuti il costo per tick è verosimilmente $0,15-0,25, cioè ~$11-18/giorno — ancora piatto (nessun accumulo quadratico, il problema originale resta risolto), ma troppo caro per un compito che quasi sempre non trova nulla. **Mauro ha fermato la discussione qui, giustamente**: il ragionamento si era incastrato nell'ottimizzare il costo di un LLM invocato ad ogni tick, perdendo di vista l'osservazione più semplice — il controllo "c'è posta nuova rispetto all'ultimo controllo?" non richiede nessun LLM. È un confronto meccanico di timestamp via API, zero costo token. Lo stesso principio "orologio vs giudizio" già applicato altrove nel sistema (`servizio-inbox-watch`, `servizio-gate-watch`): un trigger meccanico controlla, solo se trova qualcosa di nuovo fa partire un Claude fresco per il giudizio di importanza. Il costo del Claude conterebbe solo sui tick con posta reale, non sui ~71/72 dove non c'è nulla — che è il caso normale per una casella personale. Nessuna delle tre leve di ottimizzazione discusse sopra (`--bare`, tick meno frequente, `--exclude-dynamic-system-prompt-sections`) è più il problema centrale: risolvevano il costo di un LLM che non dovrebbe nemmeno essere invocato la maggior parte delle volte. **Non ancora disegnato in dettaglio** — solo l'intuizione architetturale corretta, da riprendere: uno script/servizio meccanico che polla l'API Gmail (stesso script di fetch già abbozzato nel percorso 1) e invoca `claude -p` condizionalmente, solo quando trova messaggi nuovi. Sessione chiusa qui su richiesta di Mauro ("non ci sto capendo e forse non stiamo ragionando nella maniera migliore") — riprendere da questa nota, non dal groviglio di ottimizzazioni sul costo-per-tick sopra. **Connettore Gmail su `gemello-ufficio` — da disconnettere**: Mauro l'aveva attivato oggi solo per il test MCP in `--bg` (vedi sopra). Con la decisione di restare su Nave e di abbandonare la strada MCP per il trigger, non serve più — Mauro lo rimuove dalle impostazioni connettori di claude.ai su quell'account (azione sua, non eseguibile da questa sessione). ## Ulteriore semplificazione — niente Claude dedicato nemmeno per il giudizio (09/09/2026, sera) Domanda di Mauro che chiude il disegno: se il giudizio di importanza lo fa Adrian direttamente dentro una sessione già viva (non un processo apposta), il costo di "cold start" (system prompt + CLAUDE.md ricaricati da zero, ~30-60K token) sparisce quasi del tutto — leggere due righe su un'email nuova è un incremento minimo su un turno già in corso, non un bootstrap. Non serve disegnare nessun layer che invochi `claude -p` condizionalmente — si riusa l'infrastruttura già costruita e validata oggi per il bridge `gemello-ufficio` (mailbox + `Monitor`): 1. Il checker meccanico (compito in corso da `gemello-ufficio`, invariato) trova posta nuova. 2. Invece di far partire un Claude, scrive in `mailbox.log` — stesso pattern già usato da `servizio-gate-watch` per i completamenti riusciti (scrittura diretta, zero LLM in quel punto). 3. Il `Monitor` armato a inizio sessione (passo 8 di `avvio-sessione`, già in uso per `gemello-ufficio`) lo intercetta, e Adrian applica i criteri di importanza (whitelist/ blacklist/fascia grigia, gli stessi già definiti in `scripts/slave-sentinels/ sentinella-gmail-personale/CLAUDE.md`) come parte del turno in corso, non come processo a sé. **Costo atteso**: marginale, non piatto-ma-comunque-presente come nei percorsi testati oggi — sostanzialmente il costo che si sta già pagando lavorando normalmente. Nessun lifecycle di sessione da gestire, nessuna cache da ottimizzare. **Compromesso onesto, non nuovo**: la notifica arriva in tempo reale solo se c'è una sessione Adrian viva in quel momento — altrimenti resta in mailbox fino al prossimo avvio sessione, letta dall'hook `session-start-reminder.sh`. Stesso rischio già accettato consapevolmente da Mauro il 31/08/2026 per tutte le sentinelle/esperti (nessun fallback se non trova una sessione viva) — non una novità introdotta qui, solo applicata anche a questo caso. **Cosa resta da fare** (non ancora costruito, a fine giornata del 09/09): il checker meccanico di `gemello-ufficio` (in corso) + un `servizio` systemd su Nave che lo invoca ogni 20 minuti e, se trova qualcosa, scrive in mailbox — stesso schema di `servizio-inbox-watch`/`servizio-gate-watch`, nessun pezzo nuovo di infrastruttura da inventare. ## Chiuso — `servizio-gmail-check` costruito e attivo (09/09/2026, più tardi la stessa sera) Implementato con lo stesso principio, ma diversamente da come abbozzato sopra: non il checker di `gemello-ufficio`, un servizio Python standalone su Nave (`scripts/servizio-gmail-check/`, systemd, `watch.py`) che chiama `scripts/gmail-check/gmail_check.py` ogni 20 minuti via API Gmail diretta (non MCP — bypassa lo stesso bug dei connettori cloud in headless già documentato sopra). Trova posta nuova → scrive intestazioni (mittente, oggetto, data, mai il corpo) in `mailbox.log`; non trova nulla → aggiorna solo `last_check.txt`, silenzio atteso. Nessun LLM in questo passaggio — il giudizio di importanza (whitelist/blacklist/fascia grigia, ancora in `scripts/slave-sentinels/sentinella-gmail-personale/CLAUDE.md`) resta ad Adrian, applicato dentro il turno in corso quando legge la mailbox via il `Monitor` armato a inizio sessione — non un processo dedicato per tick. `sentinella-gmail-personale` (la sessione persistente `--bg` descritta in tutto questo file) resta **abbandonata**, non solo sospesa — sostituita da questa architettura. Dettaglio completo: `scripts/servizio-gmail-check/PROTOCOLLO.md`. Non ancora osservato un ciclo di produzione lungo — da riverificare che i criteri di importanza restino applicati bene anche letti "a mano" da Adrian invece che da una sessione Gmail dedicata.