Files
ThothII/docs/testing/2026-08-31-metadata-privacy-description-test-plan.md
T
2026-09-15 14:37:29 +02:00

429 lines
34 KiB
Markdown

# Piano di test: metadati di schema, campi sensibili e descrizioni AI
- Data: 2026-08-31
- Stato: proposto
- Baseline analizzata: `f586152` sul branch `test/database-baseline`
## 1. Obiettivo
Validare insieme le tre capacità recentemente introdotte in Database Management:
1. acquisizione autorevole dei metadati fisici di uno schema esterno;
2. proposta e revisione umana dei campi sensibili dal punto di vista privacy;
3. generazione, revisione e consolidamento delle descrizioni di tabelle e colonne.
Il piano è risk-based: perdita o corruzione di metadati, lettura o invio di valori protetti e
applicazione di una selezione al target sbagliato sono rischi bloccanti. La qualità linguistica dei
testi AI è invece valutata separatamente dal contratto tecnico, perché l'output del modello è una
proposta soggetta a revisione umana.
Questo documento non costituisce una certificazione normativa o GDPR: verifica i controlli tecnici
e il flusso operativo implementati dal prodotto.
### 1.1 Assunzione operativa: ambiente completamente sacrificabile
Per indicazione esplicita del proprietario, l'installazione sul Mac è esclusivamente di sviluppo e
test. Non contiene produzione e non esiste alcun requisito di conservazione dello stato locale.
- `catalog-db`, migrazioni, configurazioni locali, run, eventi, flag, descrizioni curate e generate
possono essere cancellati e ricreati tutte le volte necessarie;
- il database reale già collegato è la fonte autorevole dalla quale ricostruire il catalogo ed è la
sorgente primaria per validare schema, classificazione privacy e generazione delle descrizioni;
- reset completi, failure injection, dati incoerenti deliberati e prove distruttive sul catalogo
locale sono ammessi senza backup o procedura di rollback dell'ambiente;
- compatibilità con stato locale preesistente, sessioni legacy e vecchie revisioni del catalogo non
è un gate di questo piano;
- le prove di atomicità, idempotenza e preservazione dei campi restano necessarie perché verificano
il comportamento del prodotto dentro un ciclo di test, non perché debbano proteggere il Mac.
Il database sorgente resta normalmente read-only: quando una prova richiede DDL o mutazioni fra
scan e conferma si usa uno schema di test esplicitamente scrivibile o la fixture supplementare. La
disponibilità a buttare lo stato locale non elimina il rischio di inviare dati personali a un
provider esterno: proprio questa non-esfiltrazione è uno degli esiti principali da collaudare.
## 2. Terminologia e risultato atteso
I tre campi testuali del catalogo hanno autorità diverse e non devono essere confusi:
| Campo | Origine e autorità | Comportamento atteso |
| --- | --- | --- |
| `sourceComment` | Commento fisico acquisito dalla sorgente | Si aggiorna con la sincronizzazione e non è modificabile come contenuto curato. |
| `generatedDescription` | Proposta prodotta dall'AI o corretta nel catalogo | È salvata separatamente, può essere rigenerata esplicitamente e non sovrascrive gli altri due campi. |
| `description` | Descrizione curata dall'amministratore | Cambia solo tramite modifica esplicita o consolidamento di una proposta selezionata. |
Nel seguito, “commento generato” indica `generatedDescription`. La generazione non deve mai
modificare `sourceComment`; il passaggio a `description` avviene solo con il consolidamento umano.
## 3. Riferimenti e baseline
Il comportamento da verificare deriva da:
- stato corrente del progetto in `PROJECT_STATE.md`;
- [contratto Catalog Schema Snapshot](../contracts/catalog-schema-snapshot.md);
- [piano del Metadata Catalog](../reports/knowledge-archives-release.md);
- [specifica della generazione descrizioni](../reports/knowledge-archives-release.md);
- [ADR 0007: sincronizzazione autorevole durevole](../adr/0007-durable-authoritative-schema-synchronization.md);
- [ADR 0009: un solo run sequenziale di generazione](../adr/0009-use-one-sequential-description-generation-run.md);
- [ADR 0010: campioni reali limitati](../adr/0010-allow-bounded-real-source-samples-for-description-generation.md);
- [ADR 0011: Sensitive Data Flag](../adr/0011-gate-source-samples-with-a-sensitive-data-flag.md);
- [accettazione AI del 2026-08-29](./2026-08-29-ai-catalog-description-generation-acceptance.md).
L'accettazione del 2026-08-29 è una baseline utile ma non chiude il gate attuale: precede i commit
`0736983` e `cb40c09`, inviava campioni reali inventati e documentava che un valore campione era
stato ripreso nella descrizione. Deve quindi essere ripetuta sul comportamento privacy corrente.
## 4. Perimetro
### Incluso
- trasporti `postgres_direct`, `rest_api` e `ssh_tunnel`;
- RPC REST tipizzato `POST /rpc/schema_snapshot` e fallback singolo read-only su
`POST /rpc/run_query`;
- metadati di tabelle, colonne, commenti sorgente, tipi, default, nullabilità, posizioni PK e coppie
FK ordinate;
- scope di sincronizzazione `tables`, `columns`, `relationships` e `all`;
- run durevoli, conferma delle differenze distruttive, cancellazione, recovery, eventi SSE e
fallback polling;
- Sensitive Data Flag, analisi locale di metadati e contenuti, review draft e storico operativo;
- scope di generazione `selected_columns`, `selected_tables`, `all` e `missing`;
- campionamento read-only, valori sintetici per colonne protette, batching, retry, stop, Unlock,
storico e consolidamento;
- uso controllato del database reale già collegato, inclusi schema e valori non sensibili, per il
collaudo end-to-end con fake provider e provider configurati;
- permessi, minimizzazione dei dati, redazione di log/errori e resistenza a input ostili.
### Escluso o rinviato
- applicazione del Sensitive Data Flag allo schema-linking/LSH del core, esplicitamente rinviata;
- sostituzione di `annotations.yaml`, pubblicazione Qdrant e cutover del runtime NL→SQL;
- alias, sinonimi, concetti, value descriptions e relazioni logiche;
- audit delle decisioni umane sul flag: per disegno si conserva solo il booleano corrente;
- DDL o scritture sul database sorgente;
- conservazione di cataloghi, run, descrizioni o sessioni locali precedenti al reset;
- compatibilità all'indietro con formati o migrazioni legacy non appartenenti alla baseline corrente.
## 5. Priorità e strategia
| Priorità | Significato | Esempi |
| --- | --- | --- |
| P0 | Gate bloccante di sicurezza o integrità | Nessuna lettura/invio di valori protetti; applicazione atomica; scope esatto; segreti non esposti. |
| P1 | Contratto funzionale necessario al rilascio | Trasporti, stati dei run, recovery, batching, review e consolidamento. |
| P2 | Qualità, UX e robustezza non distruttiva | Copy, focus, benchmark del modello, carico e degrado SSE. |
Le prove sono distribuite su quattro livelli:
1. **Contratto/unità**: repository in memoria, finti connector e Model Completer; nessuna rete.
2. **Integrazione**: catalogo PostgreSQL ricreabile via Docker, database reale come sorgente,
fixture supplementare, fake REST/SSH e Model Completer osservabile.
3. **UI/E2E locale**: component test con MSW e almeno un flusso Playwright sullo stack locale.
4. **Accettazione L2**: provider configurato realmente e database reale dopo review dei flag.
I test deterministici verificano il contratto. Le prove con un modello reale verificano
compatibilità e qualità, ma non sostituiscono i gate P0.
## 6. Ambiente e dati di test
### 6.1 Ambiente minimo
- stack locale con `catalog-db` eliminabile, migrazioni ripetibili e backend/frontend della stessa
baseline;
- database reale già collegato come sorgente primaria, con utenza capace di `SELECT` ma non di
`INSERT`, `UPDATE`, `DELETE`, DDL o cambio di schema;
- PostgreSQL effimero supplementare solo per le mutazioni controllate che non devono essere fatte
sul database reale;
- server REST fake in grado di servire snapshot valido, capability `unavailable`, 404, risposta
parziale/malformata e fallback `run_query`;
- server OpenSSH effimero con `known_hosts` esatto e varianti host key errata/assente;
- Model Completer fake che registra transitoriamente i messaggi e restituisce esiti programmabili;
- browser senza segreti in Web Storage e raccolta di log backend/SSE/API per le scansioni canary.
Le prove PostgreSQL di integrazione non devono risultare `skip`: la disponibilità di Docker è una
precondizione del gate. Il catalogo locale può essere azzerato prima di ogni wave senza snapshot o
backup del suo stato precedente.
### 6.2 Database reale e fixture supplementare `catalog_qa`
La prima baseline viene acquisita dal database reale collegato. Il test registra soltanto inventario
strutturale, conteggi e risultati sanitizzati; poi svuota il catalogo locale e dimostra di poterlo
ricostruire dalla stessa sorgente. Non è richiesto preservare alcun metadato locale precedente.
La fixture `catalog_qa` integra il database reale soltanto quando servono casi controllabili o
mutazioni che la sorgente reale non contiene. Deve includere almeno:
- `customers`: UUID PK, nome, email, codice fiscale, telefono, data di nascita, indirizzo e note;
- `orders`: FK verso `customers`, importo numerico, stato, timestamp, default e campi nullable;
- `order_lines`: PK composta e relazione composta ordinata;
- `clinical_events`: campi sanitari evidenti e tabella partizionata;
- `products`: SKU pubblico, categoria, prezzo e flag booleano;
- `empty_table`: nessuna riga ma struttura valida;
- `wide_entity`: almeno 23 colonne e metadati lunghi, per forzare batch `10 + 10 + 3` e il limite
dimensionale del messaggio;
- identificatori quotati, commenti Unicode/italiani, commenti null e oggetti fuori dallo schema.
Usare valori canary inventati e univoci, per esempio:
- `PRIV_EMAIL_CANARY_...`, `PRIV_TAX_CANARY_...`, `PRIV_HEALTH_CANARY_...` nelle colonne protette;
- `PUBLIC_SKU_CANARY_...` in una colonna esplicitamente non sensibile;
- `SECRET_API_CANARY_...` solo nel secret store del test.
I canary protetti non devono comparire nei messaggi al modello, nel catalogo, negli eventi, nelle
API, nel DOM o nei log. Il canary pubblico può apparire nel messaggio al provider entro i limiti
documentati e dopo la disclosure esplicita dell'utente. Sul database reale la stessa proprietà va
provata soprattutto osservando la proiezione SQL e il payload transitorio: i valori protetti non
devono essere letti, e nessun valore grezzo deve entrare nell'evidenza conservata.
### 6.3 Mutazioni della sorgente
Preparare tre revisioni dello schema:
- **A — iniziale**: struttura completa e commenti sorgente valorizzati;
- **B — distruttiva**: rimozione di una tabella, una colonna e una FK, più aggiunta di una nuova
colonna sensibile per nome;
- **C — race di conferma**: modifica ulteriore fra piano distruttivo e conferma, per provare il
re-scan.
Queste revisioni possono vivere nella fixture o in uno schema reale esplicitamente dichiarato
scrivibile. Fra una prova e l'altra è consentito eliminare completamente il catalogo locale,
riapplicare le migrazioni e ripartire dal database reale.
## 7. Casi di test — acquisizione dei metadati
| ID | P | Livello | Scenario | Risultato atteso |
| --- | --- | --- | --- | --- |
| MET-01 | P0 | API/Integrazione | Avvio senza binding raggiungibile, con versione database obsoleta o senza `database.manage`. | Il run non parte; risposta sicura e catalogo invariato. I segreti restano write-only. |
| MET-02 | P0 | Integrazione | Reset completo del catalogo e `all` sul database reale collegato, senza mock del client `pg_catalog`; ripetizione sulla fixture A per gli edge case assenti. | Il catalogo viene ricostruito da zero con snapshot esatta di tabelle, colonne, `sourceComment`, tipo, default, nullabilità, PK e FK ordinate; stato `succeeded`; nessuna scrittura alla sorgente. |
| MET-03 | P1 | Contratto/Integrazione | Stessa fixture via RPC REST tipizzato. | `schemaVersion: 1` e capability sono validate strettamente; risultato normalizzato uguale a MET-02. `unavailable` non è interpretato come collezione vuota. |
| MET-04 | P0 | Contratto/Integrazione | `/schema_snapshot` assente, fallback `run_query`; poi fallback assente, parziale, non JSON o con campi extra/mancanti. | Il fallback usa una sola query read-only. Ogni errore o snapshot invalida fallisce senza modifiche parziali; nessun fallback nasconde un errore operativo diverso da capability assente. |
| MET-05 | P1 | Integrazione | Accesso `ssh_tunnel` con host key corretta, errata e assente; errore durante apertura/chiusura. | Parità con MET-02 nel caso valido; fail-closed negli altri casi; processi, lease e file-segreto sempre rilasciati. |
| MET-06 | P0 | API | Esecuzione separata di `tables`, `columns`, `relationships` e `all`, con e senza selezione tabelle. | Ogni scope è autorevole solo nel proprio confine; nessun record fuori scope cambia o viene eliminato. Selezioni duplicate/inesistenti sono rifiutate atomicamente. |
| MET-07 | P0 | API/Integrazione | Ripetizione idempotente della fixture A dopo modifica di `description`, `generatedDescription` e `sensitive`. | Nessun diff fisico spurio; contenuti curati, proposte AI e flag delle entità ancora presenti sono preservati. Una nuova colonna nasce con `sensitive=false`. |
| MET-08 | P0 | API/Integrazione | Passaggio A→B, conferma assente/errata/scaduta e passaggio A→B→C prima della conferma. | Stato `awaiting_confirmation`, piano visibile e nessuna applicazione anticipata. La conferma valida provoca re-scan; se il diff cambia viene emesso un nuovo piano/token e il vecchio non applica nulla. Apply atomica oppure zero modifiche. |
| MET-09 | P0 | API/Integrazione | Timeout, disconnessione, capability incompleta o eccezione durante scan/apply. | Stato terminale coerente, errore sanitizzato, catalogo precedente intatto e lock rilasciato. Nessun segreto, SQL sensibile o stack trace nelle API/eventi. |
| MET-10 | P1 | Worker | Cancel in `queued`, `running`, `awaiting_confirmation` e `applying`; retry, restart con run attivo e lease scaduto. | Cancel è efficace solo prima di apply ed è rifiutato durante apply; retry crea un nuovo run. Recovery non duplica l'apply, marca correttamente i run interrotti, rilascia il lock e rimuove snapshot/diff/token interni non più necessari. |
| MET-11 | P0 | API | Due operazioni sullo stesso database: sync, cleanup, connection test, edit o generazione; in parallelo, operazioni su database diversi. | Una sola operazione possiede il database; conflitto 409 sicuro sullo stesso target. Nessun lock cross-database non previsto e nessuna release del token altrui. |
| MET-12 | P1 | UI | Avvio dai menu database/tabella, visualizzazione piano, conferma, history drawer, chiusura drawer, perdita SSE e polling. | Scope e selezione inviati sono esatti; azioni non eleggibili restano visibili con motivo; chiudere il drawer non ferma il run; replay/polling deduplicano gli eventi e aggiornano griglie/KPI. |
| MET-13 | P1 | E2E | Cleanup manuale di tabelle/colonne/relazioni e successiva sincronizzazione. | Cleanup modifica solo il catalogo; la sorgente resta invariata; un sync autorevole ripristina gli oggetti ancora presenti in sorgente. |
| MET-14 | P0 | Integrazione | Cambio binding/versione fra scan e apply ed errore iniettato a metà transazione. | La freshness viene ricontrollata sotto lock; il run fallisce senza righe parziali e conserva integralmente il catalogo precedente. |
| MET-15 | P1 | API/Worker | Replay SSE con `Last-Event-ID`/`after`, polling concorrente e retention oltre 30 giorni. | Cursori monotoni e nessun duplicato; gli eventi scaduti vengono potati senza corrompere run e stato finale. |
## 8. Casi di test — identificazione e protezione dei campi sensibili
| ID | P | Livello | Scenario | Risultato atteso |
| --- | --- | --- | --- | --- |
| PRV-01 | P0 | Repository/API | Prima sincronizzazione, re-sync e aggiunta di una colonna. | Il default è `false`; il valore umano delle colonne esistenti è preservato; la nuova colonna è esplicitamente da riesaminare ma non riceve uno stato audit inventato. |
| PRV-02 | P0 | API | Analisi per un database, tabelle selezionate e colonne selezionate; database multipli, target duplicati o mancanti. | Solo i target dello scope raggiungono l'adapter read-only. Input ambigui sono rifiutati prima della lettura e nessun flag cambia. |
| PRV-03 | P0 | Unit/Contratto | Valori con email, codice fiscale italiano valido, IBAN, carta con Luhn, chiave privata, chiave JSON sensibile e testo oltre 500 caratteri. | Un solo riscontro validato rende l'intera colonna `sensitive`; l'evidenza contiene solo rule ID e conteggi sanitizzati, mai il valore. |
| PRV-04 | P0 | Integrazione | Scansione completa oltre cinque secondi, timeout PostgreSQL e budget globale di sessanta secondi. | L'adapter passa al campionamento, ripristina la transazione dopo `statement_timeout`, resta read-only e non supera la deadline. Copertura incompleta senza match produce `unknown`. |
| PRV-05 | P0 | Unit/API | Tabella vuota, colonna all-null, binario non ispezionabile, scan completo senza match e scan incompleto senza match. | Gli esiti sono rispettivamente `unknown`, `unknown`, `unknown`, `non_sensitive` e `unknown`; `unknown` conserva la scelta umana corrente. |
| PRV-06 | P0 | UI/API | Apertura draft, modifica manuale, chiusura/reload e salvataggio. | La proposta non è persistita prima di Save; reload la scarta. Il reviewer può invertire scelte; si salvano solo colonne cambiate con versione ottimistica; un conflitto richiede reload. |
| PRV-07 | P1 | Repository/UI | Tentativi completati, falliti e attivi al restart. | Ogni tentativo ha un run distinto con scope, engine `local`, versione policy, tre contatori ed eventi sanitizzati; startup marca `interrupted` i run attivi. Storico newest-first senza target ID, proposte, valori o diagnostica worker. |
| PRV-08 | P0 | Integrazione | Generazione descrizioni su target con canary protetti. | Le colonne protette sono assenti dalla proiezione SQL, non semplicemente filtrate dopo la lettura. Se non rimangono colonne leggibili non viene eseguita una `SELECT`. Nessun canary protetto esce dal processo. |
| PRV-09 | P0 | Contratto/Integrazione | Tabella mista con colonne sensibili e pubbliche. | Per le sensibili il prompt contiene valori plausibili, deterministici e limitati derivati dai soli metadati, nello stesso formato dei campioni e senza etichettarli al modello come sintetici. Per le pubbliche: massimo cinque righe e cinque valori rappresentativi, valori troncati e transazione read-only chiusa con rollback. |
| PRV-10 | P1 | API | Cambio `false→true→false` dopo una descrizione già generata. | Il testo esistente non viene rigenerato retroattivamente. Solo le generazioni future cambiano fonte del contesto; tornando `false` il campionamento reale torna eleggibile. |
| PRV-11 | P0 | API/UI | Utente senza `database.manage`, sorgente/adapter indisponibile, NER assente o in timeout e richiesta interrotta. | Controlli nascosti/disabilitati in UI e rifiuto server-side; errori non espongono dettagli. Il NER opzionale degrada alle regole/coverage senza selezionare un LLM. |
| PRV-12 | P1 | L2 | Corpus etichettato con identificatori personali, credenziali/token, salute, finanza, localizzazione e controlli non sensibili/ambigui, in inglese e italiano. | Si misurano precisione, recall, falsi negativi, copertura e latenza separatamente per policy deterministica e NER CPU. La soglia va ratificata prima di abilitare NER per default; il classifier resta advisory e human-in-the-loop. |
| PRV-13 | P0 | UI/E2E | Modifica di un flag nella review senza Save e tentativo immediato di generare descrizioni. | Gate di rilascio da formalizzare: la generazione deve essere bloccata finché il draft non è salvato o scartato. In alternativa la UI deve dichiarare inequivocabilmente che verrà usato il valore persistito; non è accettabile mostrare “protetto” e campionare come non protetto. |
## 9. Casi di test — generazione e consolidamento dei commenti
| ID | P | Livello | Scenario | Risultato atteso |
| --- | --- | --- | --- | --- |
| GEN-01 | P0 | Config/API | Modelli validi, default, endpoint anonimo esplicito, secret ref mancante/errato e nessun modello. | Al browser arrivano solo ID, label e default. Nessuna chiave, provider payload o configurazione privata è esposta; feature disabilitata in modo comprensibile se non configurata. |
| GEN-02 | P0 | API | `selected_columns`, `selected_tables`, `all`, `missing`, target duplicati/inesistenti e zero eleggibili. | Scope esatto; `missing` include null/vuoto/whitespace e salta proposte esistenti; `all` sostituisce solo dopo conferma esplicita; input invalido non crea run. |
| GEN-03 | P1 | Worker | 23 colonne più tabelle, metadati e campioni lunghi. | Colonne prima delle tabelle per lo scope globale; richieste omogenee, sequenziali, massimo dieci target e sotto i limiti byte; contesto tabella aggiornato dopo le colonne. |
| GEN-04 | P0 | Contratto | JSON puro, un solo code fence JSON completo, prosa extra, payload multipli, target mancante/duplicato/ignoto, descrizione vuota o oltre limite. | Sono accettati solo i primi due formati validi. Una mappatura ambigua non applica alcun risultato del batch e conta come errore tecnico. |
| GEN-05 | P1 | API | Esito `generated` e `non_generatable` in workspace italiano/inglese. | Testo generato trimmato e salvato; il testo standard non generabile è localizzato dall'applicazione, non copiato dal provider. Gli errori tecnici non scrivono tale testo. |
| GEN-06 | P0 | Worker | Errore transiente, errore esaurito isolato, tre batch falliti consecutivi e successo fra due errori. | Helper con al massimo un retry e nessun fallback modello. Errori isolati portano a `completed_with_errors`; tre consecutivi a `failed`; un successo azzera il contatore. |
| GEN-07 | P0 | Integrazione | Successi seguiti da cancel, interruption o failure. | Ogni risultato valido è persistito subito e resta disponibile; il target fallito non riceve dati ambigui. “Generate Missing” consente il recupero naturale senza resume automatico. |
| GEN-08 | P0 | API | Secondo run durante un run attivo e modifica/sync/cleanup/consolidamento sul database posseduto. | Un solo run di generazione attivo nell'installazione; 409 chiaro al secondo Start. Il database target resta riservato e le operazioni incompatibili non alterano selezione o dati. |
| GEN-09 | P0 | Integrazione/Worker/UI | Stop in queued/running con helper attivo e con una `SELECT` sorgente deliberatamente bloccata/lenta. | Helper e query/connessione vengono terminati entro 5 secondi, stato `cancelled`, nessuna chiamata modello dopo Stop, risultati precedenti conservati ed eventi consultabili. Un test con sampler fake non è sufficiente. |
| GEN-10 | P0 | Worker/API | Restart con run `queued/running`; Unlock con worker/helper vivo e con run realmente stale; race Unlock/Start. | Startup marca `interrupted` senza replay. Unlock è rifiutato se esiste lavoro locale vivo e non può liberare la reservation di un nuovo run. |
| GEN-11 | P1 | Integrazione | Sorgente campioni indisponibile ma metadati validi. | La generazione prosegue metadata-only con un solo warning sicuro; nessun tentativo alternativo espone dettagli di connessione. |
| GEN-12 | P1 | API/UI | SSE disconnesso, replay da sequence, polling concorrente e riapertura storico. | Eventi persistiti, ordinati e deduplicati; history newest-first; contatori/stato finali coerenti; nessun prompt, campione, risposta completa o stack trace. |
| GEN-13 | P0 | UI/API | Revisione manuale della proposta e consolidamento selettivo di tabelle/colonne, inclusi target vuoti/stale. | Si copia solo `generatedDescription` non vuota dei target risolti; conteggio `copied/skipped` corretto; operazione atomica; `sourceComment` invariato. |
| GEN-14 | P0 | UI/Sicurezza | Tutti gli scope di generazione da database, tabelle e colonne selezionate, utente senza permesso, metadata contenente istruzioni ostili. | Prima di ogni Start, inclusi `selected_tables` e `selected_columns`, compare la disclosure “fino a cinque righe e cinque valori”. Il server applica comunque l'autorizzazione. Metadati e valori sono trattati come dati non fidati e l'output resta nel contratto JSON. |
| GEN-15 | P1 | L2 | Run reale sul modello di default e almeno un endpoint alternativo supportato, usando il database reale dopo la review dei flag e un role read-only. | Descrizioni nella lingua workspace, coerenti con struttura/commenti e senza fatti inventati critici; almeno una colonna e una tabella consolidate. Nessun valore protetto o segreto compare in request osservabile, eventi, API, persistenza o log. |
| GEN-16 | P1 | Integrazione | Fastify→worker→processo Python→LiteLLM→endpoint OpenAI-compatible locale di cattura. | Routing provider/model, header API key, `disableThinking`, singolo retry, timeout, limite output e payload sono corretti; chiave e diagnostica non risalgono a stdout, API o log applicativi. |
## 10. Percorso E2E prioritario
Il caso `E2E-01` deve attraversare le tre feature senza sostituire i test di contratto:
1. azzerare `catalog-db`, riapplicare le migrazioni e verificare che il role del database reale sia
realmente read-only;
2. eseguire `all` sul database reale e confrontare catalogo e snapshot sorgente; usare la fixture A
in una seconda esecuzione per gli edge case mancanti;
3. richiedere suggerimenti privacy sull'intero database;
4. modificare almeno una proposta e salvare i flag revisionati;
5. avviare `missing` dopo la disclosure, usando un Model Completer osservabile;
6. dimostrare che i canary protetti non sono letti né inviati e che il canary pubblico rispetta i
limiti;
7. correggere una `generatedDescription` e consolidare una tabella e una colonna;
8. applicare la fixture B, controllare il piano distruttivo e introdurre C prima della conferma;
9. confermare dopo il re-scan e verificare atomicità, preservazione di descrizioni/flag delle entità
superstiti e `sensitive=false` sulla nuova colonna;
10. perdere la connessione SSE, riaprire entrambi gli storici e verificare replay, polling e KPI.
Il percorso deve essere eseguito con fake provider dopo ogni reset rilevante e, in forma ridotta,
con il provider configurato sul database reale dopo che i flag sono stati revisionati e salvati.
Non è richiesto ripristinare lo stato locale precedente al test.
## 11. Valutazione qualitativa dei modelli
La qualità non deve confondersi con la sicurezza: anche un classifier perfetto non autorizza
l'esfiltrazione di un canary protetto e una descrizione elegante non rende valido un payload
ambiguo.
### 11.1 Classificazione privacy
Per ogni modello registrare matrice di confusione, precisione, recall e falsi negativi per categoria.
Eseguire almeno tre iterazioni sul corpus fisso per rilevare instabilità. Fino alla ratifica di una
soglia da parte del product owner, il risultato è un gate di review: ogni falso negativo su
credenziali/token, identificatori fiscali, dati sanitari o finanziari richiede accettazione esplicita
o correzione prima del rilascio operativo.
### 11.2 Descrizioni generate
Valutare ogni testo da 0 a 2 su:
- correttezza rispetto a struttura e commenti sorgente;
- specificità e utilità per un revisore;
- lingua e chiarezza;
- assenza di istruzioni seguite dai dati non fidati o fatti inventati;
- assenza di valori protetti e segreti.
Soglia proposta da ratificare: almeno 8/10, nessun punteggio 0 su correttezza o sicurezza. Un valore
esplicitamente non sensibile, reale o inventato, può essere ripreso entro il perimetro dichiarato;
un valore protetto non può mai esserlo.
## 12. Copertura esistente e gap da chiudere
| Area | Evidenza automatica già presente | Gap principale |
| --- | --- | --- |
| Snapshot e sincronizzazione | `backend/test/catalog-schema-introspector.test.ts`, `catalog-schema-routes.test.ts`, `catalog-table-introspector.test.ts`, `catalog-repository.integration.test.ts` | Introspezione `pg_catalog` realmente end-to-end, parità live dei tre trasporti e un unico E2E con re-scan distruttivo. |
| Privacy | `catalog-sensitivity-classifier.test.ts`, `catalog-sensitivity-value-source.test.ts`, `catalog-local-ner-detector.test.ts` e i test di route/review | Prova shadow PSD con report solo aggregato, corpus italiano etichettato e benchmark NER CPU post-ADR 0014. |
| Generazione | `catalog-description-generation-routes.test.ts`, `catalog-description-generation-worker.test.ts`, `catalog-description-generation.integration.test.ts` e test del helper | Accettazione reale aggiornata, cancellazione di una query PostgreSQL bloccata e integrazione ermetica fino all'endpoint LiteLLM locale. |
| UI | `DatabaseManagementPage.test.tsx`, `DescriptionGenerationDrawer.test.tsx`, `SensitivityAnalysisHistoryDrawer.test.tsx` | L'E2E Playwright corrente verifica soprattutto il layout, non il workflow funzionale. |
Nuovi asset consigliati:
- `backend/test/catalog-metadata-privacy-workflow.integration.test.ts`;
- `backend/test/fixtures/catalog-privacy-schema.sql` e snapshot REST equivalenti;
- integrazione a due PostgreSQL per le query reali `pg_catalog` e lo stop del sampler;
- endpoint OpenAI-compatible locale di cattura per GEN-16;
- `frontend/e2e/database-management-workflow.spec.ts`;
- corpus strutturale versionato per PRV-12, senza valori business;
- nuovo report di accettazione L2 che sostituisca il gate privacy del 2026-08-29.
## 13. Ordine di esecuzione
### Wave 1 — contratto rapido
- parser snapshot, introspector, scope e diff;
- classifier locale, validatori/checksum, copertura e fallback al campionamento;
- sampler, valori sintetici, prompt bounds e parser descrizioni;
- autorizzazione, redazione e race del coordinator.
### Wave 2 — integrazione PostgreSQL
- reset totale di `catalog-db`, bootstrap delle migrazioni e ricostruzione dal database reale;
- migrazioni e vincoli del repository;
- atomicità sincronizzazione/consolidamento;
- run ed eventi persistiti, restart e cancellation;
- `E2E-01` con fake provider e canary.
### Wave 3 — frontend e stack locale
- component test MSW;
- Playwright funzionale, perdita SSE e polling;
- verifica disclosure, review draft, history e consolidamento.
### Wave 4 — accettazione L2
- analisi shadow sul database collegato e review dei flag, senza scritture in sorgente;
- benchmark NER CPU PRV-12 e rubric GEN-15 per la generazione descrizioni;
- scansione finale di canary e segreti;
- approvazione del product owner.
Comandi di regressione:
```bash
cd backend && npx vitest run
cd backend && npx tsc --noEmit -p .
cd backend && npm run build
cd frontend && npx vitest run
cd frontend && npx tsc -b
cd frontend && npm run build
cd frontend && npm run e2e
./scripts/build-docs.sh
```
Il report deve evidenziare esplicitamente eventuali test Docker/L2 saltati; un `skip` non equivale a
PASS del relativo gate.
## 14. Evidenze da conservare
Per ogni esecuzione registrare:
- commit, configurazione pubblica dei modelli, versione fixture e trasporto;
- ID e stato finale dei run, contatori ed eventi sanitizzati;
- snapshot catalogo prima/dopo e piano distruttivo con metadati e conteggi sanitizzati, senza valori
grezzi delle righe sorgente;
- report test/JUnit, screenshot dei gate UI e risultato della scansione canary;
- matrice di confusione privacy e rubric delle descrizioni per le prove L2;
- difetti con ID del caso, severità, riproducibilità e decisione finale.
Non allegare prompt completi, righe campione, output grezzi del provider, chiavi, digest o frammenti
di segreti. Il Model Completer spy deve verificare in memoria le asserzioni e scartare il payload al
termine del test.
Non serve conservare backup del catalogo locale, run precedenti o descrizioni generate durante una
wave: l'evidenza è il report sanitizzato e la capacità di ricostruire nuovamente il risultato dalla
sorgente reale.
## 15. Criteri di ingresso e uscita
### Ingresso
- baseline unica per backend/frontend e procedura verificata per eliminare e ricreare `catalog-db`;
- accesso al database reale collegato e inventario delle tabelle/colonne da includere nella review;
- fixture e canary supplementari approvati per i soli edge case controllati;
- role sorgente read-only verificato con una scrittura deliberatamente negata;
- fake connector/provider disponibili e log collection attiva;
- per L2, secret reference configurato senza materializzare il valore nell'evidenza.
### Uscita
- 100% dei casi P0 e P1 applicabili superati; nessun difetto Sev-1/Sev-2 aperto;
- almeno due ricostruzioni complete e coerenti del catalogo a partire dal database reale dopo reset
indipendenti;
- zero comparsa dei canary protetti e dei segreti fuori dalla sorgente/secret store del test;
- nessuna modifica parziale dopo errori, cancel o conferme stale;
- parità normalizzata dei trasporti supportati e nessuna integration PostgreSQL richiesta saltata;
- stati, contatori, eventi e history coerenti dopo success, partial failure, stop e restart;
- `sourceComment`, `generatedDescription` e `description` mantengono l'autorità prevista;
- accettazione L2 sul database collegato approvata dal product owner e
build/test/typecheck/documentazione verdi;
- ogni deviazione P2 o soglia qualitativa non ancora ratificata è documentata con owner e data.
## 16. Rischi residui da rendere espliciti
- `sensitive=false` è il default e il prodotto non conserva uno stato “review completata”: dopo ogni
reset, classificazione strutturale e salvataggio umano dei flag devono precedere qualunque run con
provider reale. Il catalogo è ricostruibile; un invio errato a un provider non lo è.
- Il flag protegge i valori campionati. Nomi, `sourceComment` e descrizioni sono metadati inviabili
al modello e possono contenere testo libero: se nel database reale includono PII serve una
decisione aggiuntiva di redazione, non una diversa aspettativa di test.
- La UI deve risolvere il caso di un flag modificato ma non salvato prima della generazione e deve
mostrare la disclosure anche per tabelle/colonne selezionate; il piano considera entrambi P0.
- L'AbortSignal corrente va provato contro una query PostgreSQL realmente bloccata: la sola
cancellazione del helper non dimostra che la lettura sorgente sia interrompibile.
- Lo storico dei Sensitivity Analysis Run è operativo, non un audit delle decisioni umane.
- La policy privacy non è ancora applicata allo schema-linking/LSH; nessun risultato di questo piano
deve essere presentato come copertura di quel percorso.
- Un provider reale resta non deterministico: il rilascio deve dipendere dai gate tecnici e dalla
review umana, non dalla ripetizione byte-identica delle descrizioni.
- L'esclusione fra operazioni e Unlock è in parte locale al processo. Se il deployment ammetterà più
repliche backend, servirà un gate aggiuntivo con due istanze contro lo stesso catalogo; non va
dedotta sicurezza multi-replica dai test single-process.