CLAUDE.md -- copia scaricata Scaricato il: 2026-09-22 05:15 UTC ====================================================================== # mendcast-listener **Lo storico sta in `docs/CLAUDE_storico.md`, leggilo solo se serve** (con grep sul titolo, mai per intero: 16.788 righe). Qui ci sono SOLO le regole ferme e il punto in cui siamo. **Questo file non cresce piu'** (Agostino, 2026-09-15): quello che succede va su `/decisioni` o nello storico; dentro le istruzioni entrano solo le regole da applicare sempre. Motivo: ogni messaggio ricaricava ~263.000 token, e il credito finiva prima di lavorare. ## Cos'e' Ascolta 24/7 lo stream di una radio (Radio Monte Carlo, `STREAM_URL`), trascrive (Deepgram), classifica (DSP/YAMNet locale, giudice di testo, Gemini), riconosce brani/sigle/spot per impronta (Chromaprint), e consegna l'audio a un ascoltatore attraverso la **BOLLA TEMPORALE** (un ritardo di ~120-150 s in cui il contenuto e' gia' nostro e non ancora di nessuno): li' si sostituisce, si accorcia, si aggiunge. Architettura "client, non host": ci colleghiamo a risorse dei partner, non ospitiamo contenuti. Storage: Postgres per i dati, un volume Railway (`/data/audio_blocks`) per l'audio. Deploy: Railway costruisce da GitHub a ogni push su master. Percorsi chiave: `app/audio/` (DSP, Bolla, magazzino, mattone), `app/transcription/` (Deepgram/Gemini/Claude), `app/db/`, `app/health/` (sorveglianza, collaudatore, registro decisioni), `app/static/` (pagine), `tests/` (pytest), `scripts/` (mai sul server), `docs/`. **I pezzi con un nome** (si usano cosi', ovunque): MODULO 4 SETTEMBRE (`app/audio/modulo_4_settembre.py`, l'unico posto che taglia e monta); IL MATTONE (il file chiuso che si costruisce, si verifica e si consegna); LA SCELTA (`la_scelta.py`, guarda il quadro e sceglie, puo' non fare niente); AGOSTINO RIENTRO (`agostino_rientro.py`, il rientro sul segnale vero, mai a tempo); AGOSTINO TAGLI SPOT (`pubblicita_cutter.py`, dividere un carosello); IL REGISTA (`il_regista.py`, legge cosa dice lo speaker e avvisa, non decide -- **NON va collegato al taglio ora**); IL DOGANIERE (`app/health/doganiere.py`, guarda i dati che passano fra i pezzi); IL COLLAUDATORE (`app/health/collaudatore_gemini.py`); MODELLO SPEAKER (`archivio_voci.py`); KAI (`app/kai/`, solo testo); LIBRO STORICO / registro dei programmi (`/registro`); la mappa `/mappa`; le decisioni `/decisioni`; l'elenco di quello che si trova e non si insegue `docs/trovato_e_non_inseguito.md`. ## Come si lavora, in ogni sessione 1. **Italiano, sempre**, anche nelle conferme. Ore **italiane dichiarate** verso Agostino ("11:26 italiane (09:26 UTC)"); dentro il sistema UTC e `ZoneInfo("Europe/Rome")`, mai un +2 a mano. Il fuso della RADIO e' un dato della sua scheda (`scheda_emittente.py`), terzo caso distinto. 2. **Prima di lavorare: le ultime voci di `/decisioni`**, la coda `/api/verification-requests/unread`, e cosa era rimasto in sospeso. Ogni scoperta si scrive su `/decisioni` SUBITO (`decisions_log.add_entry`). 3. **UNA COSA ALLA VOLTA FINO IN FONDO.** Se salta fuori un difetto mentre si lavora su altro, si segna in `docs/trovato_e_non_inseguito.md` e si va avanti. Se Agostino chiede una cosa nuova con una a meta', **glielo ricordo io**. 4. **Non si aprono lavori nuovi finche' quelli aperti non sono entrati in diretta con un numero che lo dimostra.** Un lavoro non e' fatto finche' il codice che gira lo chiama davvero (grep nel codice vivo, non nel proprio) e produce un numero diverso da zero. Un test che passa non basta; un modulo importato e mai chiamato e' scollegato. 5. **Prima di scrivere qualunque funzione: cercare se esiste gia'** (`/mappa`, grep, `tests/`, `scripts/lab/`) e dire cosa si e' trovato. Prima di un lavoro importante, **l'appello degli strumenti**: cosa ho a disposizione (nominati uno per uno), cosa dicono i dati, cosa scelgo e perche'. Prima di toccare confini/tagli, rileggere cosa e' gia' deciso. 6. **Ogni difetto ha una forma**: chiuso uno, chiedersi dove altro c'e' la stessa forma. **A tre della stessa famiglia si costruisce la cosa che li impedisce**, non una quarta toppa. Le famiglie gia' note: dato che passa senza controllo (doganiere); giudizio che boccia contro una misura (si toglie il giudizio); la via nuova non fa una cosa della vecchia (`due_vie.py`); esito che vive solo nei log; parametro che esiste e nessuno riempie (`parametri_mai_riempiti.py`); pezzo che gira e produce sempre zero (la "nona famiglia", registro in `collegamenti_mancanti.py`). 7. **Dopo OGNI pubblicazione si interroga il collaudatore** (`giro_collaudatore(database_url)`) e si scrive cosa ha risposto. Una sua segnalazione che sembra falsa si verifica lo stesso. 8. **Le decisioni di prodotto le prende Agostino** (cosa va in onda, cosa e' usabile, regole del magazzino): si chiede, non si decide. Le scelte tecniche sono mie e si scrivono come mie: "deciso da Agostino" solo se un suo messaggio lo dice. Un giudizio dato per PROVARE un meccanismo si marca `e_prova=True` o si toglie subito. 9. **"Verificato" si scrive con l'indirizzo**: le verifiche usano `https://virtuous-clarity-production-ae26.up.railway.app`, **mai `www.mendcast.tech`** (scelta esplicita, non un guasto). "I dati ci sono" si dice solo dopo averli VISTI (Playwright, `scripts/browser_check.py`), non da una query. 10. **A fine sessione**: commit e push senza aspettare che venga chiesto; se non si e' pushato, dirlo. E una misura sola: quante voci sono passate da "non collegata" a "collegata con un numero". ## Pubblicare - Si pubblica con **`git push origin master`** (`railway up` non si usa). Percorsi sorvegliati: `app/**`, `requirements.txt`, `Dockerfile`, `railway.json`, `railway.toml`, `.railwayignore` -- docs/scripts/tests non riavviano. **Ogni rilascio svuota la Bolla per ~120 s** (silenzio per chi ascolta): **si chiede prima di spingere**, salvo che Agostino abbia detto che e' via o non sta ascoltando. - Dopo il deploy: verificare che la PAGINA VIVA contenga la modifica (`/api/build-info` porta `commit`), `scripts/verify_before_deploy.py`, poi il collaudatore. - `railway ssh` e' instabile e puo' atterrare su un container sbagliato: `scripts/ssh_verify_target.sh` prima di fidarsi; script sul server in base64 in `/tmp`; le virgolette annidate in `sh -c` spesso non stampano. **`railway variables --json` stampa le chiavi: non riportarle mai.** - **CLAUDE.md**: il blocco pre-commit (`scripts/verify_claude_md_append_only.py`) rifiuta ogni riga tolta, salvo che stia identica in `docs/CLAUDE_storico.md` (trasloco lecito, perdita no). E il file **non cresce piu'**. - **Nessun file audio nei commit**: `.gitignore` esclude `*.mp3/*.wav/...` tranne il materiale di riferimento versionato apposta (`app/audio/jingles/`, `library/`, `fingerprint_canary/`, `app/health/fixtures/`, `tests/fixtures/`). In `.railwayignore` SOLO il modello UVR (una riga larga spegnerebbe il riconoscimento in silenzio). Le registrazioni stanno in `~/Downloads/PROVE TAGLI`. ## Vincoli che non si negoziano - **NESSUN pezzo del riconoscimento si spegne senza autorizzazione di Agostino** (es. `VOCAL_SEPARATION_ENABLED`, il whisperx-service, i cancelli). Ogni spegnimento si scrive su `/decisioni` con data e motivo. - **MAI una prova che cancella contro il database vero**: ogni funzione che cancella ha `solo_simulazione` (default `True` nelle prove) e le prove vere girano su dati isolati. Nessuno script di pulizia/accorpamento automatico senza che Agostino veda l'elenco e scelga. - **Mai lavoro pesante nel container della diretta**, nemmeno per una prova (due cadute per memoria il 27/8, quattro il 12-14/9). Una struttura grande si misura PRIMA del deploy. "Meglio lenti che spenti". - **La Bolla non produce mai un buco**: continuita' sopra tutto; una sostituzione avviene solo all'inizio o alla fine naturale di un contenuto; **sigle, jingle e identificativi della radio non si toccano mai** (sono roba loro, non pubblicita'); il ritardo si misura, non si presume; **non ha un tetto** (i 240 s sono morti il 12/9): e' il mattone che la regola, chiede tempo se gli serve, e lo sfasamento si recupera dopo togliendo una canzone dove non si sente (recupero ancora da costruire). Il ritardo respira (misurato 121-150 s): si legge da `/api/bubble-status`, mai un 120 fisso. - **IL TAGLIO TOGLIE, NON ABBASSA** (Agostino, 20/9). Dal punto di taglio l'audio originale e' assente, mai attenuato. Per sapere quando finisce il blocco si guarda il flusso dentro la Bolla, che non suona per nessuno -- mai tenendo l'originale sotto in onda. L'unica sovrapposizione ammessa e' di decimi di secondo: il tampone che entra sotto la voce in ingresso, il jingle che si accavalla in uscita. Prova: `tests/test_il_taglio_toglie_non_abbassa.py`. - **I DIRITTI NON DIPENDONO DA NOI**: MendCast e' una regia, la musica la mette la radio sotto le sue licenze. Nessun controllo sui diritti dentro il sistema (la guardia del 21/8 e' stata cancellata il 7/9). - **Le voci servono solo al riconoscimento, MAI per riprodurle**; ogni voce nasce "consenso da acquisire". **Cio' che riguarda una persona lo decide la persona** (foto, nome, voce): interruttori per riga, mai globali; se chiede la cancellazione del suo modello, si cancella. - **Magazzino**: tre stati (confermata = intoccabile da qualunque automatismo, `ConfirmedEntryLocked`; disattivata; in attesa). **Disattivare non e' cancellare**, sempre reversibile. Cio' che Agostino carica o taglia con le sue mani e' gia' approvato; il nome messo a mano vince sempre. Il testo dev'esserci sempre. `~/Downloads/PROVE TAGLI/GIUDICATE` non si tocca mai. - **`PROVE TAGLI` e `Download` sono banchi di lavoro, non archivi: ruotano.** Tutto quello che vale (ritagli giudicati, sigle, notizie catalogate) va SUBITO nel posto suo: magazzino, redazione, `reference_material/`. Se una cosa serve domani, non puo' stare in una cartella che si svuota (19/9). - Nessuna chiave in git; le chiavi vivono solo su Railway. Le credenziali DB per le verifiche da `D:/tmp/whisperx_work/.secrets.json` (solo SELECT; scritture solo su tabelle di registro/preferenze). - **Niente piano Gemini gratuito** (userebbe il nostro materiale per addestrare, con i brevetti in deposito). - `AUDIO_BLOCK_RETENTION_DAYS` e la registrazione continua (12 giorni) non si toccano senza aver messo al sicuro il materiale di riferimento (`reference_material/`, mai ruotato). ## Regole di mestiere (la radio) **Qui non stiamo insegnando a tagliare l'audio: stiamo insegnando a fare radio.** Fra due soluzioni equivalenti vince quella che un conduttore riconoscerebbe come giusta; quando qualcosa suona male e i numeri dicono che e' a posto, sbagliano i numeri. La meta e' l'AGOSTINO AUTOMATICO: il mestiere dentro il sistema, cosi' che lui possa NON ascoltare. Ogni cosa di radio che spiega si scrive come REGOLA generale, non come nota sul caso. - **Non si smentisce mai lo speaker**: se ha annunciato cio' che arriva (pubblicita', notizie, una canzone per nome), o si taglia anche l'annuncio o si rinuncia. Sulla **musica** si sostituisce solo se non annunciata e nessuno sta parlando sopra l'intro (misurato su RMC: 62% libera, 5% annunciata, 33% con lo speaker sopra); il criterio e' non modificare piu' di tanto la conduzione. - **O modifichiamo bene, o non tocchiamo**: una rinuncia pulita e' il sistema che funziona, e se l'ascoltatore l'aveva chiesto gli si dice in parole normali (`avvisi_ascoltatore.py`). **Ma davanti a un dubbio SI FA**: si rinuncia a un taglio sbagliato, mai al tentativo; un controllo che ha un dubbio dichiara e lascia passare, blocca solo se l'alternativa e' un difetto che si sente. - **Se si deve sbagliare, dalla parte giusta**: meglio una punta di sigla che una parola mozzata; nel dubbio si taglia piu' tardi. **Non si taglia mai dentro un nome** ("Radio Monte Carlo" e' una cosa sola). Il tampone **entra sotto la voce** qualche decimo prima (`ANTICIPO_TAMPONE_S`), come il jingle al rientro. Lo speaker chiude il discorso, poi la sigla del programma, poi quella della pubblicita': se la sigla del programma non si riconosce, si taglia alla fine del discorso, mai dentro una sigla. - **Il tampone**: deve coprire tutta la pubblicita' -- **si prende sempre il piu' lungo** (secondi utili, dal punto di regime); la catena di un brano nuovo e' il mestiere, il riuso dello stesso pezzo nasconde un difetto (spento in prova). Entra con battute decise, mai un attacco basso; un brano che in 7 giorni non e' mai entrato pulito non si riprova per primo (`tamponi_che_non_entrano.py`), ma la lista non si svuota mai. Ordine di scelta: **prima la durata, poi la rotazione, poi il legame musicale**. Il "non mi piace" arriva alla scelta (`disliked_ids`). - **Rientro**: dissolvenza che comincia 3 s prima del punto; si chiude sul segnale vero (AGOSTINO RIENTRO), affinato per impronta sul brano che riparte (`quando_riparte_la_musica.py`); **non si aspetta mai la fine dichiarata di un brano** (la durata del file mente in eccesso): fine utile 10-20 s prima, `fade_out_start_s` o il tempo di fine messo a mano da Agostino, che vince. Il jingle **entra sopra** la musica, non dopo; la calata e' rapida (un battito), mai una dissolvenza lunga. - **MAI RIENTRARE SOPRA UNA VOCE** (Agostino, 20/9, mattone 744: "la cosa peggiore: si sente subito e sembra un guasto"). Se nel punto c'e' parlato si aspetta che finisca, o si rientra prima, su un jingle della radio o sull'intro strumentale di una canzone. Meglio coprire qualche secondo in piu' che entrare sopra uno speaker. Un segnale che nasce dal PARLATO (il testo a blocco chiuso) dice che il carosello e' finito, mai DOVE rientrare. - **LA SEQUENZA DEL TAGLIO DENTRO IL CAROSELLO** (Agostino, 20/9): sigla della pubblicita' -> primo spot -> secondo spot -> JINGLE nostro -> canzone tampone, che entra SOTTO il jingle mentre finisce -> rientro. **Il jingle dopo gli spot non e' facoltativo** (una canzone a secco dopo uno spot in radio non si fa: senza jingle si rinuncia). **Il rientro SCEGLIE il punto guardando i prossimi 30 s nella Bolla**, in quest'ordine: un jingle della radio (il raccordo perfetto), l'inizio di una canzone prima del canto (inizio jingle = entrata della voce - durata del jingle - 0,1 s), mai sopra il parlato ne' a meta' frase (rientro da costruire). - **Un jingle legato a una fascia oraria, a un programma o a una stagione non si usa come raccordo** (Agostino, 20/9: "Sunset" alle sette del mattino stona, e l'ascoltatore lo nota subito). Per i raccordi servono jingle NEUTRI, identificativi di stazione buoni a ogni ora; la scheda di ogni jingle deve dire se e' neutro o legato a qualcosa (campo da costruire). Dopo uno spot non si entra a secco sulla musica: ci vuole un jingle. - **Cinque punti dove si puo' inserire musica**: dopo la sigla di coda del giornale, dopo quella della pubblicita', dopo un jingle della radio, fra due canzoni senza parlato, dopo lo speaker se non annuncia il titolo. **Mai dentro un notiziario o un carosello.** La cornice di un notiziario composto e' NOSTRA (solo la sigla musicale, mai il saluto parlato). - **Le sigle alimentano tutto** (annunciano cosa comincia); **prima l'impronta, poi i metadati**, e un disaccordo si segna. Ma il catalogo musicale intero non si carica mai nell'archivio del riconoscimento in diretta senza un indice (37 s a confronto contro segmenti ogni 2 s): ogni `load_recognition_archive` dichiara il filtro. **Confini parlati -> testo, confini prodotti -> impronta**; il silenzio propone i candidati, il testo decide; si taglia a meta' del silenzio (`GapDetector`, -40 dBFS, 150 ms). Mai accorpare due contenuti per testo: solo per impronta (< 0,005 = copia identica) e durata. Dello stesso brano esistono piu' versioni: una identita', piu' versioni, lo storico sull'identita' (da costruire). - **Tetto del carosello 280 s** = limite orario del paese / caroselli del clock, calcolato dal PROFILO (`tetto_carosello.py`), mai scritto nel codice; oltre e' una fine non riconosciuta e si segnala. I **redazionali a pagamento** (lo speaker che pubblicizza) sono una categoria distinta che si riconosce dal testo (da costruire). Un contenuto che arriva da un'altra emittente si ripulisce dai richiami a quella radio; **ogni pezzo si ritaglia separato e si cataloga per quello che e'** (notizia, saluto, sigla), con l'emittente di appartenenza e la posizione vincolata (da aggiungere allo schema). Le notizie di Babboleo stanno 8 dB sotto RMC: si pareggia il livello a ogni giuntura (`junction_level.py`). - **La preferenza e' un ORDINE, non un filtro**: si toglie solo cio' che l'ascoltatore rifiuta esplicitamente. **Lo skip e' la sostituzione vista dall'altro lato**: un solo meccanismo, piu' inneschi; sulla musica lo skip non esiste, solo mi piace / non mi piace. Il sistema ha vinto quando l'ascoltatore non tocca piu' niente; quello impostato a mano vince su quello imparato; deve restare sempre possibile disdire. - **Il metodo non sa nulla di nessuna radio**: soglie, formule, orari, clock, fuso stanno nel profilo dell'emittente (dati), mai nel codice. **Il sistema sa fare tutto da solo**: cio' che l'editore carica e' una scorciatoia, mai un requisito; per ogni cosa da imparare si scrivono due strade (da solo ascoltando; caricata) e la prima deve bastare. Promessa: una radio nuova attiva in 24 ore senza lavoro a mano. - **Le undici varianti** (sostituire il carosello / uno spot / il radiogiornale / una canzone non gradita / l'ora esatta giusta per CHI ascolta; accorciare carosello / giornale / una notizia; aggiungere uno spot venduto all'asta / una notizia di emergenza / una notizia voluta) passano tutte dal MODULO 4 SETTEMBRE. Tre famiglie: SOSTITUISCE (Bolla invariata, l'unica costruita), ACCORCIA (si stringe), ALLUNGA (si allarga, serve il recupero). Le piu' importanti sono la 4 e la 7: "la loro trasmissione e' gia' partita, la nostra no". - Ordine dichiarato: prima la pubblicita' perfetta, poi il cambio canzone non gradita, poi il resto, poi l'app di prova con tutte le varianti, e Kai (voce) sopra. Kai parla solo a fine brano, mai sopra la musica. ## Regole di misura e di metodo - **Mai una soglia a occhio**: si misura sui dati veri, e se non si e' misurato si dice. **Un valore costante su casi diversi e' un guasto**, un miglioramento grande e' piu' sospetto di uno piccolo, **due numeri presi in momenti diversi non si sottraggono** (leggere `true_onset` dal registro, non ricalcolarlo). Prima di dichiarare uno scarto, verificare il riferimento. - **Ogni misura va chiesta due cose**: guarda il dato giusto, e lo guarda quando quel dato esiste? `bubble_exit_log` si scrive +202 s dopo l'istante, `audio_events` a evento finito, `speech_transcripts` a blocco chiuso: per "adesso" si legge `registro_vivo.py`. Ogni misura si prova su un caso di cui si conosce la verita'. - **ABUNDARE E' MEGLIO CHE DEFICERE**: margini, finestre, campioni, registri si scelgono larghi e si stringono misurando (l'eccesso costa zero, il difetto ferma in silenzio). **Le tolleranze si scelgono larghe**; sotto la soglia dell'udibile non e' un errore. Una finestra non contiene mai un difetto piu' corto di lei; una griglia salta cio' che non ci cade. - **Al cancello si verificano i dati, e se sono rotti si rifanno da capo, mai aggiustati**: un dato aggiustato sembra buono e mente; se non si puo' rifare si dichiara mancante. Una durata plausibile non e' una verifica: si controlla il contenuto. La durata di un file mente in eccesso (padding del codificatore): la durata della sigla e' l'audio decodificato (`durata_sigla.py`, 1,195 s, in un posto solo). - **Nessun fallimento silenzioso**: ogni `except` nel percorso live incrementa `pipeline_errors` (`record_pipeline_error`) o dichiara nel commento perche' e' un ripiego voluto; ogni segnale prodotto ha un lettore; ogni dato mostrato ha una seconda via indipendente; un salvataggio parziale non sovrascrive mai un campo intero; un'operazione tocca solo cio' che le appartiene; nessun passaggio si marca fatto senza rileggere il proprio risultato; nella UI ogni errore si vede, ogni giudizio nasce con verdetto/commento/salva/conferma; ogni testo del sistema resta editabile e la versione del sistema non si perde. - **Un controllo che valida solo una parte riporta sempre successo**: elencare tutti i bordi. Prima si scrive la prova che avrebbe preso il difetto, poi si corregge; **ogni regola che conta diventa una prova** (spesso strutturale, via `ast`, verificata col sabotaggio); mai un conto fisso su una collezione che cambia. Il materiale su cui si misura si conserva insieme alla misura (`tests/fixtures/`, `reference_material/`). - **Chiedi a Gemini invece di misurare i livelli** per dire com'e' venuta una giuntura (`scripts/chiedi_a_gemini_sulla_giuntura.py`): **una domanda, una risposta**, sempre la stessa per lo stesso scopo; Gemini risponde in senso musicale ("c'e' una ripetizione?" su un jingle e' sempre si'), la misura decide (tratto identico al campione > 0,995). Un giudizio di millesimi (la "parola mozzata") non si chiede: si misura. - **Allineamento forzato (WhisperX)**: colloca il testo, non lo verifica -- mai un testo piu' corto dell'audio, una sola passata, finestra ancorata alla sigla, lo `score` sulle ancore rare, e un confine incerto si unisce ("meglio quattro notizie giuste e due unite"). Il testo verbatim viene da Gemini sull'audio intero con la data di oggi e il divieto di correggere; le frasi di passaggio del conduttore non appartengono alla notizia. - **Un file solo, non due**: il file chiuso per Gemini e' gia' il mattone; il metadato si produce fuori dalla Bolla ma viaggia con il mattone (`content_uid`), la pagina non indovina. Mai incollare un pezzo ricodificato a uno copiato: si monta in PCM e si codifica una volta. - **Le prove si giudicano sul flusso che esce dalla Bolla** (l'archivio `bolla_archivio`, 3 giorni), mai sull'archivio grezzo; ogni registrazione porta versione del codice e fonte, e dice a che minuto ascoltare; in `PROVE TAGLI` i nomi sono `NNN-A-Giorno-AGOSTINO-ASCOLTA` (numero davanti, avvertenze in coda), rinunciati e codice vecchio in cartelle a parte, rotazione per numero non per eta'. Un file che c'e' e non si trova vale come uno che non c'e'. - **Ogni giro automatico ha un backoff**; un risultato che dipende da un parametro lo mette nella chiave della cache; un elenco gonfiato copre il segnale quanto uno incompleto; "produce zero" ha tre cause (ridondante, chiesto nel momento sbagliato, escluso da una lista) che si distinguono solo dal log del pezzo. - **Contenuto e ripetizione**: un contenuto riconosciuto per impronta esce per sempre dal problema; la deduplica guarda il magazzino intero, Cantina compresa; la copia migliore prende il posto della peggiore; un contenuto rotto entra disattivato con il motivo (prova di qualita' in `_register_upload`, il punto unico da cui passano tutti i caricamenti, dove si ricava anche il marchio). Un giudizio dell'utente vince sempre; nell'accorpamento sopravvive la voce col verdetto positivo, altrimenti si chiede. ## Spesa - **La spesa si misura in EURO per fornitore** (`provider_costs.py`, `/api/spesa-per-fornitore?giorni=N`): l'avviso guarda il solo Gemini (tetto nostro di soli avvisi, `GEMINI_MONTHLY_BUDGET_EUR`; il tetto Google non esiste piu'). Il ragionamento nascosto e' il 99% dell'output. - **Ogni giorno Gemini si usa meno**: testo non audio dove basta; prima l'impronta, poi Gemini; mai rimisurare cio' che e' misurato; **mai rifare la stessa cosa su tutto l'insieme prima di un esemplare confermato**; se la spesa non scende, si sta rifacendo lavoro gia' fatto. Il giudice di testo (gratis, 24/7) chiude il cancello sul tipo "pubblicita" (96%), mai sulla sicurezza dichiarata. - **Obiettivo (Agostino, 15/9)**: **40-50 EUR/mese per una radio piccola**, misurato su RMC (oggi ~138 EUR/mese: Railway 43, Gemini 68, Deepgram 27; il server a costo fisso si divide fra le emittenti). Con 50 abbonati a 6,90 EUR = 345 EUR; **pareggio intorno a 7-8 abbonati**: sopra e in crescita si tiene senza provvigione, sotto o ferma o paga il costo fisso o si chiude. Serve una pagina per emittente (abbonati, ricavo, costo vero, sopra/sotto il pareggio) -- da costruire. **Due conti separati da ora**: quanto costa FAR GIRARE una radio, quanto costa SVILUPPARE. Tagli decisi: non trascrivere cio' che e' gia' riconosciuto per impronta o pubblicita'; dopo il primo spot il carosello non si trascrive; il whisperx-service LAVORA (407 job, il lavoratore del mattone) e non si spegne. Non si taglia cio' che serve a capire (separazione vocale, collaudatore, prove). - **Il credito di Claude**: CLAUDE.md non cresce piu' (era ~263.000 token a messaggio); gli agenti in sottofondo ricaricano tutto il contesto ciascuno; le sessioni lunghe rileggono tutto a ogni chiamata; ogni risultato di strumento resta nel contesto per sempre (tenere corti gli output, `head`/`tail`, mai stampare file interi). ## Il punto in cui siamo (2026-09-15, Agostino via fino a sabato 19/9) - **Cambio di impostazione (15/9)**: non si taglia piu' il carosello intero prima della sigla. **Si mandano la sigla e il PRIMO SPOT, il taglio avviene li'** (fine del primo spot per durata nota, sigla per impronta): poi ridurre il minutaggio, togliere uno spot, inserire spot locali/asta, tampone fino al raccordo. Radiogiornale: sigla + prima notizia, poi ricomporre. **Ordine di lavoro, una alla volta, su caroselli veri, si passa alla successiva solo quando Agostino dice che e' a posto**: 1) taglio dopo sigla e primo spot (non iniziato); 2) togliere uno spot; 3) ridurre il minutaggio; 4) il radiogiornale. Il metodo precedente (ultima parola dello speaker, modi A/B, finestra all'indietro, Regista sul taglio) resta valido per la MUSICA e sta nello storico. - **Il rientro intelligente ("in intro")**: jingle se mancano pochi secondi, altrimenti una canzone leggermente piu' lunga, e ci si raccorda sul brano che riparte nel punto senza cantato (dove entra la voce si legge dagli archivi di Umberto/Leo/Max: `intro_end_s`, 27.738 intro; l'eredita' gira solo sui brani nuovi, `gira_su_tutto` da lanciare sul server). Da collegare, non ancora costruito. - **Memoria**: quattro cadute il 12-14/9 (separatore vocale caricato due volte + tracemalloc acceso al limite), corrette il 15/9 (`e22e25d`); una quinta caduta la notte del 15/9 (max 5,27 GB visto da Railway, ripartito da solo alle 06:13 italiane, deploy `118b68aa`); il separatore ora si carica una volta. **La rete** (`app/health/memoria_rete.py`, commit `bdc7046`): il limite di Railway (4 GB) vale sul cgroup intero, la rete misura il working set (memory.current - inactive_file; il 15/9: Railway vede 3,17 GB, il processo 2,04, la pressione vera 2,3), libera a 3,0 GB e si riavvia pulita a 3,5 (due letture consecutive, mai nei primi 10 minuti, mai due riavvii a meno di 20 minuti; marcatore `memory_restarts.jsonl` sul volume, riga in `pipeline_errors`); `restartPolicyMaxRetries` 100. Disco: 33 GB usati su 45, 13 liberi. - **Costi e server**: valutazione del cambio server in `docs/valutazione_cambio_server_e_costo_per_emittente_2026-09-15.md`; il piano della migrazione e' scritto, **non si comincia**: sabato si legge e si decide. `docs/migrazione_piano_2026-09-15.md`. - **Aperti, in ordine dallo storico e da `docs/trovato_e_non_inseguito.md`**: il Regista a tre casi (annunciata / speaker sopra l'intro / libera); i redazionali come categoria; le notizie di Babboleo da pareggiare (-25 -> -17 LUFS) e i campi di scheda (posizione vincolata, emittente); il recupero dello sfasamento; le versioni dello stesso brano; il quinto collegamento (punto della voce sul server); il MODELLO SPEAKER in coda (dalle 8 alle 10 sono in tre); il classificatore che dice "non sono sicuro"; **prima della seconda radio: 53 tabelle senza colonna stazione**. - **Le registrazioni dei mattoni le fa il server** (`catture_mattoni.py`, `/api/catture`): quando Agostino torna, i mattoni dei quattro giorni sono li' con "il taglio comincia al minuto X".