docs(spec): drop registry memory + evidence repo separato + LSH scarica-tutto

Tre correzioni da user review:

5. Memory SOLO pgvector, niente registry. Il registry.jsonl di ChironeWp3 è vestigiale:
   una volta che il metadata del vectordb ha subject/detail/rationale (decisione 6), il
   registry non serve. Promotion (F5) = upsert diretto a vectordb. tht/memory.py non
   porta le 6 funzioni registry. 'Cancellare' = metadata.status='superseded' (audit
   trail; il writer e' upsert-only). Multi-workstation OK per costruzione.

7. Evidence: repo workspace separato per-cliente, NON dentro ThothII. ThothII e'
   generico; un repo tht-workspace-<cliente>/ contiene evidence/ + workspace YAML +
   indici LSH. Deploy = checkout ThothII + checkout workspace-cliente. Niente copia
   in harness/.

8. LSH: scarica TUTTI i valori distinti (non 'campiona'), costruisce MinHash+LSH
   dentro harness come preprocessing. Indice per-cliente (nel repo workspace cliente).

Sezione 3 punto 2 (F2) riscritta: save-one diretto, drop reference a promote/index
con registry. Arricchimento metadata ora 'obbligatorio' (non opzionale): senza
registry, il vectordb e' l'unica fonte. Residui nsp nei path spec corretti a tht.
This commit is contained in:
2026-06-27 12:50:15 +02:00
parent ea6412fafc
commit 20e3820bd7
@@ -10,10 +10,15 @@
2. **Drift phase.py**: riscrittura diretta sui 12 siti cmd che usano le vecchie costanti (`MAX_PHASE`/`PHASE_NAMES`/`SCHEMA_LINKING_PHASE`/`DECISION_MIN_PHASE`) → `load_workflow()` + metodi su `Workflow`. Niente strato wrapper.
3. **Skill**: riscrittura completa ex-novo che riflette l'architettura ThothII (widget-descriptor, D11 save-one, D13 free-text, D14 value-grounding/formula, D15 rollback), prendendo spunto dalla struttura ChironeWp3 ma non copiandola.
4. **Indipendenza da ChironeWp3** (proprietà architetturale): quando ThothII è pronto, il server non deve avere ChironeWp3 installato — solo Supabase (DWH + pgvector con le RPC SECURITY DEFINER, indipendenti dal codice app) e la cartella evidence (dentro ThothII). Nessuna dipendenza dal repo `chirone`. Verificato: il codice ThothII non ha riferimenti a ChironeWp3/psdwp3 (rename completo); le RPC vivono nel DB.
5. **Registro memory: locale per-workstation.** `registry.jsonl` vive in `harness/artifacts/memory/` su ciascuna workstation (NON condiviso). La F2 (applicazione) legge dal vectordb condiviso e, grazie all'arricchimento metadata (decisione 6), ricostruisce la decisione senza lookup nel registro. Il registro resta necessario solo per la F5 (promozione: scrive localmente + indicizza sul vectordb condiviso). Multi-workstation OK: le memorie si condividono via vectordb.
6. **Arricchimento metadata memory vectordb**: `subject`/`detail`/`rationale` nel jsonb (vedi Sezione 3 punto 2). Correzione del gap ereditato da ChironeWp3. Reso possibile dal fatto che la tabella memory è vuota al momento del porting.
7. **Evidence: dentro ThothII.** La cartella evidence (contenuto statico curato, 229 markdown) si sposta in `harness/evidence/`. ThothII diventa self-contained (vedi Onda 0b).
8. **Renaming prodotto `tht` (Thoth = prodotto, PSD = cliente).** Il codice non porta traccia del contesto clinico. Rinomine: comando `nsp`→**`tht`**, package `nsp/`→**`tht/`** (46 file import), skill `nsp-sessione`→**`tht-sessione`**, gate `nsp-gate.js`→**`tht-gate.js`**, workspace `chirone.*`→**`tht.example.yaml`/`tht-test.yaml`** (generici-prodotto; un deploy cliente crea il suo `psd.yaml` non-committato). Variabili env `THOTH_*`→**`THT_*`** (uniformate al comando).
5. **Memory: SOLO pgvector, niente registry.** Le memory vivono esclusivamente nella tabella `vectors.memory` (vectordb condiviso). Il `registry.jsonl` di ChironeWp3 è **eliminato** (vestigiale: una volta che il metadata del vectordb contiene subject/detail/rationale, il registry non serve più). La promotion (F5) = upsert diretto a vectordb (`save-one` per una memoria, batch per molte). Le decisioni restano nel ledger di sessione (`review_decisions.jsonl`, per-sessione, separato dalle memory). Multi-workstation OK per costruzione: niente file locale da sincronizzare.
- **Listare memory** → scan del vectordb.
- **"Cancellare"** → il writer REST è upsert-only; non si cancella fisicamente, si marca `metadata.status="superseded"` (audit trail).
- **Re-index (modello embedding cambia)** → operazione server-side che rilegge dal vectordb stesso.
- **Conseguenza nel codice:** `tht/memory.py` NON porta `load_registry`/`save_registry`/`update_record`/`delete_record`/`registry_path`/`promote(registry_path)` (6 funzioni droppate). Restano `MemoryRecord`, `memory_vector_records`, `save_one_memory`. La promotion (F5) si riscrive come upsert batch al vectordb (Onda 3).
6. **Arricchimento metadata memory vectordb**: `subject`/`detail`/`rationale` nel jsonb (vedi Sezione 3 punto 2). Correzione del gap ereditato da ChironeWp3. Reso possibile dal fatto che la tabella memory è vuota al momento del porting. Reso **necessario** dalla decisione 5 (senza registry, il vectordb è l'unica fonte).
7. **Evidence: repository separato per-cliente, NON dentro ThothII.** Il contenuto evidence è specifico del dominio cliente (es. aritmologia @ policlinico). Struttura: ThothII è generico (zero contenuto cliente); un **repo workspace separato per-cliente** (es. `tht-workspace-psd/`) contiene `evidence/`, il workspace YAML cliente, e gli indici LSH (vedi punto 8). Deploy = `checkout ThothII + checkout tht-workspace-<cliente>`, con `.env` che punta `THT_DOCS_ROOT` al repo cliente. Niente "copia in harness/".
8. **LSH: scarica TUTTI i valori distinti, non "campiona".** `tht lsh build` scarica tutti i valori distinti scaricabili delle colonne di testo dal DWH (parametrico, dipende dal workspace), costruisce i MinHash+LSH dentro harness come attività di preprocessing. L'indice risultante è **per-cliente**: vive nel repo workspace separato (decisione 7), non in `harness/indexes/` generico.
9. **Renaming prodotto `tht` (Thoth = prodotto, PSD = cliente).** Il codice non porta traccia del contesto clinico. Rinomine: comando `nsp`→**`tht`**, package `nsp/`→**`tht/`** (46 file import), skill `nsp-sessione`→**`tht-sessione`**, gate `nsp-gate.js`→**`tht-gate.js`**, workspace `chirone.*`→**`tht.example.yaml`/`tht-test.yaml`** (generici-prodotto; un deploy cliente crea il suo `psd.yaml` non-committato). Variabili env `THOTH_*`→**`THT_*`** (uniformate al comando).
9. **Neutralizzazione riferimenti cliente nel codice.** Commenti/docstring che menzionano "ChironeWp3", "PsdWp3", "DWH Chirone", "policlinico", "sandonato" vengono resi generici ("the reference implementation", "the DWH") o rimossi. Il contesto cliente (endpoint `supabase-...policlinicosandonato.it`, nomi schema `datawarehouse`) vive SOLO nei file di configurazione reali (`.env` gitignored, workspace cliente non-committato) — mai nel codice versionato né nei template.
10. **Renaming come Onda -1 (isolata, prima del porting CLI).** Si fa come passo separato e verificato: dopo il rename, `pytest` deve restare **109 passed** (verifica che il renaming non ha rotto nulla). Poi il porting CLI (Onde 0-4) avviene già col nome nuovo `tht` — niente doppio lavoro. Isolare il renaming dal porting permette di debuggare l'uno indipendentemente dall'altro.
@@ -60,40 +65,40 @@ Dopo ogni onda: `pytest` verde (109, nessuna regressione) + import smoke (`tht <
## Sezione 2 — Porting CLI e drift (dettaglio)
### Onda 0b — Setup pre-sessione (evidence + LSH)
### Onda 0b — Setup pre-sessione (repo workspace per-cliente + LSH build)
Perché la sessione L2 validi davvero D14 (value-grounding) e F4 evidence-based (come claims la Sezione 4), servono due setup che oggi mancano:
Perché la sessione L2 validi davvero D14 (value-grounding) e F4 evidence-based (come claims la Sezione 4), servono due setup che oggi mancano. Entrambi sono **per-cliente** (decisioni 7+8), NON dentro ThothII.
**Evidence:**
- La cartella evidence è **contenuto statico curato a mano** (229 markdown, 11M, nessuno script generatore ETL). Verificato: è documentazione clinica/db, non output di un processo. Per il deploy multi-workstation self-contained, **si sposta dentro ThothII** in `harness/evidence/` (non più dipendenza dal path esterno `/Users/mp/Chirone/chirone/etl/docs`).
- Operazione: `cp -r /Users/mp/Chirone/chirone/etl/docs harness/evidence` (path sorgente — l'unico riferimento al repo chirone, puro one-shot di copia) + aggiornare il default di `THT_DOCS_ROOT` a `harness/evidence` + blocco `evidence` nel `tht-test.yaml`:
```yaml
evidence:
source_root: ${THOTH_DOCS_ROOT}
evidence_dir: evidence
**Repo workspace per-cliente (decisione 7):**
- ThothII è generico. Il contenuto evidence è specifico del dominio cliente (aritmologia @ PSD nel caso di test). Struttura di deploy:
```
- **Nota sicurezza:** la cartella va revisionata per contenuto sensibile (dati paziente?) prima di committare nel repo. Se contiene PII, va nel `.gitignore` e distribuita fuori banda, non nel repo pubblico. Da verificare prima del commit.
checkout ThothII # generico, niente contenuto cliente
checkout tht-workspace-psd # repo separato: evidence/ + workspace YAML + indici LSH
```
Con `.env` che punta `THT_DOCS_ROOT=<repo-workspace>/evidence` e `THT_WORKSPACE=<repo-workspace>/psd.yaml`.
- **Non si copia evidence in harness/.** Ogni cliente ha il suo repo workspace.
- Per il test L2 locale: il repo workspace PSD è `/Users/mp/Chirone/chirone/etl/docs` (la cartella esiste). Si crea un repo `tht-workspace-psd/` che la referenzia/conteiene, separato da ThothII. Operazione one-shot del deploy, documentata in README, non nel codice ThothII.
- Verifica: `tht search --kind evidence "<termine>"` ritorna risultati non vuoti su un termine noto.
**LSH index (value-grounding D14a):**
**LSH index (value-grounding D14a, decisione 8):**
- Dipende da `tht.lshindex` (portato in Onda 0) + `tht lsh build` (lsh_cmd, portato in Onda 4).
- Build sul workspace tht-test (UNA volta, prima della sessione):
- Build sul workspace cliente (UNA volta, prima della sessione):
```bash
tht lsh build --workspace workspaces/tht-test.yaml
tht lsh build --workspace <repo-workspace>/psd.yaml
```
Questo campiona i valori distinti delle colonne di testo dal DWH via REST (`db.sampling.unique_values_for_lsh_rest`), costruisce i MinHash + LSH, e li serializza in `indexes/` (path del workspace).
**Scarica TUTTI i valori distinti scaricabili delle colonne di testo** dal DWH (parametrico, via REST `db.sampling.unique_values_for_lsh_rest`), costruisce i MinHash + LSH, li serializza nel path `indexes/` del **repo workspace cliente** (NON in `harness/indexes/`).
- Verifica: `tht search "<valore noto>"` ritorna match multi-colonna (es. "ablazione" su più colonne) e `test_value_grounding_real` (L2) smette di skip-piare.
- Prerequisito: la build richiede il DWH raggiungibile (VPN) + embeddings (Ollama attivo). È un'operazione one-shot, non a ogni sessione.
- Prerequisito: la build richiede il DWH raggiungibile (VPN) + embeddings (Ollama attivo). Operazione one-shot per cliente, non a ogni sessione.
**Ordine:** Onda 0b si esegue DOPO Onda 0 (lshindex) e Onda 4 (lsh_cmd), e PRIMA della sessione L2. È un'operazione dell'operatore (build indice), non codice nuovo — ma va nel piano come step esplicito con la sua verifica.
**Ordine:** Onda 0b si esegue DOPO Onda 0 (lshindex) e Onda 4 (lsh_cmd), e PRIMA della sessione L2. È un'operazione dell'operatore (crea repo workspace + build indice), non codice nuovo — ma va nel piano come step esplicito con la sua verifica.
### Onda 0 — backend mancanti
Tutti piccoli, nessun drift (non toccano phase/workflow). Port verbatim con rename `psdwp3→nsp`:
Tutti piccoli, nessun drift (non toccano phase/workflow). Port verbatim con rename `psdwp3→tht`:
| Modulo | Righe | Dipendenze | Note |
|---|---|---|---|
| `vendor/thoth_lsh.py` + `VENDORED.md` | ~85 | datasketch, tqdm (leaf puro) | copia in `nsp/vendor/` |
| `vendor/thoth_lsh.py` + `VENDORED.md` | ~85 | datasketch, tqdm (leaf puro) | copia in `tht/vendor/` |
| `lshindex/__init__.py` | ~85 | vendor + `LshConfig` (portato) | `sed` rename |
| `sqlcheck/__init__.py` | ~125 | `ExecutionConfig` + `mschema.models` (portati) | leaf |
| `execute/__init__.py` + `execute/warnings.py` | ~150 | `ExecutionConfig` (portato) | leaf |
@@ -109,7 +114,7 @@ I 3 cmd radice si portano verbatim (rename), **eccetto** gli import di `session.
```python
# phase_cmd.py — aggiunta a quanto già portato (meta)
def require_phase_or_exit(cfg, session: str, min_phase: int) -> None:
from nsp.workflow import load_workflow
from tht.workflow import load_workflow
cur = current_phase(session_dir(cfg, session))
if cur < min_phase:
wf = load_workflow()
@@ -131,14 +136,14 @@ from psdwp3.session.phase import MAX_PHASE, PHASE_NAMES, SCHEMA_LINKING_PHASE, D
... MAX_PHASE ... PHASE_NAMES.get(n) ... DECISION_MIN_PHASE.get(type, 1) ... cur < SCHEMA_LINKING_PHASE
# DOPO (ThothII)
from nsp.workflow import load_workflow
from tht.workflow import load_workflow
wf = load_workflow()
... wf.max_phase ... wf.phase_name(n) ... wf.decision_min_phase(type) ... cur < wf.schema_linking_phase()
```
Siti: 5 file cmd (`phase_cmd`, `session_cmd`, `decision_cmd`, `cte_cmd`, `datamart_cmd`).
**Gap da colmare in `Workflow`**: manca il metodo `schema_linking_phase()`. Da aggiungere a `nsp/workflow.py`:
**Gap da colmare in `Workflow`**: manca il metodo `schema_linking_phase()`. Da aggiungere a `tht/workflow.py`:
```python
def schema_linking_phase(self) -> int:
@@ -176,11 +181,11 @@ Riscrittura ex-novo che prende spunto dalla struttura ChironeWp3 ma riflette Tho
1. **Modello gate → widget-descriptor.** La prosa originale dice "dialog native / checklist a caselle". Riscritta in termini di widget: `reviewer_select` (single-pick), `reviewer_decide` (multiselect con payload decisione), `reviewer_confirm` (artifact-gate). Le invarianti (Altro sempre presente, no-limbo, recommended marker) sono garantite dai builder (C1). La disciplina resta identica, il vocabolario cambia.
2. **Fase 2 — save-one (D11) invece di full resync.** Originale: `memory promote` poi `memory index` (full resync, solo-server). ThothII: in `profile=workstation`, la promozione di una singola memoria usa `nsp memory save-one` (upsert mirato via writer key, implementato in B2); `memory promote + memory index` (full resync) resta per `profile=server`. La skill guida il modello al comando corretto: presenta `memory save-one` quando il contesto è una singola memoria promossa in workstation, `memory promote + memory index` quando si fa promozione batch lato server. La discriminante è il comando invocato (la CLI legge il profilo da `.env`); la skill non deve far dedurre il profilo al modello, ma scegliere il comando in base al numero di memorie da promuovere (una → save-one, molte → promote). Onesta sul limite: save-one ha cleanup distruttivo assente (resta server-side).
2. **Fase 2 — save-one (D11).** Originale: `memory promote` (scriveva il registry) poi `memory index` (full resync vectordb dal registry). ThothII **droppa il registry** (decisione 5): le memory vivono SOLO nel vectordb. La promozione di una singola memoria usa `tht memory save-one` (upsert mirato via writer key, implementato in B2). La skill F5 (promozione batch) si riscrive come upsert batch diretto a vectordb (niente registry intermedio). La F2 (applicazione) legge dal vectordb; grazie all'arricchimento metadata (decisione 6), l'hit basta per ricostruire la decisione. Onesta sul limite: il writer REST è upsert-only, la "cancellazione" è logica (`metadata.status="superseded"`).
**Arricchimento metadata vectordb (correzione del gap ereditato da ChironeWp3).** La tabella `vectors.memory` aveva `metadata = {type, session_id, tables, concepts}` — mancavano `subject`/`detail`/`rationale` strutturati, quindi l'hit vettoriale non bastava per applicare la memoria (ChironeWp3 faceva lookup nel registro canonico). ThothII arricchisce: in `memory_vector_records` (`nsp/memory.py`) si aggiungono `subject`/`detail`/`rationale` al dict `metadata` del `VectorRecord`. `pack_metadata` (`rest_writer.py:26`) li serializza automaticamente nel jsonb via `**record.metadata`. Nessuna modifica al writer RPC, nessuna modifica allo schema DB. `search_similar` proietta già `metadata` completo → la F2 ricostruisce la decisione direttamente dall'hit, senza lookup nel registro. Il registro globale resta come source-of-truth per la promozione (F5), ma la F2 legge solo dal vectordb. **Momento ideale: la tabella memory è vuota, niente re-indicizzazione di dati esistenti.**
**Arricchimento metadata vectordb (correzione del gap + reso necessario dal drop del registry).** La tabella `vectors.memory` aveva `metadata = {type, session_id, tables, concepts}` — mancavano `subject`/`detail`/`rationale` strutturati. ThothII arricchisce: in `memory_vector_records` (`tht/memory.py`) si aggiungono `subject`/`detail`/`rationale` al dict `metadata` del `VectorRecord`. `pack_metadata` (`rest_writer.py:26`) li serializza automaticamente nel jsonb via `**record.metadata`. Nessuna modifica al writer RPC, nessuna modifica allo schema DB. `search_similar` proietta già `metadata` completo → la F2 ricostruisce la decisione direttamente dall'hit. **Senza registry, questo arricchimento è obbligatorio** (non opzionale): il vectordb è l'unica fonte. **Momento ideale: la tabella memory è vuota, niente re-indicizzazione di dati esistenti.**
3. **Fase 3 — riscrittura (invariata nel modello, solo vocabolario gate).** Prerequisito "devi essere già in Fase 3" (exit 5, `nsp/phase.py:171`), `question_rewritten` come decisione, sequenza ordinata (decisione → `rewrite_question` tool → `nsp session set-question` scrive `question.md` deterministicamente, `session/store.py:set_question`). Allineamento widget-descriptor. Gestione modifica/rifiuto invariata ("Altro" itera, "Torna indietro" riapre F1).
3. **Fase 3 — riscrittura (invariata nel modello, solo vocabolario gate).** Prerequisito "devi essere già in Fase 3" (exit 5, `tht/phase.py:171`), `question_rewritten` come decisione, sequenza ordinata (decisione → `rewrite_question` tool → `nsp session set-question` scrive `question.md` deterministicamente, `session/store.py:set_question`). Allineamento widget-descriptor. Gestione modifica/rifiuto invariata ("Altro" itera, "Torna indietro" riapre F1).
4. **Fase 4 — value grounding (D14a) + formula (D14b).** Originale F4 menziona solo `table_promoted/excluded/column_corrected`. ThothII aggiunge:
- `value_grounded`: quando un valore citato (es. "ablazione") matcha **più colonne** (flag + testo), il modello presenta `reviewer_decide` con opzioni `value_grounded` per ogni colonna candidata (usando `aggregate_lsh_multi` che non collassa al miglior match). Il reviewer sceglie l'ancora.
@@ -215,7 +220,7 @@ L'intento preciso di ogni fase (vincoli come "max 5 memorie candidate", "solo 3
**Skill:**
- Non unit-testable in L1 (prosa per il modello).
- Verifica automatica: `grep -c "PsdWp3\|psdwp3"` in `harness/.pi/skills/nsp-sessione/` = 0; ogni `nsp <cmd>` citato corrisponde a un sottocomando registrato in `cli/__init__.py` (script di coerenza).
- Verifica automatica: `grep -c "PsdWp3\|psdwp3"` in `harness/.pi/skills/nsp-sessione/` = 0; ogni `tht <cmd>` citato corrisponde a un sottocomando registrato in `cli/__init__.py` (script di coerenza).
### Test L2 — la sessione di test (manuale, pre-rilascio)