diff --git a/harness/.pi/skills/tht-sessione/SKILL.md b/harness/.pi/skills/tht-sessione/SKILL.md new file mode 100644 index 00000000..367b4927 --- /dev/null +++ b/harness/.pi/skills/tht-sessione/SKILL.md @@ -0,0 +1,169 @@ +--- +name: tht-sessione +description: Orchestratore del workflow Thoth NL->SQL, fasi 1-8 (chiarimento domanda, memorie, riscrittura, schema linking, sintesi, piano CTE, SQL finale, datamart dbt). Usare quando si lavora una domanda in linguaggio naturale dentro una sessione Thoth. +--- + +# Workflow sessione Thoth (fasi 1-8) + +Sei l'orchestratore di un workflow **human-in-the-middle**: tu proponi, il reviewer +decide, la CLI `tht` persiste. **NON sei in modalità autonoma.** Una domanda alla +volta al reviewer; attendi la sua risposta prima di proseguire; MAI promuovere, +escludere, correggere o applicare alcunché senza conferma esplicita. + +Il reviewer risponde tramite i **widget** del gate (costruiti da `tht-gate.js`): +`reviewer_select` (scelta singola), `reviewer_decide` (multiselect con payload +decisione), `reviewer_confirm` (gate su artefatto). Il testo libero arriva via +"Altro" o prefissando `!` nella chat. Le invarianti (Altro sempre presente, +no-limbo, nessuno step senza esito) sono garantite dal gate. + +## Discipline (valgono in ogni fase) + +1. **Un fatto, un comando `tht`.** Ogni azione che modifica lo stato passa da un + singolo comando `tht` (mai `tht phase advance` o `tht decision add` da shell — + sono bloccati dal gate anti-bypass). Esegui un comando alla volta e leggi + l'output prima del prossimo. +2. **Una domanda alla volta.** Un widget = una domanda. Non accumulare. +3. **"Accetta la proposta" sempre fra le opzioni.** Quando proponi qualcosa, la + prima opzione è la tua proposta raccomandata (`recommended:true`); "Altro" c'è + sempre per chiedere modifiche. Mai forzare. +4. **Nessuno step in limbo.** Ogni widget ha un esito (scelta / Altro / Torna + indietro / Esci). Se il reviewer chiude senza scegliere, il gate ripropone. +5. **Artefatto = output di prima classe.** `schema_linking.json`, `cte_plan.json`, + `ctes/*.sql`, `sql_final.sql` sono prodotti e revisionati esplicitamente, non + nascosti nel ragionamento. +6. **Free-text (D13).** Quando il reviewer usa "Altro" con testo libero, **valuta + il testo in contesto, agisci, ri-chiedi se ambiguo** (non defaultare al silenzio + o alla prima opzione). Registra il testo del reviewer nel `rationale` della + decisione, verbatim. +7. **Rollback (D15).** Dopo `/torna N` o "Torna indietro", riprendi dalla Fase N + **rivedendo gli artefatti esistenti**; `tht phase reopen` cancella gli artefatti + oltre il target. NON ri-eseguire comandi `tht` per artefatti ancora validi. + +## Fase 1 — Chiarimento + +Prerequisito: devi essere già in Fase 1. + +1. Esplora il DWH e la knowledge base con `tht search ""` (evidence + + schema) e `tht search --kind evidence ""`. L'LSH espone tutte le + colonne dove un valore compare (non collassa al miglior match). +2. Per ogni ambiguità (termine clinico, popolazione, periodo, outcome),presenta + un `reviewer_select` con le interpretazioni candidate (`recommended:true` sulla + migliore) + "Altro". Registra `concept_clarified` per ogni chiarimento. +3. Quando la domanda è univoca, chiudi con `reviewer_confirm kind:"phase"`. +4. Dopo l'avanzamento, aggiorna la domanda con il tool `rewrite_question` del gate + (scrive `question.md` deterministicamente). + +## Fase 2 — Memorie + +Prerequisito: Fase 1 completata. + +1. Cerca memorie riutilizzabili: `tht memory search "" --session + --json`. **Passa SEMPRE `--session `**: la CLI esclude le memorie già decise + in questa sessione (così non riproponi ciò che il reviewer ha scartato). +2. L'hit arriva con metadata completo (subject/detail/rationale): leggi cosa dice, + da dove viene, perché potrebbe valere qui, rischio fuori-contesto. +3. Presenta in **un solo** `reviewer_decide(multi:true, advance:true, + allow_empty:true)` le candidate (max **5**, solo tipi riusabili: + `concept_clarified`, `table_promoted`, `table_excluded`). Ogni opzione porta + `mem_id` + `type`/`subject`/`rationale`. Le selezionate si applicano + (registra la decisione citando il mem_id nel rationale), le deselezionate si + registrano come `memory_rejected` (così non riappaiono). Lista pre-selezionata + con le raccomandate. +4. Se nessuna memoria supera score 0.5, chiudi rapidamente con + `reviewer_confirm kind:"phase"`. +5. **Promozione (D11).** Per promuovere UNA memoria in `profile=workstation`, usa + `tht memory save-one` (upsert mirato via writer key). La promozione batch resta + server-side. + +## Fase 3 — Riscrittura + +Prerequisito: Fase 2 completata; la decisione `question_rewritten` è rifiutata +prima di Fase 3 (exit 5 dalla CLI). + +1. Leggi `rewriting.md`. Produci la domanda riscritta (popolazione esplicita nei + termini del modello, ogni condizione come clausola numerabile, termini ambigui + sostituiti con i concetti chiariti in Fase 1 citando l'evidence, output atteso + esplicitato). +2. Presenta in **un solo** `reviewer_decide(advance:true, allow_other:true)`. + Opzione "Conferma riscrittura" `recommended:true` con + `{type:"question_rewritten", subject:"domanda", detail:""}`. +3. **Ordine (conta):** (a) `reviewer_decide(advance:true)` registra `question_rewritten` + e avanza → (b) il tool `rewrite_question` del gate chiama `tht session set-question` + che scrive `question.md` (rigenera domanda + sezione "## Assunzioni"). Niente + edit/write manuale di `question.md`. +4. "Altro" itera (riproponi un nuovo `reviewer_decide`). "Torna indietro" riapre F1. + +## Fase 4 — Schema linking + +Prerequisito: Fase 3 completata. + +1. `tht schema introspect` + `tht schema render --format mschema-text` per il + contesto schema. +2. Proponi tabelle/colonne/join in `reviewer_decide(advance:true)`. Registra + `table_promoted`/`table_excluded`/`column_corrected`/`join_modified`. +3. **Value grounding (D14a).** Se un valore citato (es. "ablazione") matcha + **più colonne** (flag + testo patologia), presenta un `reviewer_decide` con + opzioni `value_grounded` per ogni colonna candidata (l'LSH le espone tutte, + non collassa al miglior match). Il reviewer sceglie l'ancora. +4. **Formula di concetto (D14b).** Se un concetto (es. "fascia pediatrica", + "stesso anno") ha una formula SQL candidata (ricerca nelle evidence o derivata + dal contesto), presentala e il reviewer approva/rifiuta + (`concept_formula_approved`/`rejected`). +5. Scrivi `schema_linking.json` (l'artefatto di Fase 4) e chiudi con + `reviewer_confirm kind:"phase"`. Non eseguire `tht session check` (è Fase 5). + +## Fase 5 — Sintesi + +Prerequisito: Fase 4 completata; `schema_linking.json` presente. + +1. `tht session check` (gate oggettivo: decisioni + schema_linking valido). +2. Riassumi lo schema-linking al reviewer; se servono correzioni, riapri Fase 4. +3. **Promozione memorie (F5).** Se vuoi promuovere memorie da questa sessione, usa + `tht memory save-one` (workstation) o `tht memory promote` (server). Le memory + vivono SOLO nel vectordb (niente registry locale). +4. Chiudi con `reviewer_confirm kind:"phase"`. + +## Fase 6 — Piano CTE + +Prerequisito: Fase 5 completata. + +1. Leggi `cte.md`. Scomponi la domanda riscritta in CTE (Agent View Generation): + ogni CTE cattura un sottoinsieme informativo con scopo chiaro, in snake_case. +2. Presenta il piano CTE al reviewer (`reviewer_decide` con il piano completo). +3. Per ogni CTE (nell'ordine del piano): scrivi `sessions//ctes/.sql` + (SOLO `WITH ... AS (...)`, niente SELECT finale), testa con + `tht cte test --session `, presenta il risultato in + `reviewer_confirm kind:"cte_result"`. Il CTE successivo è testabile SOLO dopo + che il precedente è approvato (exit 5 se fuori ordine). Copia ESATTAMENTE i + nomi di tabelle/colonne dal contesto schema; usa valori verificati con `tht search`. +4. Dopo l'ultimo CTE approvato, chiudi con `reviewer_confirm kind:"phase"`. + +## Fase 7 — SQL finale + +Prerequisito: Fase 6 completata. + +1. Leggi `sql-generation.md`. Divide-and-conquer ricorsivo: i CTE approvati in + Fase 6 sono i mattoni preferenziali (riusali per nome). +2. Componi il SQL finale (dialetto PostgreSQL, nomi esatti dal contesto schema). + **Dimensione tempo:** `data_time_key` è la FK verso `dim_time.day_key` (NON + dichiarata nel DWH, va aggiunta a mano nel join); usa `JOIN dim_time` e le sue + colonne (`dt.year`, `dt.month`, ...), MAI aritmetica sulla chiave. +3. `tht sql validate` + `tht sql preview` (max 10 righe). Su errori/risultati + sospetti, applica la checklist di `sql-generation.md` e correggi col reviewer. +4. `tht sql save` scrive `sessions//sql_final.sql` (SOLO SQL pulito, niente + commenti). Chiudi con `reviewer_confirm kind:"sql"`. + +## Fase 8 — Datamart + +Prerequisito: Fase 7 completata. + +1. Chiedi al reviewer se vuole un datamart (`reviewer_select` sì/no). +2. Se sì: `tht datamart generate` (stub — alza NotImplementedError per ora). + Comunica al reviewer che la generazione dbt non è ancora implementata. +3. Se no: chiudi con `reviewer_confirm kind:"phase"`. La sessione è finalizzabile. + +## Fine sessione + +Quando il workflow è completo (Fase 8), `tht session finalize` chiude la sessione +e sblocca l'input. Lo stato persistito (ledger `review_decisions.jsonl` + artefatti) +è la verità: ciò che non è registrato non è avvenuto. diff --git a/harness/.pi/skills/tht-sessione/cte.md b/harness/.pi/skills/tht-sessione/cte.md new file mode 100644 index 00000000..f3d13302 --- /dev/null +++ b/harness/.pi/skills/tht-sessione/cte.md @@ -0,0 +1,39 @@ +# Tecnica di generazione del piano CTE (Agent View Generation) + +Adattata dallo step CTE di AV-SQL. I CTE catturano incrementalmente +l'informazione necessaria a rispondere alla domanda riscritta, SENZA +rispondere alla domanda finale (quella e' la fase successiva). + +Regole (dalla disciplina AV-SQL, valgono alla lettera): + +1. Copia ESATTAMENTE i nomi di tabelle e colonne dal contesto schema fornito + (`tht schema render --format mschema-text --table ...`). Mai inventare + oggetti non presenti. +2. Tieni sempre le chiavi (PK e colonne di join) nei CTE: serviranno dopo. +3. Meglio una colonna in piu' che una in meno: se non sei sicuro, includila. +4. Un CTE = un sottoinsieme informativo con uno scopo chiaro (es. "ricoveri + con ablazione nel 2025"), nominato in snake_case parlante. +5. I CTE possono referenziarsi in catena; l'ultimo del file e' quello che + `tht cte test` interroghera'. +6. Ogni file in sessions//ctes/.sql contiene SOLO il blocco + `WITH ... AS (...)` (anche multi-CTE), SENZA SELECT finale. Una riga + `SELECT ...` dopo il blocco WITH causa un errore in `tht cte test`: non + aggiungerla mai. I CTE vengono testati **e approvati (decisione + `cte_approved`) nell'ordine del piano**: il CTE successivo è testabile + SOLO dopo che il precedente è stato approvato con `kind:"cte_result"`. La + CLI rifiuta i CTE fuori ordine (exit 5). +7. Filtri: usa i valori di campo verificati con `tht search` (match LSH sui + valori reali), non valori immaginati. + +Presentazione al reviewer, per ogni CTE del piano: + +> **** — scopo: +> Tabelle usate: (tutte dal perimetro promosso) +> ```sql +> WITH AS (...) +> ``` + +Dopo la conferma (o per revisione diretta del reviewer) scrivi il file e +testa: `tht cte test --session `. Su errore o warning rilevanti, +discuti la correzione col reviewer prima di riscrivere il file. Se il +reviewer modifica il file a mano, RILEGGILO prima di ritestare. diff --git a/harness/.pi/skills/tht-sessione/memoria.md b/harness/.pi/skills/tht-sessione/memoria.md new file mode 100644 index 00000000..1cd7d9b2 --- /dev/null +++ b/harness/.pi/skills/tht-sessione/memoria.md @@ -0,0 +1,40 @@ +# Presentazione delle memorie riapplicabili + +Le memorie arrivano da `tht memory search "" --session --json`, +gia' ordinate per similarita'. Tu le argomenti, il reviewer decide. MAI +applicarle da solo. **Passa SEMPRE `--session `**: la CLI esclude le memorie +gia' decise in questa sessione (applicate o rifiutate), cosi' non riproponi cio' +che il reviewer ha gia' scartato (anche dopo un reopen della Fase 2). + +Per ogni memoria da presentare in checklist, includi nella `label` e/o +`description` dell'opzione: + +- **Cosa dice**: tipo + soggetto + detail (es. "table_promoted: + fact_seeablazione — tabella principale per le ablazioni"). +- **Da dove viene**: question_context e session_id di origine. +- **Perche' potrebbe valere qui**: sovrapposizione di concetti/tabelle con la + domanda corrente (campi tables/concepts), score di similarita'. +- **Rischio fuori-contesto**: cosa c'era nella sessione originale che qui + potrebbe non valere (periodo diverso, popolazione diversa, schema cambiato). + +Regole: + +- Proponi al massimo **5** candidate. Includi SOLO memorie dei 3 tipi + riusabili: `concept_clarified`, `table_promoted`, `table_excluded`. Le + decisioni query-specifiche (es. `question_rewritten`, `sql_approved`) NON + vanno proposte: non si trasferiscono ad altre domande. +- Tutte le memorie candidate vanno in **un solo** `reviewer_decide(multi:true, + advance:true, allow_empty:true)`: ogni opzione selezionata viene applicata + (registra la decisione col tipo appropriato, citando l'id memoria nel + rationale), ogni opzione deselezionata viene **registrata come `memory_rejected` + dal gate** (così la prossima `tht memory search --session` non la ripropone). + Per abilitare questo, OGNI opzione memoria DEVE portare il campo + `mem_id:"mem-"` (oltre a `type`/`subject`/`rationale`). La checklist parte + pre-selezionata con le memorie raccomandate. Con `allow_empty:true` una + selezione **vuota è accettata** (nessuna memoria applicata; le deselezionate + restano comunque registrate come rifiutate) e la fase avanza — nessun gate + separato. +- Per "ispeziona": mostra il record JSON integrale della memoria nella prosa + prima di presentare la checklist, se il reviewer lo richiede. +- Se nessuna memoria supera score 0.5, dillo e chiudi la fase rapidamente + (con `reviewer_confirm kind:"phase"` se la lista è vuota). diff --git a/harness/.pi/skills/tht-sessione/rewriting.md b/harness/.pi/skills/tht-sessione/rewriting.md new file mode 100644 index 00000000..adaf888f --- /dev/null +++ b/harness/.pi/skills/tht-sessione/rewriting.md @@ -0,0 +1,25 @@ +# Tecnica di riscrittura della domanda + +Adattata dallo step "rephrase question" di AV-SQL: scomporre la domanda in +condizioni esplicite, senza perdere informazione. + +La domanda riscritta deve: + +1. Esplicitare la popolazione (chi/cosa si conta o seleziona) con i termini + del modello dati (es. "pazienti distinti in dim_patient"). +2. Esplicitare ogni condizione come clausola separata e numerabile: + periodo temporale, eventi inclusi/esclusi, soglie, stati. +3. Sostituire i termini ambigui con i concetti chiariti in Fase 1, citando + l'evidence che li definisce quando esiste (id evidence tra parentesi). +4. Esplicitare l'output atteso: conteggio, elenco, aggregato, trend. +5. Restare in italiano, in una forma che un secondo reviewer capirebbe senza + aver visto la conversazione. + +Formato della proposta al reviewer: + +> **Domanda riscritta:** +> **Condizioni:** 1) ... 2) ... 3) ... +> **Assunzioni usate:** +> **Output atteso:** + +Itera sulla base delle correzioni. Non procedere alla Fase 4 senza conferma. diff --git a/harness/.pi/skills/tht-sessione/sql-generation.md b/harness/.pi/skills/tht-sessione/sql-generation.md new file mode 100644 index 00000000..1464240f --- /dev/null +++ b/harness/.pi/skills/tht-sessione/sql-generation.md @@ -0,0 +1,53 @@ +# Tecnica di generazione del SQL finale + +Adattata dallo step "query generation" di AV-SQL (divide-and-conquer ricorsivo) +e dalla sua checklist di revisione. + +## Generazione (divide-and-conquer) + +1. **Dividi**: scomponi la domanda riscritta in sotto-domande, ognuna mirata a + un pezzo di informazione o logica (una popolazione, un filtro, un aggregato). +2. **Conquista**: per ogni sotto-domanda formula uno pseudo-SQL, con segnaposto + per le sotto-domande non ancora risolte. I CTE gia' testati in Fase 6 sono i + mattoni preferenziali: riusali per nome, col loro esito noto. +3. **Ricombina**: sostituisci i segnaposto dal basso verso l'alto fino al SQL + completo. Il SQL finale puo' includere i CTE nel proprio WITH. +4. Dialetto: PostgreSQL. Copia ESATTAMENTE i nomi di tabelle e colonne dal + contesto schema; mai inventare oggetti. + +Il file `sessions//sql_final.sql` deve contenere SOLO il SQL, pulito e +copiabile: niente commenti di razionale (quello vive negli artefatti di audit). + +## Dimensione tempo (analisi per anno/mese/trimestre) + +Le fact table hanno `data_time_key` (`integer`, formato `YYYYMMDD`): è la FK +verso `dim_time.day_key`. **Questa FK non è dichiarata** nel DWH (le fact hanno +`foreign_keys: []`), quindi non comparirà in `schema_linking.json`: vai aggiunta +a mano nel join. + +- Per estrarre anno, mese, trimestre, semestre ecc. fai + `JOIN dim_time dt ON dt.day_key = .data_time_key` e usa le colonne della + dimensione: `dt.year`, `dt.month`, `dt.quarter`, `dt.semester`, `dt.full_date`, + `dt.month_name_it`, `dt.year_month`. +- **Non** fare aritmetica sulla chiave (es. `data_time_key / 10000` per l'anno): + funziona per caso ma è fragile e si rompe appena serve formattare una data o + fare cast. Usa sempre `dim_time`. +- "Ultimi N anni dall'anno più recente": `dt.year >= (SELECT MAX(year) FROM + dim_time WHERE day_key IN (SELECT data_time_key FROM )) - (N-1)`, oppure + calcola il max anno sulle righe effettivamente presenti nella fact. + +## Checklist di revisione (su errori o risultati sospetti) + +- Le colonne restituite rispondono esattamente alla domanda? +- I filtri (WHERE/HAVING) riflettono tutte le condizioni della domanda riscritta? +- Aggregazioni, raggruppamenti e ordinamenti sono quelli richiesti? +- Risultato vuoto o zero: quasi sempre indica un problema in condizioni o join. + Verifica i valori dei filtri con `tht search ""` (match sui valori reali). +- I join seguono quelli promossi in schema_linking.json? (eccezione: la FK + `data_time_key → dim_time.day_key` non è dichiarata, vedi sezione tempo.) +- Analisi temporali: stai usando `JOIN dim_time` e non aritmetica sulla chiave? + +Ogni revisione sostanziale va registrata con: +`reviewer_decide(options:[{label:"Registra revisione", type:"sql_revised", +subject:"sql_final", detail:"", rationale:""}], +allow_other:false)`.