247 lines
24 KiB
Markdown
247 lines
24 KiB
Markdown
# Gestione delle relationship: da ThothAI a ThothII
|
|
|
|
**Stato:** analisi e direzione funzionale/UX confermate nel *grill with docs*; non costituisce ancora un piano di implementazione.
|
|
**Revisione esaminata:** commit ThothII `f586152636b1fd653b0bc1d40b54be7f89bbd2bb`. I sorgenti legacy di ThothAI citati sotto sono versionati nello stesso repository, sotto `Thoth/ThothAI/`.
|
|
**Ambito:** import delle foreign key fisiche, inferenza di relationship logiche, modifica umana, pubblicazione verso il workflow NL→SQL e principali gap tra i due sistemi.
|
|
|
|
## Sintesi fattuale
|
|
|
|
1. In ThothAI le foreign key dichiarate nel database venivano importate e una procedura separata proponeva relationship basate sul nome dei campi e su una verifica dei valori. Entrambi i percorsi scrivevano però nello stesso modello `Relationship` (`Thoth/ThothAI/backend/thoth_core/dbmanagement.py:484-640`; `Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:571-826`; `Thoth/ThothAI/backend/thoth_core/models.py:488-530`).
|
|
2. Il ricordo di una relationship inferita persistita come `generated` non trova riscontro nel modello esaminato: `Relationship` contiene solo quattro foreign key verso tabelle e colonne, senza provenienza, stato, confidenza o flag `generated`. La procedura di inferenza calcola localmente pattern e tasso di validazione, ma non li salva (`Thoth/ThothAI/backend/thoth_core/models.py:488-530`; `Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:697-801`).
|
|
3. L'amministratore Django di ThothAI permetteva CRUD manuale sul medesimo insieme di relationship e verificava che gli endpoint appartenessero allo stesso database e alle tabelle selezionate; non distingueva visivamente o semanticamente relationship fisiche, inferite e manuali (`Thoth/ThothAI/backend/thoth_core/admin_models/admin_relationship.py:19-313`).
|
|
4. ThothII oggi separa già i due concetti: il catalogo backend conserva solo le foreign key fisiche dichiarate, mentre il core/harness possiede annotazioni di foreign key logiche curate e un comando che suggerisce candidate per nome, primary key e SQL osservato (`CONTEXT.md:291-300`; `docs/adr/0006-separate-physical-and-logical-relationships.md:3-8`; `harness/tht/cli/schema_cmd.py:203-299`).
|
|
5. I due mondi ThothII non sono ancora integrati: i record di Database Management non modificano il workflow NL→SQL e l'integrazione catalogo→core/schema-linking è ancora un gate di design esplicitamente differito (`PROJECT_STATE.md:116-124`; `PROJECT_STATE.md:161-165`).
|
|
|
|
## Evidenze ThothAI
|
|
|
|
### Foreign key ufficiali
|
|
|
|
Il percorso di import legge le foreign key dal database, limita l'import alle tabelle già presenti nel catalogo, crea le colonne mancanti, crea o riusa un record `Relationship` e aggiorna anche le stringhe denormalizzate `pk_field`/`fk_field` sulle colonne (`Thoth/ThothAI/backend/thoth_core/dbmanagement.py:484-640`, in particolare `:501-513`, `:526-591` e `:606-607`).
|
|
|
|
**Fatto:** una foreign key fisica diventa quindi un record applicativo, non rimane soltanto un fatto letto al momento dal database.
|
|
|
|
### Relationship inferite
|
|
|
|
La routine legacy dichiara sei famiglie di confronto tra il nome della colonna candidata e quello della primary key: corrispondenza esatta, snake case, kebab case, camel case, concatenazione e solo nome tabella (`Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:571-689`).
|
|
|
|
Per una candidata, la routine:
|
|
|
|
- legge fino a 20 valori distinti e non nulli dalla colonna candidata;
|
|
- verifica ogni valore contro la primary key bersaglio;
|
|
- accetta la candidata quando almeno il 70% dei valori esaminati trova riscontro;
|
|
- crea o recupera un normale `Relationship` e aggiorna i campi denormalizzati delle tabelle (`Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:697-801`).
|
|
|
|
**Correzione documentale:** un commento parla di campionamento casuale, ma la query mostrata non contiene un ordinamento casuale; il comportamento verificabile dal codice è “fino a 20 valori distinti e non nulli”, non un campione statisticamente casuale (`Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:697-716`).
|
|
|
|
**Rischio legacy:** nomi di tabelle e colonne sono interpolati direttamente in SQL in questo percorso. Portare la logica letteralmente in ThothII riprodurrebbe un problema di quoting/sicurezza e richiederebbe inoltre una policy esplicita per l'accesso ai valori del DWH (`Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:709-750`).
|
|
|
|
### Un solo modello per tre origini
|
|
|
|
Il modello `Relationship` legacy contiene soltanto `source_table`, `target_table`, `source_column` e `target_column`, più metodi di rappresentazione/aggiornamento. Non contiene campi per origine, algoritmo, evidenza, confidenza, approvazione o disabilitazione, né un vincolo di unicità dichiarato nel modello (`Thoth/ThothAI/backend/thoth_core/models.py:488-530`).
|
|
|
|
L'admin consente aggiunta, modifica e cancellazione ordinarie e valida la coerenza tra database, tabelle e colonne (`Thoth/ThothAI/backend/thoth_core/admin_models/admin_relationship.py:30-38`, `:125-157`, `:218-245`).
|
|
|
|
**Conclusione fattuale:** relationship importate, inferite e create manualmente convergono nello stesso tipo persistito. Dal record finale non è possibile ricostruirne con certezza l'origine. Il termine `generated`, se usato nell'interfaccia o nel linguaggio operativo dell'epoca, non era una qualificazione persistita dal modello esaminato.
|
|
|
|
### Uso nella comprensione dello schema
|
|
|
|
La generazione M-Schema conserva colonne PK/FK e ricostruisce la sezione `【Foreign keys】` analizzando le stringhe denormalizzate `fk_field` (`Thoth/ThothAI/frontend/sql_generator/helpers/main_helpers/main_generate_mschema.py:25-61`, `:94-101`, `:160-187`).
|
|
|
|
**Fatto:** le relationship curate nel catalogo legacy influenzavano la rappresentazione dello schema consumata dal generatore SQL, anche se tramite una proiezione denormalizzata.
|
|
|
|
## Stato attuale di ThothII
|
|
|
|
### Catalogo backend: relationship fisiche
|
|
|
|
Il modello di dominio corrente distingue esplicitamente:
|
|
|
|
- **Physical Relationship:** vincolo dichiarato nel database;
|
|
- **Catalog Relationship:** copia persistita di quel fatto fisico, non creabile o modificabile manualmente ma eliminabile per cleanup;
|
|
- **Logical Relationship:** relazione semantica curata o inferita, con ownership e lifecycle separati (`CONTEXT.md:291-300`; `docs/adr/0006-separate-physical-and-logical-relationships.md:3-8`).
|
|
|
|
La migrazione del catalogo crea `catalog_relationships` e le coppie ordinate in `catalog_relationship_columns`. L'identità della relazione fisica è `(source_table_id, constraint_name)`; non esistono campi di origine, stato o confidenza (`backend/src/catalog/migrations/003_catalog_schema_sync.ts:42-74`). Il tipo TypeScript è solo strutturale e le descrizioni generate riguardano esclusivamente tabelle o colonne (`backend/src/catalog/types.ts:123-152`).
|
|
|
|
L'introspezione PostgreSQL legge soltanto `pg_constraint` con `contype = 'f'` e mantiene l'ordine delle coppie per le chiavi composite (`backend/src/catalog/schema-introspector.ts:110-171`; `backend/src/catalog/schema-introspector.ts:360-416`; `docs/contracts/catalog-schema-snapshot.md:40-53`). La sincronizzazione autoritativa crea, aggiorna o elimina le copie fisiche in base allo snapshot (`backend/src/catalog/repository.ts:1165-1218`; `backend/src/catalog/repository.ts:1316-1393`).
|
|
|
|
Le API e la UI espongono lettura, sync e cleanup in massa, non CRUD logico per singola relationship (`backend/src/routes/catalog-schema.ts:25-37`; `backend/src/routes/catalog-schema.ts:134-150`; `frontend/src/api/catalog-databases.ts:452-489`; `frontend/src/shell/database-management/DatabaseRelationships.tsx:114-123`; `frontend/src/shell/database-management/DatabaseRelationships.tsx:160-225`).
|
|
|
|
**Effetto rilevante:** il cleanup è intenzionalmente reversibile tramite una sync successiva e può lasciare temporaneamente un catalogo incompleto (`docs/adr/0008-allow-manual-catalog-metadata-cleanup.md:7-16`). Un test di integrazione ammette anche una relationship rimasta senza coppie di colonne dopo la cancellazione delle colonne catalogate (`backend/test/catalog-repository.integration.test.ts:194-264`, in particolare `:246-252`). Un futuro consumer non può quindi assumere che ogni stato intermedio del catalogo sia pubblicabile così com'è.
|
|
|
|
### Core/harness: relationship logiche e suggerimenti
|
|
|
|
Il modello M-Schema del core possiede già `TableAnnotation.foreign_keys`, descritte come foreign key logiche curate e unite alle foreign key fisiche (`harness/tht/mschema/models.py:35-39`; `harness/tht/mschema/models.py:76-84`). Il renderer fonde i due insiemi e li presenta insieme nella sezione `【Foreign keys】` (`harness/tht/mschema/render.py:9-20`; `harness/tht/mschema/render.py:46-80`).
|
|
|
|
Il comando `schema suggest-fks` costruisce candidate da:
|
|
|
|
- uguaglianze trovate in SQL, quando esattamente un lato è una primary key;
|
|
- convenzione speciale `*time_key → dim_time.<PK singola>`;
|
|
- stesso nome tra colonna e primary key a proprietario univoco;
|
|
- assunzioni esplicite per disambiguare;
|
|
- esclusione di nomi PK generici come `id`, `key` e `code` e dei proprietari ambigui (`harness/tht/cli/schema_cmd.py:203-299`; `harness/tht/mschema/fkmine.py:1-58`).
|
|
|
|
Il comando può produrre un documento candidato e, con `--write`, aggiungere annotazioni mancanti in modo idempotente; il messaggio stesso chiede revisione manuale (`harness/tht/cli/schema_cmd.py:406-429`; `harness/tht/cli/schema_cmd.py:519-540`; `harness/tests/test_schema_fk_annotations.py:131-146`; `harness/tests/test_schema_fk_annotations.py:454-470`).
|
|
|
|
**Fatto:** ThothII non parte da zero sull'inferenza. Possiede già un motore più conservativo, basato su PK univoche e SQL osservato, ma non conserva per ogni relazione origine, evidenza, frequenza o confidenza. Il miner conta le occorrenze internamente, ma il modello candidato non promuove quel conteggio a lifecycle persistito (`harness/tht/mschema/fkmine.py:1-58`; `harness/tht/mschema/models.py:35-39`).
|
|
|
|
**Rischi strutturali già visibili:**
|
|
|
|
- `ForeignKey` ammette liste di colonne, ma non valida che source e target abbiano la stessa cardinalità; il renderer usa `zip`, quindi una relazione malformata può essere troncata silenziosamente (`harness/tht/mschema/models.py:35-39`; `harness/tht/mschema/render.py:76-78`).
|
|
- il merge evita duplicati rispetto alle FK fisiche iniziali, ma non aggiorna l'insieme `seen` dopo aver aggiunto un'annotazione; due annotazioni logiche uguali possono sopravvivere al merge (`harness/tht/mschema/render.py:9-20`).
|
|
- il catalogo fisico supporta coppie composite ordinate, mentre le euristiche correnti e legacy sono sostanzialmente unary. La semantica delle candidate composite resta da decidere (`backend/src/catalog/migrations/003_catalog_schema_sync.ts:56-74`; `harness/tht/cli/schema_cmd.py:203-299`).
|
|
|
|
### Revisione e pubblicazione correnti
|
|
|
|
Le annotazioni canoniche del workspace sono un blob Git. Il flusso pubblico produce candidate, verifica le annotazioni e richiede un'accettazione umana esplicita dopo commit/push/pull; l'accettazione registra digest del candidato e delle annotazioni, revisione e blob (`docs/contracts/workspace-preprocessing-cli.md:89-106`; `backend/src/workspaces/preprocessing-service.ts:202-297`). Il preprocessing successivo procede solo se il digest accettato coincide con le annotazioni correnti (`backend/src/workspaces/preprocessing-service.ts:335-388`).
|
|
|
|
**Limite fattuale:** il record di review conserva digest, revisione e blob, ma non attore, motivazione o decisioni per singola candidata (`backend/src/workspaces/preprocessing-state.ts:67-73`).
|
|
|
|
Il runtime usa le annotazioni quando renderizza lo schema, ma la ricerca vettoriale indicizza record di tabelle e colonne senza contenuto esplicito delle relationship (`harness/tht/vectorstore/records.py:106-138`; `harness/tht/cli/search_cmd.py:233-273`). Inoltre la vista colonne usata in F4 continua a leggere i commenti fisici, non le annotazioni (`harness/tht/cli/schema_cmd.py:592-621`).
|
|
|
|
La review dei join durante una sessione è distinta dalla curatela globale: il reviewer conferma l'insieme dei join oppure chiede una revisione completa; non modifica la mappa canonica delle relationship (`harness/.pi/skills/tht-sessione/SKILL.md:301-341`; `frontend/src/widgets/JoinReviewWidget.tsx:5-84`). Il modello di sessione registra join come due stringhe e una decisione opzionale, senza ID stabile della relationship o coppie ordinate strutturate (`harness/tht/session/models.py:147-170`).
|
|
|
|
## Delta e rischi da sottoporre al grill
|
|
|
|
| Tema | Fatto documentato | Delta/rischio aperto |
|
|
|---|---|---|
|
|
| Origine | ThothAI perdeva l'origine; ThothII separa fisico e logico a livello concettuale | Decidere quale provenienza debba essere persistita per manuale, euristica, SQL osservato e import fisico |
|
|
| `generated` | Non era un flag del modello ThothAI; in ThothII “Generated Description” è già un termine del catalogo (`CONTEXT.md:302-311`) | Usare `generated` anche per relationship potrebbe creare ambiguità terminologica |
|
|
| Cancellazione utente | In ThothAI era CRUD sul record unico; in ThothII il cleanup fisico viene ricostruito dalla sync | “Eliminare” può significare cancellare una relazione logica, sopprimere un fatto fisico per il core oppure pulire temporaneamente la copia catalogata: sono operazioni diverse |
|
|
| Inferenza | ThothAI usava sei pattern e valori DWH; ThothII usa PK univoche, nomi e SQL osservato | Stabilire se sostituire, integrare o non portare il campionamento dei valori; servono policy di dati sensibili, query read-only, quoting e limiti |
|
|
| Approvazione | ThothII dispone di review Git/digest dell'intero artefatto | Mancano decisioni per candidata, motivazione, attore, sticky rejection, versione algoritmo e gestione dello stale |
|
|
| Compositi | Il catalogo fisico conserva coppie ordinate; le annotazioni accettano liste | Mancano invarianti forti e una strategia di inferenza/review per join compositi |
|
|
| Pubblicazione | Catalogo management e runtime core sono oggi separati | Va stabilito se pubblicare tutto, una selezione esplicita o una revisione immutabile; il catalogo può essere incompleto durante cleanup/sync |
|
|
| UI e ownership | UI catalogo fisico read-only; curatela logica via CLI/Git; review join per sessione separata | Va scelto chi cura la mappa e in quale superficie, senza confondere amministrazione globale e correzione della singola sessione |
|
|
| Retrieval | Le relationship entrano nel render M-Schema ma non nei record vettoriali | Va deciso se e come influenzano selezione tabelle, ranking e descrizioni, oltre al rendering finale |
|
|
| Drift | Sync fisica è autoritativa; annotazioni sono una revisione separata | Servono semantiche per endpoint rinominati/eliminati, candidate stale, orphan e riapprovazione |
|
|
|
|
Nota storica: questa analisi precede il passaggio PostgreSQL catalog-to-core. Per il contratto implementato usare [architettura corrente](../architecture/overview.md) e ADR 0016; le vecchie ipotesi di pubblicazione non sono istruzioni operative.
|
|
|
|
## Direzione confermata nel grill
|
|
|
|
Il 31 agosto 2026 è stato concordato il seguente flusso minimo:
|
|
|
|
1. Le foreign key dichiarate continuano a essere sincronizzate come Catalog Relationship fisiche.
|
|
2. Le foreign key ipotetiche sono salvate una sola volta come Logical Relationship fra colonna
|
|
sorgente e colonna destinazione; le tabelle sono ricavate dalle colonne e la UI le presenta nel
|
|
relativo contesto tabella.
|
|
3. Una relationship inferita è marcata `generated`; una relationship aggiunta dall'utente non lo è.
|
|
4. La cancellazione logica conserva il record e impedisce a una ricostruzione di riattivarlo.
|
|
5. La cancellazione fisica rimuove il record; una ricostruzione può ricrearlo se viene nuovamente
|
|
inferito.
|
|
6. La ricostruzione è additiva: conserva le relationship attive già presenti, non rimuove quelle
|
|
non più inferibili e non modifica le relationship manuali.
|
|
7. L'inferenza usa nomi, primary key e compatibilità dei tipi. Non campiona valori del DWH.
|
|
8. La relationship è l'unica fonte di verità: non viene duplicata in stringhe `fk_field` sulle
|
|
colonne.
|
|
9. I nomi vengono confrontati senza distinzione fra maiuscole/minuscole e normalizzando snake case,
|
|
kebab case e camel case. La regola riconosce anche casi come `user_id → users.id`, richiede tipi
|
|
compatibili e una sola destinazione possibile; riconosce inoltre un nome PK non generico con un
|
|
unico proprietario e la convenzione `*time_key → dim_time.<PK singola>`. Le colonne sorgenti
|
|
possono appartenere a PK composite, mentre nomi generici isolati come `id`, `key`, `code` e `pk`
|
|
non costituiscono evidenza. I casi ambigui vengono ignorati e non si usa fuzzy matching o un LLM.
|
|
10. La ricostruzione parte da un'azione amministrativa esplicita `Rebuild generated relationships`,
|
|
separata dalla sincronizzazione dello schema.
|
|
11. Le relationship cancellate logicamente restano consultabili tramite filtro e possono essere
|
|
riattivate con un'azione `Restore`.
|
|
|
|
Non restano decisioni di dominio aperte per il flusso minimo. La progettazione UX e il seam tecnico
|
|
sono descritti nelle sezioni seguenti.
|
|
|
|
## Lacuna UX emersa nel grill
|
|
|
|
La vista Fleet `Relationships` esiste, ma nella UI di produzione non ha oggi un punto di ingresso
|
|
raggiungibile. La riga del database espone sincronizzazione, tabelle, dettagli, modifica e rimozione,
|
|
ma non le relationship; inoltre i tab `Overview / Tables / Relationships` appartengono soltanto alla
|
|
presentazione legacy. Il test di navigazione esistente esercita anch'esso la presentazione legacy,
|
|
non quella Fleet (`frontend/src/shell/database-management/DatabaseGrid.tsx:122-180`;
|
|
`frontend/src/shell/database-management/DatabaseForm.tsx:442-475`;
|
|
`frontend/src/shell/database-management/DatabaseForm.tsx:517-546`;
|
|
`frontend/src/shell/DatabaseManagementPage.test.tsx:2635-2703`).
|
|
|
|
Se aperta programmaticamente, la vista mostra soltanto `Physical relationships` in sola lettura. La
|
|
toolbar contiene ricerca, conteggio, `Refresh`, un selettore azione con esecuzione esplicita e
|
|
`Sync history`; la griglia offre unicamente `Details`, che apre il drawer della relationship fisica.
|
|
Sono assenti ingresso visibile, aggiunta manuale, ricostruzione delle generated relationship, origine,
|
|
stato attivo/cancellato, cancellazione logica, cancellazione fisica e ripristino
|
|
(`frontend/src/shell/database-management/DatabaseRelationships.tsx:114-225`).
|
|
|
|
I pattern Fleet già consolidati da riutilizzare sono:
|
|
|
|
- una sola griglia nel livello corrente, con breadcrumb e controllo Back;
|
|
- azioni di pagina nel selettore `Choose an action…` con `Run action` e motivo visibile quando
|
|
indisponibili;
|
|
- azioni della singola riga nella colonna finale fissata a destra;
|
|
- form e dettagli in un drawer modeless che restituisce il focus al controllo di origine;
|
|
- conferme distruttive inline o nel drawer, non tramite una nuova pagina;
|
|
- toast per accettazione o errore e feedback persistente soltanto per le operazioni lunghe;
|
|
- card di errore con Retry ed empty state che indica la prossima azione possibile.
|
|
|
|
ThothAI non offre un modello UX da copiare. L'inferenza è nascosta fra 21 bulk action della lista
|
|
database, non mostra avanzamento in tempo reale e restituisce soltanto messaggi a fine richiesta. Il
|
|
CRUD manuale vive in un'altra schermata Django Admin, non distingue origine o stato, offre soltanto
|
|
la cancellazione fisica e il form di aggiunta ha una validazione server strutturalmente incoerente
|
|
con le select popolate dal browser
|
|
(`Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:252-274`;
|
|
`Thoth/ThothAI/backend/thoth_core/admin_models/admin_relationship.py:58-119`;
|
|
`Thoth/ThothAI/backend/thoth_core/admin_models/admin_relationship.py:218-313`).
|
|
|
|
## UX confermata
|
|
|
|
Il 31 agosto 2026 sono state confermate le seguenti scelte:
|
|
|
|
1. La colonna Actions della riga database espone un accesso diretto `Relationships`, accanto a
|
|
`Tables`. La vista conserva breadcrumb e controllo `Back to databases` esistenti.
|
|
2. La pagina presenta una sola griglia `Relationship map`, contenente relationship `Physical`,
|
|
`Generated` e `Manual`. Le colonne sono Source table, Source column, Target table, Target column,
|
|
Origin, Status e Actions. Constraint e regole update/delete rimangono nel drawer delle FK fisiche.
|
|
3. Un filtro visibile seleziona `Active`, `Excluded` o `All`; il valore predefinito è `Active`. Le FK
|
|
fisiche sono consultabili ma non modificabili da questa vista.
|
|
4. `Add relationship` è il pulsante primario visibile nella toolbar. Apre il drawer standard con i
|
|
quattro campi Source table, Source column, Target table e Target column e i comandi `Cancel` e
|
|
`Add relationship`. Il flusso minimo gestisce una sola coppia di colonne e mostra la validazione
|
|
accanto al campo interessato.
|
|
5. `Rebuild generated relationships` entra nell'attuale selettore `Choose an action…`, insieme a
|
|
`Synchronize physical relationships` e `Synchronize full schema`, con esecuzione esplicita tramite
|
|
`Run action`. `Refresh` ricarica la griglia; `Sync history` resta riservato alla sincronizzazione
|
|
fisica.
|
|
6. Il drawer di dettaglio contiene le azioni sulle relationship logiche. `Exclude` realizza la
|
|
cancellazione logica, `Delete permanently` quella fisica e `Restore` riattiva una relationship
|
|
esclusa. La conferma avviene nel drawer e spiega rispettivamente che la ricostruzione non
|
|
riattiverà un record escluso e potrà invece ricreare un record eliminato definitivamente.
|
|
7. La ricostruzione non introduce un nuovo sistema di job o di storico. Durante l'esecuzione mostra
|
|
`Rebuilding…`; al termine un toast riporta added, already present, excluded e ambiguous. Add,
|
|
Exclude, Delete permanently e Restore producono toast specifici; gli errori mantengono aperto il
|
|
contesto corrente. L'empty state propone di ricostruire dai nomi o aggiungere manualmente.
|
|
8. L'inferenza non usa AI. È codice deterministico nel backend del catalogo, basato su normalizzazione
|
|
dei nomi, primary key/unicità, compatibilità dei tipi e assenza di ambiguità. Non usa LLM,
|
|
embedding o campionamento dei dati. L'AI consuma la mappa risultante per comprendere lo schema,
|
|
ma non la costruisce.
|
|
|
|
## Seam tecnico confermato
|
|
|
|
Il Catalog PostgreSQL è l'unica fonte modificabile delle Logical Relationship. Le relationship
|
|
fisiche e logiche restano in modelli distinti, coerentemente con ADR-0006, mentre un servizio profondo
|
|
espone a API e UI una sola mappa discriminata per origine e stato.
|
|
|
|
All'avvio o alla ripresa di una sessione, il backend materializza le sole relationship attive in una
|
|
snapshot JSON canonica e immutabile, collegata alla stessa lease della configurazione runtime. La
|
|
snapshot comprende anche le FK fisiche e conserva l'ordine delle coppie composite. Se due record hanno
|
|
gli stessi endpoint, la FK fisica ha precedenza.
|
|
|
|
Quando la snapshot è presente, l'harness la usa come fonte esclusiva delle relationship e continua a
|
|
leggere dalle annotazioni Git-pinned soltanto descrizioni, sinonimi, concetti e altri metadati. Non
|
|
scrive `annotations.yaml` e non interroga direttamente il Catalog. Una snapshot dichiarata ma assente,
|
|
invalida o incoerente con lo schema fisico fallisce esplicitamente; un runtime legacy che non dichiara
|
|
la snapshot mantiene il precedente comportamento di compatibilità.
|
|
|
|
Questa proiezione non è un secondo store: non può essere modificata, viene eliminata insieme alla
|
|
configurazione runtime e una sessione Pi vede una mappa stabile per tutta la propria vita. La decisione
|
|
duratura è registrata in ADR-0012.
|
|
|
|
La proiezione e la ricostruzione sono ammesse soltanto dopo una sincronizzazione completa della
|
|
versione corrente del database e vengono serializzate con le mutazioni del catalogo. Un catalogo mai
|
|
sincronizzato, reso stale da una modifica della configurazione o invalidato da metadata cleanup non
|
|
può quindi diventare accidentalmente la fonte esclusiva del runtime. Il cleanup esplicito di una
|
|
tabella o colonna endpoint è anche il confine distruttivo del tombstone: rimuove definitivamente la
|
|
relationship esclusa, perché conservarla richiederebbe una seconda identità testuale denormalizzata.
|