CHANGELOG.it.md
Rama main · réplica de lectura del repositorio original.
# Changelog
*English version: [CHANGELOG.md](CHANGELOG.md).*
Tutti i cambiamenti rilevanti di Spendif.ai sono documentati in questo file.
Il formato segue [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
Il versioning segue [Calendar Versioning](https://calver.org/) nella forma AAAA.M.N da 2026.10.0 (anno, mese senza zero iniziale, release del mese da 0); le release precedenti seguivano Semantic Versioning.
## [Unreleased]
## [2026.11.0] - 2026-11-10
### Cambiato
- I flussi guidati hanno una sola barra in fondo alla finestra: Indietro ed Esci a sinistra, un solo pulsante principale a destra. Con scelte non ancora salvate il pulsante principale è "Salva e vai avanti"; Indietro le salva; Esci chiede se salvarle. Finché un passo ha qualcosa di suo da fare, come importare i file in attesa, il pulsante principale resta quello
- Le tabelle dei flussi stanno nella finestra e scorrono al loro interno, così i pulsanti si vedono sempre
- La barra dei flussi si stacca dalla pagina e i suoi pulsanti dalla barra, nel tema chiaro e in quello scuro: Indietro ed Esci contornati, il pulsante principale pieno nel colore del marchio
- Il passo delle controparti spiega in poche righe cos'è una controparte; i dettagli sono in "Come funziona"
- "Tassonomia" ora si chiama "Schema categorie" e "Ledger" si chiama "Registro dei movimenti", nell'app, nelle guide e nelle risposte dell'assistente
- Il modello locale in uso è quello consigliato per il tuo computer, mai il primo file trovato nella cartella dei modelli; se ne usi uno non provato, Home e Importa lo dicono e propongono quello consigliato
### Corretto
- La pulizia delle descrizioni può solo togliere parole: un nome di controparte con parole che non sono nella descrizione della banca viene richiesto, e dopo tre tentativi resta il testo della banca
- Aggiornata una dipendenza con un difetto di sicurezza noto (oauthlib)
- Il suggerimento della voce Flusso di lavoro nel menu non resta più da solo in alto a sinistra dopo l'apertura del flusso
## [2026.10.5] - 2026-10-27
### Aggiunto
- Un conto dei contanti: le spese in contanti che dici all'app diventano subito suoi movimenti, con il giorno e l'importo detti, le tue parole come descrizione e la categoria scelta, già spuntati. Il conto si sceglie nelle Impostazioni, oppure si crea dalla chat la prima volta. Si importa come ogni altro conto, quindi ci si può caricare l'export di un'altra app dei contanti
- Dettare una spesa a Siri senza aprire l'app, su macOS: Spendif.ai prepara il comando di Comandi Rapidi con i nomi scelti nelle Impostazioni e lo propone una volta all'avvio. Quando apri la pagina vocale ogni frase diventa uno scontrino da controllare, "ieri" letto rispetto al giorno in cui l'hai detta e la categoria presa dalle tue parole; una frase senza importo resta da completare o da scartare, e una senza giorno vale per il giorno in cui l'hai detta
- Ogni scontrino salvato dice se è stato pagato in contanti: quello in contanti non aspetta nessun estratto conto, quello con carta si abbina al suo movimento
### Modificato
- "Ho uno scontrino" ora si chiama "Gestione vocale operazioni"
- Il riquadro di Siri sopra la chat è una riga di riepilogo, e ogni voce compare in un posto solo, dove c'è qualcosa da fare
## [2026.10.4] - 2026-10-27
### Aggiunto
- Rispondere a voce in "Ho uno scontrino": la prima volta la pagina chiede se scaricare il modello che ti ascolta (Whisper small, 190 MB, resta sul tuo computer); dopo il sì la conversazione va avanti senza toccare niente e la risposta la legge la voce del sistema. Per ora solo con l'interfaccia in italiano
- Salvare uno scontrino il cui movimento non è ancora importato: data, importo, cosa hai comprato e categoria; all'inizio della conversazione gli scontrini che ora hanno il loro movimento sono proposti uno alla volta, con le tue parole accanto al movimento, e spuntati solo dopo il tuo sì
- Il giorno detto come ordinale: "il primo", "il secondo del mese", "secondo ottobre"
- La guida e l'assistente dicono il modo migliore di usare Spendif.ai e rispondono a "me lo fa già la banca"
### Modificato
- Un menu uguale per tutti: in alto Carica l'estratto conto, Controlla i movimenti, Ho uno scontrino, Chiedi a Spendif.ai e Facciamo il punto (la vecchia Home), dove l'app si apre; tutte le altre pagine in due gruppi, Approfondimenti e Regole e impostazioni. Il menu per livello e la sua scelta nelle Impostazioni non ci sono più
- La differenza fra entrate e uscite nel grafico dei flussi si chiama Giacenza, "+" quando metti da parte, "-" quando prendi
- Nel menu è evidenziata solo la voce della pagina aperta
## [2026.10.3] - 2026-10-26
### Aggiunto
- Una prima pagina a registro vuoto: farsi guidare dal primo passo, oppure andare diretti all'import
- Il menu al tuo livello: il primo giorno il percorso guidato, Conferma una spesa, l'assistente e la Home; le altre pagine a un clic, sotto "Altre voci...". Il menu cresce con i tuoi dati e non si riduce da solo, e dalle Impostazioni scegli cosa mostra
- Conferma una spesa: risposte più brevi, che non ripetono ciò che hai appena detto; "Fatto." e si ascolta la spesa successiva; una frase breve ("ho finito") chiude con il numero dei movimenti spuntati; "cambia categoria" subito dopo una conferma riapre la sua categoria; il sì si dice in molti più modi. Le risposte che le parole non capiscono le legge il modello delle categorie, che propone soltanto: l'app chiede conferma e agisce sul tuo sì
- La ricerca nel Registro trova gli importi: 23,40 trova i movimenti di 23,40 in entrata e in uscita; 23 trova da 23,00 a 23,99
- La Diagnostica dice se l'import ha categorizzato e quanto variano le categorie, solo con conteggi
### Modificato
- La conversazione di Conferma una spesa scorre in un riquadro di altezza fissa
- Con una scheda grafica da 12 GB il primo avvio scarica il modello 7B invece del 3B; onboarding e download usano lo stesso calcolo della memoria
### Corretto
- "Tutte le date" in Registro, Analisi e Resoconti copre tutto il registro invece di tornare al mese corrente
- Il percorso aperto a registro vuoto finiva su un resoconto vuoto
## [2026.10.2] - 2026-10-25
### Aggiunto
- Conferma una spesa: con lo scontrino in mano scrivi o detti data e importo come si dicono ("ieri, 23 e 40") e l'app trova il movimento fra le uscite entro tre giorni. Se ce n'è uno lo confermi o ne correggi la categoria; se sono più d'uno li propone come scelta; se non c'è lo dice. La categoria viene dalle tue parole, e il modello si interpella solo quando non ne nominano nessuna: tre proposte vicine, poi l'elenco intero. Il movimento confermato diventa controllato
- Conferma una spesa, voce: le risposte si leggono ad alta voce con la voce del sistema operativo, dentro la finestra dell'app, senza mandare niente in rete, e un interruttore la spegne. Gli importi si leggono come si dicono. Le risposte partono con Invio, scritte o dettate; con la dettatura del Mac si prende solo la parte nuova. Limite noto: mentre la dettatura del Mac ascolta il sistema trattiene la voce, quindi le risposte si sentono a dettatura spenta
- "Sempre così?": dopo tre movimenti della stessa controparte messi a mano nella stessa categoria, il Registro chiede se diventa una regola. Sì la scrive e la applica ai movimenti già presenti, senza toccare le scelte a mano e i movimenti controllati; no si ricorda e non si chiede più
- Ogni movimento ricorda chi l'ha categorizzato all'import: una regola, lo storico, l'elenco degli esercenti, il modello, una controparte multi-categoria o la categoria di ripiego, ora distinta dal modello. I movimenti più vecchi restano vuoti invece che indovinati
### Modificato
- Spuntare una controparte con la categoria già giusta la accetta così com'è: i suoi movimenti tengono la categoria e chi l'ha data, nessuna regola prende i prossimi import, e né i passi guidati né "Sempre così?" lo chiedono di nuovo
- Una controparte con più categorie resta nella pagina Regole, ma la sua regola non gira mai: i suoi movimenti tengono la categoria che hanno, e nessuna regola più ampia li prende
- "Raw" diventa "descrizione originale" ovunque, in tutte e cinque le lingue
### Corretto
- Le regole applicate da una pagina (modifica di una regola, correzione dei suoi movimenti, applicazione immediata di una regola nuova, movimenti simili in Revisione) sovrascrivevano le categorie scelte a mano e i movimenti controllati; ora li lasciano stare, come già faceva l'import, e i conteggi annunciati comprendono solo i movimenti che cambiano
- L'export di un'app di pagamento con una colonna di ID transazione prendeva l'ID come descrizione, quindi ogni movimento arrivava con la descrizione vuota e poi prendeva lo stesso nome e la stessa categoria. Una colonna di codici tutti diversi e della stessa lunghezza non è mai la descrizione, e un nuovo formato di file che lascia senza descrizione più del 10% delle righe si ferma per la revisione
- I movimenti con data futura tengono la categoria proposta e la sua fonte fino alla loro data
## [2026.10.1] - 2026-10-24
### Aggiunto
- Un import in corso che smette di avanzare lo dice: dopo cinque minuti senza progressi il pannello dice da quanto è fermo e propone di interromperlo, così se ne può avviare uno nuovo. I file già completati in quell'import restano salvati
- Subito dopo l'onboarding, con l'archivio ancora vuoto, l'assistente ti manda prima a caricare un estratto e non inventa cifre. Nuove FAQ rispondono a "L'ho installato, e adesso?"
- L'ultimo passo dell'onboarding chiede se andare avanti a caricare i tuoi dati: sì apre Prepariamo i tuoi movimenti, no apre l'assistente
### Modificato
- La categoria dice perché, il contesto dice l'occasione. Ristorazione diventa Vitto e alloggio (Pasti, Albergo / B&B); la sottocategoria viaggi sotto il tempo libero sparisce, perché una cena in vacanza è una cena e la vacanza è il suo contesto; autobus, treno, aereo, traghetto e taxi diventano Mezzi pubblici e taxi; il carburante si divide in gasolio/benzina/GPL e ricarica elettrica. Movimenti, regole e correzioni esistenti si spostano una volta; le categorie create da te non si toccano
- I movimenti che hai controllato escono dalle griglie delle controparti e dai loro conteggi, nella pagina controparti e nei passi guidati
### Corretto
- Il modello locale torna a girare sulla scheda grafica. Da maggio l'onboarding scriveva zero strati sull'acceleratore, quindi ogni installazione analizzava sul processore, da cinque a otto volte più lenta. Le nuove installazioni usano la scheda che la libreria di inferenza vede, e al primo avvio di questa versione le copie già installate ci tornano, una volta sola; uno zero messo a mano dopo resta. Su Linux e Windows la scheda Vulkan ora si vede prima di caricare il primo modello
- Diagnostica: l'accelerazione è "attiva" solo con una scheda utilizzabile e strati su di essa; se una scheda è disponibile si dice a parte
- Il log non contiene più il contenuto dei tuoi file. Il riconoscimento dell'intestazione scriveva le prime righe di ogni file importato (esercente, importo, data, intestatario, IBAN); sono spariti anche descrizioni, pattern delle regole, nomi delle controparti e domande e risposte dell'assistente. Il log dice ancora come è stato letto un file
- Due colonne di importo valgono come dare e avere solo se si riempiono su righe diverse: l'importo in valuta originale e la sua commissione non prendono più il posto dell'importo in euro. Un nuovo formato di file che lascia senza importo più del 10% delle righe si ferma per la revisione
- La descrizione non unisce più mese, numeri di riferimento, categoria della banca o cambio: una colonna si unisce solo se contiene soprattutto lettere
- Un movimento che non è un giroconto ha sempre una categoria: un giroconto tornato spesa o entrata prende la proposta del modello o il ripiego, da rivedere. Il ripiego delle entrate è la sottocategoria generica, non più "vendita beni usati". I movimenti già rimasti senza categoria si riparano all'avvio
- Una seconda scheda del browser, o una riconnessione, durante un import non dichiara più interrotto quell'import
- Dipendenze: gitpython 3.2.1, opentelemetry-api 1.45.1 e filelock 4.2.1 per vulnerabilità pubblicate
## [2026.10.0] - 2026-10-22
### Aggiunto
- Prepariamo i tuoi movimenti (prima Flusso di lavoro): la pagina da cui si riparte ogni volta. Le due uscite dell'onboarding ci portano, e la prima volta lo dice, una sola volta
- Se vuoi controllare: un flusso a sé, dalla sua voce di menu, per controllare i movimenti un conto alla volta. Una lista mostra ogni conto con l'ultimo movimento controllato e il primo ancora da controllare; viene proposto il conto più indietro, dal suo movimento non controllato più vecchio. Poi la griglia del Registro a tutta pagina con i soli movimenti non controllati di quel conto, dal più vecchio: a ogni salvataggio i controllati escono e arrivano i successivi, e un pulsante con conferma segna controllati tutti quelli mostrati. Mentre si crea una regola il salvataggio aspetta; la regola salva prima le altre modifiche della griglia. Finito il conto, si chiede dei giroconti senza l'altro lato, poi si torna alla lista
- Un nome tuo per un movimento della banca: rinominare una descrizione chiede se rinominare tutti i movimenti uguali, solo quelli con lo stesso importo (rate che cambiano) o solo questo, e può fissare insieme la categoria con una regola. La pagina Regole elenca i tuoi nomi
- Investimenti e risparmi: una categoria nei due versi. In uscita per bene (immobili, azioni, pensione, fondi, vincolato), in entrata per origine dei soldi (rendita, cedole e dividendi, interessi, vendite e riscatti). Le commissioni bancarie hanno una categoria propria, quel che resta della finanza diventa Assicurazioni, la pensione va nelle entrate da lavoro, i trasporti hanno il pedaggio. Movimenti, regole e budget esistenti si spostano una volta, nella lingua delle tue categorie; le categorie create da te restano dove sono
- Sankey della Home dalle entrate alle uscite: categorie di entrata a sinistra, di uscita a destra, ogni colonna dal ramo più grande. Interruttori per solo spese, sottocategorie e valori in percentuale o in euro sui rami abbastanza alti. Ora sta sopra il grafico mensile
- Ogni colonna del Registro spiega cosa significa, e i totali dicono quali movimenti lasciano fuori
- Una sola colonna di categoria in ogni griglia ("Categoria / Sottocategoria"), griglie modificabili e menu che offrono solo le categorie del verso del movimento
- I giroconti si abbinano anche fra import diversi, i movimenti con data futura restano in attesa fino alla loro data, e le entrate hanno un passo proprio
- L'assistente risponde con il modello locale, basandosi sulle guide attuali
- Flusso di lavoro (ora Prepariamo i tuoi movimenti): una nuova voce in cima al menu accompagna l'utente un passo alla volta, a schermo pulito come l'onboarding, da cosa caricare all'import, alle controparti, alle categorie miste fino al quadro del periodo. Uscendo il passo si salva, e la volta dopo si riprende da lì, saltando i passi già fatti nel frattempo. Dopo l'onboarding, "Carica ora" lo apre al passo dell'import
- Flusso di lavoro, passo delle controparti: solo le controparti di spesa ancora da controllare, dalla più grande. Basta una spunta per controparte, e i prossimi import non la richiedono. In alto spiega l'obiettivo, le controparti che fanno 8 euro su 10 delle spese del periodo, e dice quanto è controllato, quanto manca e quante controparti bastano. L'obiettivo è un suggerimento: Avanti funziona sempre
- Flusso di lavoro, passo delle categorie miste: le controparti di spesa con movimenti in più categorie. Se è un errore, una categoria diventa regola per tutti i movimenti; se dipende da cosa si è comprato, i movimenti si guardano uno a uno, perché nessuna regola distingue movimenti con lo stesso nome. I movimenti già guardati non si richiedono: una controparte torna solo con quelli arrivati dall'ultimo import, e la pagina dice quanti erano già sistemati. Il passo delle controparti dice anche quante spese del periodo le spunte precedenti hanno categorizzato da sole
- Flusso di lavoro, passo dell'import: a import finito, se non c'è nessuna domanda in attesa, un pulsante in fondo alla pagina porta alle controparti, così il passo successivo non si perde
### Modificato
- Il segno di ogni movimento viene da come si usa il conto ed è misurato sui dati; le controparti si scrivono in un solo modo, e una controparte ha una sola categoria
- Ogni regola ha un verso: vale solo per i soldi in uscita o solo per quelli in entrata. Le regole valide nei due versi sono state divise secondo i movimenti che coprivano; 27 movimenti categorizzati da una regola del verso opposto sono tornati da rivedere
- Home e Registro contano gli stessi movimenti: tutti quelli del periodo tranne giroconti e saldi carta. Le spese con carta arrivano nei grafici della Home
- Le colonne del Registro sono in ordine di lettura: quel che leggi, quel che decidi, la spunta della regola, poi i simboli che informano soltanto, a metà larghezza. Conto e tipo sono filtri, non più colonne
- I movimenti controllati tengono la loro categoria: regole, estensione ai simili e modello non li toccano. La categoria di ripiego non viene mai riusata come esempio
- Da questa release le versioni seguono il calendario: AAAA.M.N
- La Lista di controllo ora dice cosa scaricare, con le date esatte, sopra la griglia: prima il conto più indietro con un pulsante verso l'import, poi gli altri conti e i mesi vuoti in mezzo. Se non c'è ancora nessun conto chiede solo il nome; la prima volta suggerisce un mese solo, così il primo giro resta breve
- Home e Report si aprono sui tre mesi che finiscono con l'ultimo movimento caricato, invece che su un periodo che finisce oggi. Dopo settimane senza importare i grafici non si aprono più vuoti: mostrano dove eri rimasto, e un avviso dice fino a quando sei arrivato e rimanda alla Lista di controllo. Nella Home è la nuova scelta predefinita, "Ultimi 3 mesi caricati"; gli altri periodi non cambiano
- La didascalia del periodo nella Home non dice più "Ultimi 12 mesi" qualunque periodo sia selezionato
- Nella pagina Controparti una controparte si può spuntare come controllata quando la sua categoria è già giusta: la categoria mostrata diventa una regola per tutti i suoi movimenti, passati e futuri. Prima una controparte risultava controllata solo cambiandole categoria
- La Lista di controllo ha un nome solo per lingua, uguale nel menu, nella pagina, nei messaggi, nella chat e nelle guide. Prima l'italiano alternava "Lista di controllo" e "Check List", e francese, tedesco e spagnolo usavano l'inglese "Check List"; il titolo della pagina chiama i movimenti con la parola delle banche
### Corretto
- Rinominare tutti i movimenti uguali estende anche il segno di giroconto
- I rami del Sankey non finiscono più sotto il grafico mensile
- Dipendenze: pillow 13.0.0 e streamlit 1.66.1 per nove CVE di pillow, certifi 2026.10.19 per PYSEC-2026-4320
- Il flusso di lavoro non richiede più le controparti che hanno già una tua regola. Prima il passo delle controparti le proponeva di nuovo, e a ogni import le ritrovavi con i movimenti vecchi sistemati e i nuovi da sistemare. Ora "controllato" e "categorizzato" sono due cose: **Categoria decisa da te** (come hai speso) vale per la controparte e una regola la decide per tutti gli import; è ciò che chiede tutto il flusso. **Controllato da te** (i soldi li hai spesi davvero) vale per un movimento solo, non si eredita e si spunta solo nel Registro. Cambiare la categoria di un movimento non lo segna più come controllato, e lo storico impara solo dalle categorie decise da te
- Una controparte confermata non torna più "mista" dopo l'import successivo. Confermare una controparte, o applicare una regola ai movimenti già importati, registrava quei movimenti come modificati a mano, mentre l'import registra i nuovi come categorizzati dalla regola: stessa categoria, due origini, e la controparte risultava mista finché non si riapplicavano le regole. I movimenti categorizzati attraverso una regola ora risultano tali. Una controparte è mista solo quando hai cambiato a mano uno dei suoi movimenti, che è un'eccezione vera alla regola
## [0.3.5] - 2026-10-12
### Aggiunto
- Una controparte si può segnare come giroconto invece di darle una categoria. Nella pagina Controparti una colonna con la spunta la segna, a scelta anche sui movimenti già importati; da lì ogni import trasforma i suoi movimenti in giroconti e li lascia fuori dalla categorizzazione. Togliendo la spunta tornano spese o entrate. La pagina Regole mostra queste regole e permette di crearne o modificarne una
### Corretto
- La pagina Controparti parla in modo semplice: movimenti, importo medio, chi ha scelto la categoria, controllata da te. Un'etichetta diceva il contrario di quello che misurava: la colonna chiamata variabilità è la quota di movimenti con la categoria più usata, e il filtro "bassa variabilità" mostrava in realtà le controparti con categorie miste. Ora si chiama "solo categorie miste"
- Le controparti già segnate come giroconto non spariscono più dalla pagina Controparti
## [0.3.4] - 2026-10-12
### Corretto
- La schermata di import non resta più su "in attesa di revisione schema" dopo la conferma della revisione. Ogni import che si era fermato per la revisione risulta completato, con i movimenti confermati aggiunti al suo conteggio, e un import che non aveva bisogno di revisione non riporta più i file in attesa di uno precedente
## [0.3.3] - 2026-10-12
### Corretto
- Gli estratti conto con due colonne di date scritte con il mese per esteso ("02 settembre 2026") vengono importati con le date come date. Prima entrambe le colonne erano prese per testo e unite alla descrizione: ogni movimento portava le date nella descrizione e la colonna della data aveva due ruoli. Il tipo delle colonne ora lo decide lo stesso lettore di date usato dall'import
- La revisione delle colonne non salva più una colonna scelta per due ruoli, per esempio la stessa colonna come importo e come entrate. Indica la colonna e i suoi ruoli, e l'import attende finché la scelta non cambia
## [0.3.2] - 2026-10-11
### Corretto
- La pagina Chat funziona nell'applicazione installata. Era rotta in ogni pacchetto mai pubblicato, su tutti i sistemi: il suo modulo non stava in nessuno degli elenchi di cio' che entra nel pacchetto, quindi aprire la Chat dava errore. Un test ora fallisce se un elenco dimentica un modulo che l'applicazione importa, o ne nomina uno che il repository non contiene
- Il segno degli importi lo decide il documento, non il tipo di conto scelto in un modulo. Un'etichetta impostata alla creazione del conto poteva trasformare un mese di spese della carta in entrate. Ora gli importi vengono misurati e confrontati con cio' che ha letto il modello: se le due letture concordano l'importazione prosegue, se non concordano l'applicazione chiede una volta in che verso vanno gli importi e ricorda la risposta per quel formato
- Un'intestazione disegnata con i bordi delle celle (logo, periodo, intestatario) non viene piu' scambiata per la tabella dei movimenti. Il rilevamento prendeva il primo riquadro bordato e si dichiarava certo; ora scarta le cornici quasi vuote e sceglie la tabella piu' piena. Le immagini in un foglio di calcolo non influiscono sulla lettura, e un test lo verifica
- Un crash lascia la sua traccia nel registro dell'applicazione. L'unica copia stava nel registro di avvio, riscritto a ogni partenza, quindi riavviare dopo un crash la cancellava. Il resoconto tecnico dice se un crash e' stato registrato, senza copiarne il testo, che contiene percorsi personali
- Gli estratti conto Excel nel vecchio formato (`.xls`) si importano nell'applicazione installata. Il caricamento li accettava, ma la libreria che legge quel formato non era fra le dipendenze ne' nel pacchetto, quindi ogni `.xls` si fermava con un errore. Per trovare la riga di intestazione serviva una seconda correzione: le prime righe venivano lette con uno strumento che i `.xls` non li apre, e il titolo della banca finiva preso per intestazione
- Le date con il mese scritto per esteso ("02 settembre 2026") si leggono in italiano, inglese, francese, tedesco e spagnolo, per esteso o abbreviate. Prima si leggevano solo i mesi in inglese, e un estratto conto scritto cosi' perdeva tutte le righe
- Un'importazione che non ha letto quasi niente non si dichiara piu' riuscita. Una nuova mappatura delle colonne che legge meno di meta' delle righe si ferma per la revisione con il motivo, invece di essere salvata e annunciata come importata con zero movimenti
- Una colonna assegnata a due ruoli, per esempio la data presa anche come descrizione, manda la mappatura in revisione invece di passare come certa
- Il riconoscimento con il modello delle righe di fondo (totali, saldi, note in coda all'estratto conto) non era mai partito: il suo prompt dava errore a ogni chiamata e subentrava sempre il metodo statistico. Ora parte, e il metodo statistico resta per quando il modello non risponde
- Cinque dipendenze aggiornate oltre le vulnerabilita' pubblicate: pyjwt, urllib3, fsspec, oauthlib e setuptools
- Su macOS i due canali installano lo stesso pacchetto, `/Applications/SpendifAi.app`. Homebrew installava `Spendif.ai.app` e l'immagine disco `SpendifAi.app`, quindi dopo aver sostituito l'uno con l'altro brew si rifiutava di aggiornare
### Aggiunto
- Il pacchetto per Debian e Ubuntu (`.deb`) porta con se' il proprio Python e la libreria di inferenza con Vulkan come plugin, su amd64 e arm64: accelerazione sulla scheda grafica AMD, Intel e NVIDIA senza installare altro, e niente da compilare sulla macchina dell'utente. I pacchetti `.rpm` e Arch usano ancora la costruzione precedente e avranno l'accelerazione in una release successiva
- Lo stato dell'importazione mostra da quanto tempo e' in corso e, alla fine, quanto e' durata: piu' o meno quanto costera' la prossima sulla stessa macchina
- support@spendif.ai sulle pagine iniziale e di installazione, in tutte le lingue, per chi non ha un account GitHub. La pagina Diagnostica dice dove mandare il suo resoconto
- I resoconti di prova costruiscono da soli la tabella di compatibilita'. La pagina Informazioni tecniche apre un modulo GitHub gia' compilato con il suo resoconto, e un workflow lo legge, aggiunge il computer a `docs/compatibility.md` (e `docs/compatibility.it.md`) e propone la modifica come pull request. Nessuno ricopia a mano sistema operativo, scheda grafica o tempi. Il modulo e' pubblico, quindi il resoconto e' trattato come dato non fidato: solo campi nominati, di lunghezza limitata, e niente che nella tabella possa diventare un link o HTML
- Il resoconto tecnico nomina la distribuzione ("Debian GNU/Linux 13 (trixie)") invece del kernel, e mostra l'ultima importazione da sola accanto alla media, cosi' si confrontano una corsa sul processore e una sulla scheda grafica. Il file salvato dice cosa contiene: `spendifai-report_0.3.1_debian-13_arm64_cpu_20261010-1432.xml`
- Una matrice di prova in `docs/compatibility.it.md` elenca le configurazioni provate prima di una release e spunta ciascuna dai resoconti che le corrispondono: fatta quando un resoconto dice che tutti e quattro i passi sono riusciti, fatta in parte quando qualcuno no. La spunta si calcola, non si scrive a mano. `docs/test_builds.it.md` riporta i comandi per scaricare e avviare su ogni sistema
- I resoconti di prova possono arrivare anche via email, per chi non ha un account GitHub. La pagina Diagnostica offre un'email con nel corpo l'elenco dei quattro passi da spuntare e il documento salvato in allegato, e l'email salvata entra in tabella con lo stesso lettore e gli stessi controlli del modulo
- Windows riceve l'accelerazione sulla scheda grafica con Vulkan, come gia' Linux, da un solo bundle che copre AMD, Intel e NVIDIA. L'applicazione trova anche i plugin della libreria di inferenza su Windows: cercava il caricatore in una libreria che su Windows non lo esporta, quindi non ne avrebbe caricato nessuno e non avrebbe elencato alcun dispositivo
## [0.3.1] - 2026-09-23
### Corretto
- Su Linux e Windows l'installazione non compila piu' niente. llama-cpp-python veniva costruito dai sorgenti su ogni macchina che installava Spendif.ai, e serve CMake piu' un toolchain C/C++ che un computer normale non ha: era la ragione per cui l'applicazione non si riusciva a installare su una Ubuntu appena installata o una Windows 11 pulita. Ora arriva come wheel precompilata fissata nel lockfile, e il primo avvio e' uno scaricamento invece di tre-otto minuti di compilazione. macOS resta com'era e continua a compilare in CI, dove il toolchain c'e', cosi' l'inferenza su GPU Apple Silicon non viene persa in silenzio
- Il pacchetto Debian dichiarava `gcc` e `cmake` ma non `g++` ne' `make`, mentre quello Fedora li dichiarava tutti e quattro. Il pacchetto si installava pulito su Ubuntu e poi non partiva mai, fermandosi su `Could not find compiler set in environment variable CXX`
- Gli installer Windows non dichiarano piu' di essere riusciti dopo un fallimento. In PowerShell 5.1 un comando nativo che esce con errore non solleva niente, quindi l'installer Docker annunciava "Docker trovato" una riga sotto il messaggio di Docker che diceva che il motore non era in esecuzione, poi proseguiva fino ad aprire il browser su un'applicazione mai avviata, e quello nativo annunciava le dipendenze installate subito dopo che la costruzione di una era fallita
- Un primo avvio fallito su Linux si vede. Il launcher usciva prima del proprio messaggio di errore e della finestra di avviso, quindi chi avviava dall'icona non vedeva accadere assolutamente nulla. Uno scaricamento che non riesce a risolvere un nome viene ora distinto da un'installazione rotta, perche' il primo caso e' la rete di quel computer e non un nostro difetto
- L'installer Windows accettava qualunque Python trovasse dopo aver installato il 3.13, quindi una macchina che nel PATH aveva ancora il 3.12 proseguiva con il 3.12. L'intervallo supportato viene ora verificato prima e dopo, e coincide con quello che il progetto dichiara
- I pacchetti Linux non hanno mai avuto l'icona dell'applicazione. Entrambi installavano un PNG da 256px se esisteva, e niente lo ha mai prodotto: ogni .deb e ogni .rpm sono usciti con una voce di menu che punta a un nome di icona che non risolve nulla
### Aggiunto
- Arch Linux e' un sistema supportato. La release contiene `spendifai-<versione>-1-any.pkg.tar.zst`, costruito e provato in un container che parte da zero, cosi' una dipendenza non dichiarata fa fallire la costruzione invece di funzionare per caso sulle macchine che ce l'hanno gia'. Si installa con `sudo pacman -Syu` seguito da `sudo pacman -U ./spendifai-*.pkg.tar.zst`
- Python 3.14 e' supportato. Le distribuzioni rolling lo spediscono da agosto e l'ambiente Linux si crea sull'interprete di sistema, quindi il limite precedente rendeva il pacchetto non installabile la'. I test girano su 3.14 in CI
- Le schede grafiche AMD e Intel vengono nominate invece di essere date per assenti. L'accelerazione per quelle schede non c'e' ancora, e l'applicazione ora lo dice chiaramente invece di sostenere che non ci sia nessuna GPU
### Modificato
- Scaricare e' un clic. Ogni release contiene anche ciascun installer sotto un nome fisso, che e' quello che i bottoni del sito collegano: si risolvono sulla release piu' recente senza una richiesta all'API di GitHub, quindi continuano a funzionare dietro un blocco per la privacy, oltre il limite di richieste dell'API e con JavaScript disattivato
- La compilazione di llama-cpp-python per i modelli Qwen 3.5 non parte piu' da sola al primo avvio, dove sostituiva una wheel funzionante con una costruzione locale non provata. Resta disponibile su richiesta con `SPENDIFAI_SSM_BUILD=1`
- Sul sito viene offerto un solo modo di installare su Windows. L'installer da sorgenti richiedeva un toolchain che una macchina pulita non ha ed era pubblicizzato accanto all'MSIX
### Sicurezza
- Le wheel che contengono il motore di inferenza vengono confrontate con i checksum registrati. Arrivano da un indice che non ne pubblica, quindi il lockfile fissa la versione ma non i byte: i checksum verificati sono committati e ricontrollati a ogni cambio della versione e una volta a settimana
## [0.3.0] - 2026-09-20
### Aggiunto
- Notifica di aggiornamento in-app per le installazioni a pacchetto. Un'installazione via Homebrew, DMG, .deb o .rpm ora si accorge che esiste una versione nuova e mostra il comando giusto per il modo in cui e' stata davvero installata. Fino a ora il badge lo alimentava solo il `git fetch` del launcher dell'installazione da sorgente, quindi un pacchetto leggeva un file che nessuno avrebbe mai scritto (#AI-317)
- Tabella `update_event`, una riga per ogni versione che questa installazione ha eseguito, con sistema operativo, architettura e metodo di installazione. Storico locale che non esce dalla macchina; Impostazioni > Aggiornamenti lo mostra e permette di disattivare il controllo (#AI-317)
- Repository APT firmato. `packaging/linux/update-apt-repo.py` costruisce e firma l'archivio pubblicato su `spendifai/apt`, cosi' Debian e Ubuntu ottengono `apt update` e `apt upgrade` invece di un `apt install ./file.deb` una tantum (#AI-322)
- Il cask Homebrew viene pubblicato automaticamente alla pubblicazione di una release, non piu' con un passo manuale che si poteva dimenticare (#AI-316)
### Sicurezza
- Il postinst del `.deb` e il `%post` dell'`.rpm` non passano piu' `curl` a una shell come root per installare uv. La versione e' fissata, l'asset viene scaricato per nome e il suo sha256 e' verificato prima di eseguire qualunque cosa. In caso di mancata corrispondenza il passo rifiuta senza alcun ripiego (#AI-321)
- `anyio` aggiornato a 4.14.2 per CVE-2026-63374 e CVE-2026-64847
### Corretto
- I pacchetti Linux spedivano la versione che si trovava committata in `core/_build_info.py`, perche' solo i builder macOS e Windows la stampavano. Con il nuovo controllo aggiornamenti quel valore stantio avrebbe significato un falso "aggiornamento disponibile" permanente per ogni utente Linux (#AI-320)
- Il `.deb` dichiarava `Section: finance`, che non e' una sezione Debian, dipendeva dall'obsoleto `pkg-config` invece che da `pkgconf`, e non spediva ne' changelog ne' file di copyright. lintian ora gira in CI (#AI-321)
## [0.2.1] - 2026-09-01
### Changed
- La build macOS è firmata con certificato Developer ID e notarizzata da Apple. Gatekeeper accetta DMG e app senza alcun workaround: niente `--no-quarantine`, niente `xattr -dr` (#AI-243)
- Le istruzioni di installazione del cask Homebrew non richiedono più `--no-quarantine`
### Fixed
- `packaging/macos/sign-local.sh` firmava il bundle con `codesign --deep`, sconsigliato da Apple e che lascia i Mach-O annidati senza hardened runtime. Ora firma inside-out, applica gli entitlements necessari a un bundle Python congelato, e avvisa se il DMG viene costruito prima della firma dell'app
## [0.2.0] - 2026-08-25
### Added
- Barra privacy a semaforo nella pagina di configurazione LLM: mostra a colpo d'occhio cosa esce dalla macchina per ciascun backend, prima che l'utente ne scelga uno (#AI-229, #161)
- Landing page: metadati SEO + GEO, `sitemap.xml`, `robots.txt` e `llms.txt` (#155)
- Pagina di feedback alpha su gh-pages, basata su un form Tally (#162)
- Pulsanti di download diretto degli installer (DMG / MSIX / .deb / .rpm) sulla pagina getting-started, in tutte e 9 le lingue (#163)
- Nuovo brand mark applicato alle landing page e alle icone dell'app bundle (#159)
### Changed
- Navigazione della sidebar completamente tradotta in italiano (#158)
- Documentazione: Python >= 3.12 dichiarato nelle istruzioni di installazione, landing di brand in inglese, link reciproco al README italiano (#164)
- Riferimenti di versione allineati tra README e `pyproject.toml` (#160)
### Fixed
- Finestra desktop: il pulsante Deploy di Streamlit e la sua toolbar non sono più visibili nella finestra pywebview; `toolbarMode = viewer` mantiene raggiungibili Settings e Tema (#165)
- Coupling: il layer UI non importa più direttamente `db`/`core`, ed è riparato il gate CI sul coupling che passava silenziosamente (#157)
### Security
- Alzati i floor delle dipendenze dopo una segnalazione di pip-audit: `gitpython >= 3.1.58` (GHSA-9rj7-rf2p-w77r, GHSA-4gmw-gg2m-w46p, CVE-2026-76217, GHSA-wvpp-8hx9-p66j, GHSA-jm78-9fvv-mhgr) e `cryptography >= 50.0.0` (PYSEC-2026-3552). Il lockfile risolve a gitpython 3.1.59 e cryptography 50.0.0
- Immagine Docker alleggerita e irrobustita: il toolchain di packaging dell'interprete di sistema (pip, setuptools, `pkg_resources`, wheel bundled) viene rimosso a build conclusa, e `uv sync` non lascia più la sua cache di build in `/root/.cache/uv`. Nessuno dei due è raggiungibile dall'app — uv installa tutto in `/app/.venv` e l'entrypoint passa da `uv run`. Nota: questo **non** ha chiuso le due segnalazioni HIGH che Trivy riporta sull'immagine (setuptools 70.3.0 / CVE-2025-47273 e msgpack 1.1.2 / GHSA-6v7p-g79w-8964); la loro posizione nell'immagine resta ignota e il gate di sicurezza sul container resta rosso per questa release
### Known limitations
- Il DMG macOS e l'MSIX Windows **non sono firmati**: Gatekeeper e SmartScreen mostreranno un avviso al primo avvio. Firma del codice e notarization sono tracciate separatamente
- I manifest winget e il cask Homebrew in `packaging/` fanno ancora riferimento all'artefatto ZIP Windows ritirato e a un nome file `Spendif.ai-<versione>.dmg`; per questa release nessuno dei due canali viene sottomesso
## [0.1.0] - 2026-07-29
*Prima release pubblica, taggata e pubblicata il 2026-07-29 con gli installer per macOS, Windows e Linux. Le voci qui sotto erano prima divise tra una sezione `Unreleased` e una sezione `0.1.0` datata con l'inizio dello sviluppo (2026-04-06); sono state unite qui per corrispondere a ciò che è effettivamente uscito negli artefatti v0.1.0.*
### Added
- Prima release
- Import di estratti conto CSV/XLSX (9 strumenti finanziari italiani)
- Categorizzazione AI locale via llama.cpp (Qwen3.5, Gemma4, Phi4, Llama3.2)
- Matching delle controparti con consapevolezza NSI/OSI
- Cache storica, regole utente, personalizzazione della tassonomia
- App bundle macOS, integrazione con Spotlight
- Installer Windows via winget/PowerShell
- Dashboard di analytics interattiva (in arrivo)
- App desktop nativa: finestra pywebview che incorpora Streamlit (niente Terminal, niente browser)
- Download automatico del modello LLM consigliato in base al rilevamento hardware (RAM/GPU)
- Setup llama.cpp zero-config al primo avvio
- Splash screen con barra di avanzamento del download
- Installer macOS (`packaging/macos/install.sh`) con launcher per finestra nativa
- Installer Windows (`packaging/windows/install.ps1`) con launcher per finestra nativa
- Installer Ubuntu/Debian (`packaging/linux/install-debian.sh`) e builder .deb
- Installer Red Hat/Fedora (`packaging/linux/install-redhat.sh`) e builder .rpm
- File spec PyInstaller per generare .app standalone (macOS) e .exe (Windows)
- Script di disinstallazione per macOS, Windows (con flag `-Silent`) e Linux
- Registrazione in Add/Remove Programs di Windows durante l'installazione
- Workflow CI Linux (`release-linux.yml`): build di .deb/.rpm, smoke test in container, allegati alla GitHub Release
- Identificatore winget rinominato in `SpendifAi.SpendifAi`
- Installer MSIX per Windows (`packaging/windows/build-msix.ps1` + `AppxManifest.xml.in`), sostituisce il precedente artefatto ZIP-only per Windows
- Script di firma locale: `packaging/macos/sign-local.sh` (codesign + notarytool + stapler) e `packaging/windows/sign-local.ps1` (wrapper SignTool)
- Builder DMG locale (`packaging/macos/build-dmg.sh`) che replica il job CI per riproducibilità offline
- Modello release CI ibrido: `release.yml` builda tutti e quattro gli installer unsigned e pubblica una GitHub Release in **draft**; l'owner firma DMG e MSIX in locale e li sostituisce via `gh release upload --clobber` prima di rimuovere `--draft`. Documentato in `docs/release_process.it.md` §2bis (EN+IT)
- Pagina "Primo avvio" su gh-pages, copertura completa 9 lingue (`getting-started.{html,en,de,es,fr,ja,nl,pl,pt}.html`): guida illustrata a 3 step (Download → Installa → Primo avvio) con bottoni di download per DMG/MSIX/.deb/.rpm e placeholder per screenshot (`assets/screenshots/`). Ogni pagina include il beacon Cloudflare Web Analytics e il selettore lingua completo
- Aggiornata la sezione "Primo avvio" di `installation_{macos,windows}.{md,it.md}` per descrivere il flusso nativo pywebview (splash + download modello + wizard onboarding), sostituendo la sequenza obsoleta Terminale/browser che vale solo per i vecchi script `install.sh`/`install.ps1`
- Landing page (tutte le 9 lingue: `index.html` IT, `index.{en,de,es,fr,ja,nl,pl,pt}.html`): aggiunto CTA localizzato "Scarica installer" che punta alla pagina getting-started corrispondente alla lingua, sopra le tab esistenti con gli script curl
- README riorganizzato per audience tecnica/sviluppatori (era misto end-user + dev): ridotto da 663 → ~180 righe, banner che dirige gli utenti finali alla getting-started su gh-pages, sei bullet "Cosa è implementato" con path file + RF-codes + beta tag onesti, sezione develop-locally con chiarimento Ollama vs llama.cpp, link reciproci IT·EN nella tabella documentazione
- Nuovi `docs/architecture.{md,it.md}` con diagramma a layer e dettagli Flow 1 vs Flow 2 prima inseriti nel README
- Nuovi `docs/design_decisions.{md,it.md}` con il razionale Decimal/SHA-256/invert_sign/RF-03/RF-04 prima nel README, con flag espliciti `(beta)` su RF-03 e RF-04
- Nuova sezione "Cosa ottieni" su tutti i 9 `getting-started.*.html` con sei bullet user-value (tradotti in tutte le lingue) — framing complementare a "Cosa è implementato" del README
- Sezione README "Cosa lascia la macchina" resa esplicita e onesta: esempio concreto before/after di redazione PII e ammissione che **importi** e **date** delle transazioni viaggiano comunque verso i backend LLM remoti nell'implementazione attuale (item di backlog AI-55 copre le modalità di redazione). Bullet privacy su tutti i 9 getting-started gh-pages riscritto con lo stesso tono onesto
- Log persistente del boot launcher in `~/Library/Logs/spendifai-launcher.log` (macOS) / `~/.spendifai/spendifai-launcher.log` (Linux/Windows). Cattura stdout/stderr del launcher anche quando `console=False` in `desktop.spec` li avrebbe deviati a `/dev/null`. Troncato ad ogni launch
- Download modello in parallelo con banner ETA live. `desktop/launcher.py` separa il bootstrap sincrono dell'ambiente dal download multi-GB del modello. Il download gira in un daemon thread e aggiorna `~/.spendifai/model_download.status` con `pct`, `elapsed_s`, `eta_remaining_s`. Nuovo `ui/widgets/model_download_banner.py` lo legge via `st.fragment(run_every=2)` e mostra un banner sticky senza re-renderizzare l'intera pagina. `core/model_manager._download_from_hf` aggancia il tqdm di Hugging Face al nostro callback per progress reale chunk-per-chunk (prima il callback partiva solo al 100%)
- Pagina di primo avvio immersiva. Quando `onboarding_done == false`, `app.py` inietta CSS per nascondere sidebar / header / toolbar e renderizza solo il banner download + wizard onboarding. Appena il wizard è confermato, il layout torna all'app normale con sidebar
- Build `.deb` cross-arch. `release.yml` esegue `build-deb.sh` per `amd64` e `arm64` via matrix; stesso source Python, cambia solo `Architecture:` in `DEBIAN/control` (il postinst compila le deps native sulla macchina target via `uv sync`). Sblocca l'install su VM Apple Silicon (#AI-57)
- Documentato il flusso dev end-to-end in `docs/desktop_build_and_test.{md,it.md}`: build DMG locale, MSIX CI via `workflow_dispatch`, install MSIX su VM clean tramite `packaging/windows/dev-install.ps1` (auto self-signed cert, firma, installa), tutti i path di log, cleanup totale
- `packaging/windows/dev-install.ps1`: script all-in-one per tester — genera un cert self-signed con l'esatto Publisher CN del manifest, lo importa come trusted, trova SignTool, firma l'MSIX e `Add-AppxPackage` per installare. Idempotente (cert reuse), auto-elevate se non Admin
- Script di cleanup totale per tutti e tre i sistemi: `packaging/macos/cleanup.sh`, `packaging/linux/cleanup.sh`, `packaging/windows/cleanup.ps1`. Ognuno killa processi attivi, disinstalla il pacchetto di sistema (AppX / apt / dnf / rm dell'.app), cancella `~/.spendifai`, la cache modelli Hugging Face e i log launcher. Opzioni `--keep-models` per preservare la cache GGUF e (solo Windows) `-RemoveDevCert` per rimuovere anche il cert self-signed
### Fixed
- Il registry dei modelli non punta più il tier 16 GB al repo gated `google/gemma-3-12b-it-GGUF` (i download anonimi ricevevano HTTP 401 → tutta la feature AI disabilitata su Apple Silicon / workstation NVIDIA). Ora usa il mirror pubblico `bartowski/google_gemma-3-12b-it-GGUF` con gli stessi pesi. `ensure_model_available` ha anche un fallback automatico: ad ogni fallimento di download prova il tier inferiore invece di restituire `None`. Worst case un host da 64 GB scende gemma-3-12b → qwen2.5-7b → qwen2.5-3b → qwen2.5-1.5b. Chiude #AI-59
- Onboarding wizard ora aspetta il download del modello LLM sul bottone finale "Avvia". Il launcher desktop fa partire il download all'apertura dell'app; l'utente può completare tutti gli step del wizard in parallelo mentre il modello si scarica in background. Sullo step di riepilogo il bottone resta disabilitato con indicatore live "⏳ Attendi modello AI — 78% · ~3 min" finché il download non raggiunge il 100%. Auto-refresh ogni 2 s senza perdere lo stato del wizard (#AI-58 follow-up)
- Onboarding wizard ora include nei suoi settings di completamento i default LLM invisibili (`llm_backend=local_llama_cpp`, `cat_llm_backend=local_llama_cpp`, `llama_cpp_n_gpu_layers=0`, `llama_cpp_n_ctx=4096`, `llama_cpp_model_path` da `$LLAMA_CPP_MODEL_PATH` settato dal launcher). Senza questo seed un utente che completava il wizard si trovava comunque l'errore `llm_backend not configured` sulla pagina Import
- Auto-skip migration ora richiede TUTTE e 4 le prerequisiti del wizard onboarding prima di marcare silenziosamente un utente come "già onboardato": `ui_language` configurata, `owner_names` non vuoto, `llm_backend` configurato, e ≥1 riga in `account`. Prima bastava una sola riga di tassonomia popolata — ma il default taxonomy seed parte su ogni fresh install, quindi ogni nuovo utente veniva saltato direttamente al di là del wizard (#AI-58)
- Auto-invalidazione degli schemi: gli schemi in cache (Flow 1) con parse rate < 10% vengono eliminati automaticamente e ritentati con Flow 2 (riclassificazione LLM)
- Pulizia schemi orfani: la migration di avvio rimuove le righe `document_schema` senza `header_sha256`, prevenendo voci stale irraggiungibili
- Header SHA256 sempre popolato sullo schema prima del persist, evitando la creazione di schemi orfani
- Crash del sanitizer su colonne pandas 2.x `string`-dtype con NaN: `astype(str).tolist()` lasciava trapelare float NaN nel sanitizer regex, sollevando `TypeError: expected string or bytes-like object, got 'float'` su import CSV bancari reali. Fix in `core/classifier.py` (`fillna("").astype(str)`) più guard difensivo in `core/sanitizer.py` (`redact_pii` coerce gli input non-string a `""`). 5 test di regressione in `tests/test_sanitizer.py::TestNonStringInputs` (#108)
- Catena di silent crash del bundle desktop: cinque bug distinti che facevano uscire il `.app` PyInstaller senza output ad ogni launch — `console=False` che ingoiava stdout, il path di `splash.html` che collassava a `_MEIPASS` invece di `_MEIPASS/desktop/`, loop di re-exec infinito quando launcher.py invocava `subprocess.Popen([sys.executable, "-m", "streamlit", …])` (l'eseguibile è il bundle stesso, che rientrava nel launcher), Streamlit che rifiutava `--server.port` perché `global.developmentMode` di default era `true` nel bundle, e `support/logging.py` che hard-codava `os.makedirs("logs")` read-only nel bundle. Tutto sistemato in `fix(desktop): make the PyInstaller bundle actually start` (#AI-49 follow-up)
- Albero processi non killato alla chiusura finestra. `_cleanup` ora usa `os.killpg` sui process group avviati con `start_new_session=True` così Streamlit + uvicorn + grandchildren muoiono insieme. Prima lo Streamlit residuo teneva aperta la porta e macOS Launch Services trattava il doppio-click successivo come "riapertura dell'istanza in esecuzione"
- `packaging/linux/build-rpm.sh` entry icona in `%files` resa condizionale su `ICON_PRESENT=1` così rpmbuild riesce anche quando non c'è `spendifai_256.png` sul build host (chiude AI-56)