docs: add canonical Evidence structure design

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
This commit is contained in:
2026-08-18 09:52:53 +02:00
co-authored by CommandCodeBot
parent 18233d59af
commit 1d045a44a6
@@ -0,0 +1,211 @@
# Evidence canonica — struttura tipizzata per disambiguazione, schema linking e SQL
## Contesto e decisioni prese
ThothII ha già due livelli separati che non si parlano:
- **`EvidenceDoc`** (`harness/tht/evidence/model.py`) — runtime: frontmatter piatto
(`id/title/tier/status/tables/concepts/sources`) + `body` markdown libero. `tier`
distingue solo `structural|concept`, insufficiente rispetto ai 6 tipi reali del corpus.
- **Pipeline corpus** (`harness/tht/corpus/`) — canonizzazione *tecnica* (hash,
provenienza, chunking, vettorizzazione), agnostica rispetto al tipo di evidenza.
Il corpus reale (`ChironeWp3/artifacts/evidence/`) ha una tassonomia implicita in 6
directory (`00-glossario`, `10-domini-clinici`, `20-valori-enum`, `30-esempi-nlq`,
`40-mapping-semantico`, `50-metadati-normalizzazione`) ma:
- frontmatter piatto, `tier` inadeguato;
- file già rotti (`--` invece di `---`, bullet `•⁠ ⁠` invece di `-`) che `EvidenceDoc.parse`
rifiuterebbe;
- al retrieval la struttura si perde: `tht search pack` proietta solo `title` + 400 char.
**Decisioni (confermate nel brainstorming):**
1. **Obiettivo**: strutturare il *contenuto* runtime, non toccare la pipeline corpus.
2. **Forma**: ibrido — frontmatter tipizzato + sezioni canoniche per `kind`.
3. **Tassonomia**: doppia — `kind` (contenuto) + `applies_to` (destinazioni:
`disambiguation`, `rewriting`, `schema_linking`, `sql_generation`, `memory`).
4. **Anchors come fonte primaria** per il value-grounding in F4; LSH solo fallback.
5. **Approccio**: contratto in `tht` + authoring sottile (niente app separata, niente
client LLM diretto in `tht`).
6. **Modello sorgente→canonizzato** (non sovrascrittura in place): file umani in
`source_root/evidence/`, canonizzati derivati in `artifacts/evidence/`, coerente con
l'esistente `tht evidence extract`.
7. **Review**: batch con diff aggregato (approvazione per file).
8. **Rielaborazione**: LLM-assistita via Pi (sessione di manutenzione + gate), con parte
deterministica in `tht`.
## Modello canonico v2
### `CanonicalEvidence` (frontmatter tipizzato)
```yaml
---
schema_version: 2
id: ev-dom-ablazione-see
title: Dominio Ablazione e SEE
kind: domain # glossario | domain | enum | example | mapping | normalization
applies_to: # destinazioni d'uso
- disambiguation # F1
- schema_linking # F4
- sql_generation # F6/F7
status: reviewed
language: it
concepts:
- term: ablazione
synonyms: [ablazione transcatetere, SEE, studio elettrofisiologico]
- term: fibrillazione atriale
synonyms: [FA]
tables:
- name: datawarehouse.fact_studio_elettrofisiologico_endocavitario_ablazione
role: fact
columns: [ablazione_transcatetere, cod_paz, num]
anchors:
- column: datawarehouse.fact_see_ablazione_procedura_patologia.patologia
value: ablazione
match: exact
sources: [...]
# provenienza del derivato (aggiunta dal canonicalize, non dall'umano):
source_file: 10-domini-clinici/ablazione.md
source_fingerprint: sha256:...
canonicalized_at: 2026-08-18T...
---
```
Il `body` resta markdown, ma con **titoli di sezione canonici per `kind`**:
- `glossario`: `## Definizione`
- `domain`: `## Cosa rappresenta`, `## Schema a stella`, `## Granularità`, `## Domande di business tipiche`
- `enum`: `## Valori ammessi`
- `example`: `## Domanda → SQL` + `## Nota clinica`
- `mapping`: `## Trasformazioni disponibili`
- `normalization`: `## Regole di normalizzazione`
Le sezioni canoniche permettono al retrieval di estrarre solo la sezione rilevante per
fase invece di 400 caratteri generici.
### `kind` vs `applies_to`
- `kind` descrive il *contenuto* (i 6 tipi già impliciti).
- `applies_to` descrive *quando usarlo*. Esempi: `enum-tipo-intervento` è `kind: enum` ma
`applies_to: [disambiguation, sql_generation]`; un `example` è
`applies_to: [sql_generation, memory]`.
## Modello a 3 livelli
```
source_root/evidence/*.md (umano, libero, può essere sporco)
│ tht evidence canonicalize (deterministico + LLM via Pi + review batch)
▼
artifacts/evidence/*.md (canonico, validato, derivato, rigenerabile)
│ tht evidence index (esistente)
▼
corpus / vector store (vettorializzato dal canonico, mai dal sorgente)
```
Il canonizzato porta `source_file` + `source_fingerprint`: se il sorgente cambia, la
canonizzazione ripropone il diff; se invariato, no-op.
## Componenti da creare/modificare
### 1. `harness/tht/evidence/model.py` (modifica)
- Aggiungere `CanonicalEvidence` (pydantic, v2) con `schema_version`, `kind`,
`applies_to`, `concepts[]` (`{term, synonyms[]}`), `tables[]`
(`{name, role, columns[]}`), `anchors[]` (`{column, value, match}`), `source_file`,
`source_fingerprint`, `canonicalized_at`.
- Validatore strict: `kind` ammesso, `applies_to` ammesso, `anchors[].column` deve
essere `schema.colonna` ben formato, `match` in `{exact, contains, regex, substring}`.
- `EvidenceDoc` v1 resta per leggere il sorgente e per retro-compatibilità dei vecchi
canonizzati; `CanonicalEvidence` è un modello separato, non una sottoclasse.
- Validatore che garantisce `source_fingerprint` = sha256 del contenuto sorgente.
### 2. `harness/tht/evidence/lint.py` (nuovo, deterministico)
- `lint_source(path) -> list[Diagnostic]`: frontmatter rotto, `tier`/`status` non validi,
bullet Unicode, campi mancanti, `tables` non qualificate con schema, concetti senza
sinonimi, sezioni non canoniche per il `kind` atteso.
- Exit code 0 se nessun errore, 1 se warning, 2 se errori. Nessun LLM.
### 3. `harness/tht/evidence/canonicalize.py` (nuovo)
- `plan(sources, artifacts) -> CanonicalizePlan`: confronta fingerprint dei sorgenti con
i canonizzati esistenti, produce la lista dei file da (ri)canonizzare.
- `render_proposal(source, llm_completions) -> CanonicalEvidence`: assembla il
canonizzato da parsing deterministico + campi semantici forniti da Pi.
- `apply(plan, approved_ids) -> None`: scrive solo i canonizzati approvati in
`artifacts/evidence/`, atomico per file, mantiene la gerarchia per dominio.
- `diff(source, candidate) -> str`: diff markdown per il gate.
### 4. `harness/tht/vectorstore/records.py` (modifica)
- `evidence_records()`: metadata ora include `kind`, `applies_to`, `anchors` (non solo
`status/tier/tables/concepts`).
- Il `content` di ogni record resta title+body, ma per file con sezioni canoniche si
indicizza anche un record per sezione (id `evidence:<id>:<sezione>`) così il retrieval
può restringere per sezione oltre che per documento.
### 5. `harness/tht/search/__init__.py` + `cli/search_cmd.py` (modifica)
- `SearchResult` e `combined_search` accettano `applies_to` come filtro metadata.
- `tht search pack`: la sezione "Evidence rilevanti" ora proietta `kind` + sezione
canonica pertinente (non solo excerpt generico); usa `applies_to` per non mischiare
destinazioni.
- `tht search find --kind evidence --applies-to <dest>` per il retrieval mirato.
### 6. `harness/tht/cli/evidence_cmd.py` (modifica)
- Nuovi comandi:
- `tht evidence lint --source <path|dir>` — diagnostica deterministica.
- `tht evidence canonicalize plan --source-root <dir> --json` — piano di
(ri)canonizzazione.
- `tht evidence canonicalize apply --plan <file> --approved <ids> --json` — applica.
- `--json` sempre pristine (solo JSON su stdout), come da contratto di progetto.
### 7. `harness/.pi/extensions/tht-gate.js` (modifica)
- Nuovo tool `reviewer_evidence_batch`: riceve il piano + i diff aggregati e presenta un
multiselect per file (approva/rifiuta/richiedi modifica). Le scelte approvate vengono
persistite tramite `tht evidence canonicalize apply`.
- Aggiungere `tht evidence canonicalize apply` alla FORBIDDEN anti-bypass list (il
modello non può applicare canonizzazioni senza review).
### 8. Sessione di manutenzione Pi (nuova skill o sezione SKILL)
- Una skill dedicata `tht-evidence-canonicalize` (o un comando slash nel gate) descrive
la sessione di manutenzione: il modello legge i sorgenti marcati dal piano, propone i
campi semantici (`kind`, `applies_to`, `concepts` con sinonimi, `tables` con ruolo,
`anchors`) e li invia al `reviewer_evidence_batch`.
- Il gate è l'unico canale di review; il modello non scrive mai direttamente
`artifacts/evidence/`.
### 9. `harness/.pi/skills/tht-sessione/SKILL.md` (modifica)
- Aggiornare i punti F1/F4/F6-F7 dove il modello legge evidence: spiegare che l'evidence
canonica espone `kind`/`applies_to`/`anchors`, che gli `anchors` sono la fonte primaria
per il value-grounding in F4, e che le sezioni canoniche vanno citate per destinazione.
- Aggiungere il nuovo tool `reviewer_evidence_batch` alla lista dei tool disponibili.
## Migrazione del corpus (incrementale, v1+v2 convivono)
- `CanonicalEvidence` (v2) convive con `EvidenceDoc` (v1): `lint` segnala i non-canonici
ma nulla si rompe; `load_evidence_dir` carica entrambi.
- Convertire a lotti, partendo da `20-valori-enum` e `10-domini-clinici` (impatto
maggiore su F1/F4, anchors più ricchi).
- Il canonizzato derivato in `artifacts/evidence/` sostituisce progressivamente il file
sorgente mirrorato; finché un sorgente non è canonizzato, `extract` continua a
rispecchiare il v1 come oggi.
## Ordine di implementazione
1. `CanonicalEvidence` + `lint` (TDD, nessun LLM).
2. `canonicalize.py` (plan/apply/diff) + comandi CLI (TDD).
3. `records.py` + filtro `applies_to` in `search` + proiezione pack (TDD).
4. Gate `reviewer_evidence_batch` + skill manutenzione Pi.
5. Aggiornamento `SKILL.md` tht-sessione.
6. Conversione primo lotto (`enum` + `domain`) con review batch.
## Verifica
- `cd harness && .venv/bin/pytest -q` — test nuovi per model/lint/canonicalize/records/search.
- `node --test` sul gate per `reviewer_evidence_batch` e anti-bypass.
- Su un campione del corpus: `tht evidence lint` riporta i file rotti (frontmatter `--`,
bullet Unicode) senza crash; `tht evidence canonicalize plan --json` produce piano
corretto; `apply` con fingerprint invariato è no-op.
- `tht search pack "<domanda ablazione>"` mostra evidence con `kind`/sezione pertinente e
gli anchor presenti; `tht search find --kind evidence --applies-to schema_linking`
restituisce solo evidence pertinenti.
- Live: una sessione F4 su psd usa un anchor canonico per il value-grounding invece del
solo LSH (verificabile dal `reviewer_decide` con opzioni `value_grounded` provenienti
dall'evidence, non dal ranking LSH).
- `git diff --check` e typecheck/ruff puliti.