From 292048f777648b4469bf54aecb342279a0876615 Mon Sep 17 00:00:00 2001 From: mptyl Date: Sat, 27 Jun 2026 14:50:30 +0200 Subject: [PATCH] feat(harness): language workspace param + skill riscritta in inglese (semantica completa) Due cambiamenti interconnessi da user review: 1. language come parametro workspace (spec decisione 9): - Config.language (default 'en') + workspaces PSD con 'language: it' - Generalizza Thoth oltre l'italiano: descrizioni tabelle/colonne ed evidence sono nel workspace language; le istruzioni della skill restano in inglese (piu' affidabili per modelli piccoli, meno ambigue) 2. Skill riscritta in INGLESE preservando la semantica COMPLETA dell'originale (autocritica: la mia riscrittura precedente aveva perso ~10 vincoli precisi): - 'promuovere' ambiguo (3 accezioni: phase advance / recommend / memory promote) -> 'never advance a phase or record a decision without confirmation' - recuperati vincoli persi: choice-is-confirmation (no reviewer_confirm dopo reviewer_decide), reviewer_select SOLO per iterazione no-decision, messaggi auto-contenuti obbligatori, artefatto = superficie di decisione (gate rilegge da disco per CTE/SQL), candidati con provenienza+score non verita', opzione 'leave ambiguity open', F1 passa lista completa non solo ultima - language contract esplicito (istruzioni EN, output nel workspace language) Sottomoduli cte/memoria/rewriting/sql-generation in inglese, semantica tecnica intatta (regole AV-SQL, dim_time trick, max 5 memorie solo 3 tipi riusabili). Verifica: 0 residui nsp/chirone, tutti i tht citati registrati, 165 passed. --- ...li-port-completo-skill-riscritta-design.md | 3 +- harness/.pi/skills/tht-sessione/SKILL.md | 317 ++++++++++-------- harness/.pi/skills/tht-sessione/cte.md | 61 ++-- harness/.pi/skills/tht-sessione/memoria.md | 69 ++-- harness/.pi/skills/tht-sessione/rewriting.md | 38 +-- .../.pi/skills/tht-sessione/sql-generation.md | 83 +++-- harness/tht/config.py | 4 + harness/workspaces/tht-test.yaml | 2 + harness/workspaces/tht.example.yaml | 2 + 9 files changed, 317 insertions(+), 262 deletions(-) diff --git a/docs/superpowers/specs/2026-06-27-cli-port-completo-skill-riscritta-design.md b/docs/superpowers/specs/2026-06-27-cli-port-completo-skill-riscritta-design.md index d0c6b603..7ef805af 100644 --- a/docs/superpowers/specs/2026-06-27-cli-port-completo-skill-riscritta-design.md +++ b/docs/superpowers/specs/2026-06-27-cli-port-completo-skill-riscritta-design.md @@ -18,7 +18,8 @@ 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-`, 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. **`language` come parametro del workspace.** Il workspace dichiara la lingua in cui sono scritte le descrizioni di tabelle/colonne e le evidence (es. `language: it` per PSD). La skill lo legge e lo usa per: (a) interpretare i match LSH/evidence nel linguaggio giusto, (b) indirizzare il reviewer in quella lingua. Le **istruzioni della skill restano in inglese** (più affidabili per i modelli, meno ambigue); solo contenuto e output seguono il `language`. Default `en`. Rende Thoth non legato all'italiano (generalizzazione per altri clienti/domini). +10. **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. diff --git a/harness/.pi/skills/tht-sessione/SKILL.md b/harness/.pi/skills/tht-sessione/SKILL.md index 367b4927..4f1502cd 100644 --- a/harness/.pi/skills/tht-sessione/SKILL.md +++ b/harness/.pi/skills/tht-sessione/SKILL.md @@ -1,169 +1,218 @@ --- 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. +description: Orchestrator of the Thoth NL->SQL workflow, phases 1-8 (question clarification, memories, rewriting, schema linking, synthesis, CTE plan, final SQL, datamart). Use when working a natural-language question inside a Thoth session. --- -# Workflow sessione Thoth (fasi 1-8) +# Thoth session workflow (phases 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. +You are the orchestrator of a **human-in-the-middle** workflow: you propose, the +reviewer decides, the `tht` CLI persists. **You are NEVER in autonomous mode.** +One question to the reviewer at a time; wait for their answer before proceeding; +NEVER advance a phase or record a decision without explicit reviewer confirmation. -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. +The reviewer answers via the gate's **widgets** (built by `tht-gate.js`): +`reviewer_select` (single pick, no decision recorded), `reviewer_decide` +(multiselect, each selected option IS a decision — the choice is the confirmation), +`reviewer_confirm` (gate on an artifact / phase transition). Free text arrives via +the "Altro/Other" option or by prefixing `!` in chat. -## Discipline (valgono in ogni fase) +**Language contract (from the workspace `language` field):** the table/column +descriptions and the evidence you read are written in the workspace language (e.g. +`it` for PSD). **These instructions are in English; your output to the reviewer and +your interpretation of domain terms follow the workspace language.** When in doubt +about a domain term, ask the reviewer. -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. +## Disciplines (hold in every phase) -## Fase 1 — Chiarimento +1. **One fact, one `tht` command.** Every state change goes through a single `tht` + command (you invoke `tht ...` via the shell tool). NEVER run `tht phase advance` + or `tht decision add` from the shell — they are blocked by the gate's anti-bypass + hook; the gate extension records every decision via the `reviewer_*` tools. +2. **The choice is the confirmation.** For `reviewer_decide`, do NOT add a separate + `reviewer_confirm` after — each selected option already records its decision and + (if `advance:true`) advances. Add a `reviewer_confirm kind:"phase"` ONLY where the + phase genuinely needs a deliberate gate (F1 close, F5 close, last CTE + `kind:"cte_result"`, final SQL `kind:"sql"`, F8 close) — never as a redundant + echo of a `reviewer_decide`. +3. **`reviewer_select` is for iteration only.** Use it when you propose options and + want the reviewer to pick / refine before any decision is recorded (e.g. iterating + a clarification before confirming). It records NO decision. Never use it for a + substantive decision — that's `reviewer_decide`. +4. **"Accept the proposal" is always an option.** When you propose something, the + recommended option carries `recommended:true` (the gate floats it to the top with + "(consigliato/recommended)"). "Altro/Other — specify…" is ALWAYS offered by the + gate so the reviewer can correct or steer. Never force your recommendation. +5. **No step in limbo.** Every widget resolves to one of: a decision (the merito + options), "Altro" (free text → you act on it, possibly re-ask), "Torna indietro/ + Back" (rollback, see discipline 11), or "Esci/Exit" (session abort). If the + reviewer closes without choosing, the gate re-presents the same widget — there is + no silent skip. +6. **Self-contained messages.** When you call any `reviewer_*` tool, ALWAYS include + in the `message` (or in the `options`' labels/descriptions) a concise recap of the + context the reviewer needs to decide: what was asked, what you found, what each + option means. The reviewer does not see your internal reasoning — only the widget. +7. **Artifact = first-class output.** `schema_linking.json`, `cte_plan.json`, + `ctes/*.sql`, `sql_final.sql` are produced and reviewed explicitly, never hidden. + For CTE (`kind:"cte_result"`) and final SQL (`kind:"sql"`) the gate **reads the + file from disk and shows it integral** to the reviewer — so the file content is + what the reviewer approves. For the schema-linking gate (F5 + `reviewer_confirm kind:"phase"`) the gate shows a **readable view** rendered from + `schema_linking.json`. Write the artifacts with care; they are the decision surface. +8. **Candidates are candidates, not truth.** Present LSH/vector/evidence matches with + their **provenance** (LSH / vector / evidence) and their scores, never as absolute + truth. The reviewer may reject them. Verify filter values with `tht search + ""` (real-value match) before baking them into SQL. +9. **Open ambiguities are explicit.** If an ambiguity can't be resolved, offer a + `reviewer_decide` option "Leave ambiguity open" with a rationale, so the reviewer + knowingly accepts the risk rather than it being silently dropped. +10. **Free-text (D13).** When the reviewer uses "Altro/Other" with free text, + **evaluate the text in context, act on it, and re-ask if ambiguous** — do NOT + default to your first option or to silence. Record the reviewer's words verbatim + in the decision `rationale`. +11. **Rollback (D15).** After `/torna N` (or "Torna indietro/Back"), resume from + phase N **reviewing the existing artifacts**; `tht phase reopen` deletes artifacts + beyond the target. Do NOT re-run `tht` commands for artifacts that are still valid. -Prerequisito: devi essere già in Fase 1. +## Phase 1 — Clarification -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). +Prerequisite: you must already be in Phase 1. -## Fase 2 — Memorie +1. Explore the DWH and knowledge base: `tht search ""` (evidence + schema, LSH + over real values) and `tht search --kind evidence ""`. The LSH exposes + EVERY column where a value appears — it does not collapse to a single best match, + so a value like "ablazione" may anchor on multiple columns. +2. For each ambiguity (clinical term, population, time window, outcome), present a + `reviewer_select` with the candidate interpretations (`recommended:true` on the + best) + "Altro". When a clarification is settled, move on. Pass the FULL list of + clarifications, not only the latest, when you close. +3. To close Phase 1: `reviewer_confirm kind:"phase"` (the deliberate "I'm done + clarifying" gate). Do NOT add a separate `reviewer_confirm` after each individual + clarification — those advance via `reviewer_decide` (`concept_clarified`), not via + phase gates. +4. After the phase advance, update the question with the gate's `rewrite_question` + tool (it calls `tht session set-question`, which writes `question.md` + deterministically — never edit `question.md` by hand). -Prerequisito: Fase 1 completata. +## Phase 2 — Memories -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. +Prerequisite: Phase 1 closed. -## Fase 3 — Riscrittura +1. Search reusable memories: `tht memory search "" --session --json`. + **ALWAYS pass `--session `**: the CLI excludes memories already decided in + this session (so you don't re-propose what the reviewer already rejected — even + after a Phase 2 reopen). +2. The hit comes with full metadata (subject/detail/rationale): read what it says, + where it comes from, why it might apply here, the out-of-context risk. +3. Present candidates in **a single** `reviewer_decide(multi:true, advance:true, + allow_empty:true)`. Rules: at most **5** candidates; ONLY the 3 reusable types + (`concept_clarified`, `table_promoted`, `table_excluded`) — query-specific + decisions (`question_rewritten`, `sql_approved`, …) are NOT transferable, never + propose them. Each option carries `mem_id:"mem-"` plus `type`/`subject`/ + `rationale`. Selected options are applied (register the decision citing the + `mem_id` in the rationale); deselected ones are recorded as `memory_rejected` by + the gate (so the next `tht memory search --session` won't re-propose them). The + checklist starts pre-selected with the recommended memories. With `allow_empty:true` + an empty selection is accepted (no memory applied; deselected still recorded) and + the phase advances — no separate gate. +4. If no memory clears score 0.5, say so and close the phase quickly + (`reviewer_confirm kind:"phase"` if the list is empty). +5. **Promotion (D11).** To promote ONE memory in `profile=workstation`, use + `tht memory save-one` (targeted one-row upsert via the writer key). Batch + promotion (`tht memory promote`) is server-side. Memories live ONLY in the + vectordb (no local registry). -Prerequisito: Fase 2 completata; la decisione `question_rewritten` è rifiutata -prima di Fase 3 (exit 5 dalla CLI). +## Phase 3 — Rewriting -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. +Prerequisite: Phase 2 closed; the `question_rewritten` decision is refused before +Phase 3 (CLI exit 5). -## Fase 4 — Schema linking +1. Read `rewriting.md`. Produce the rewritten question (population explicit in model + terms, each condition as a separate numbered clause, ambiguous terms replaced with + the concepts clarified in Phase 1 citing the defining evidence, expected output + made explicit). +2. Present in **a single** `reviewer_decide(advance:true, allow_other:true)`. The + "Confirm rewriting" option is `recommended:true` with + `{type:"question_rewritten", subject:"domanda", detail:""}`. + Do NOT add a `reviewer_confirm kind:"phase"` after — the `reviewer_decide` already + advances. +3. **Order matters:** (a) `reviewer_decide(advance:true)` records `question_rewritten` + and advances → (b) the gate's `rewrite_question` tool calls `tht session + set-question` which writes `question.md` (regenerates question + an "## Assunzioni" + section). Never edit/write `question.md` manually. +4. "Altro" iterates (re-propose a new `reviewer_decide`). "Torna indietro" reopens F1. -Prerequisito: Fase 3 completata. +## Phase 4 — Schema linking -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 +Prerequisite: Phase 3 closed. + +1. `tht schema introspect` + `tht schema render --format mschema-text` for the schema + context. Copy table/column names EXACTLY from it — never invent objects. +2. Propose tables/columns/joins in `reviewer_decide(advance:true)`. Register `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). +3. **Value grounding (D14a).** If a cited value (e.g. "ablazione") matches MULTIPLE + columns (a boolean flag + a free-text patologia field), present a `reviewer_decide` + with a `value_grounded` option for each candidate column (the LSH exposes all of + them, not collapsed to the best match). The reviewer chooses the anchor(s). +4. **Concept formula (D14b).** If a concept (e.g. "fascia pediatrica", "stesso anno") + has a candidate SQL formula (found in the evidence or derived from context), + present it and let the reviewer approve/reject + (`concept_formula_approved`/`concept_formula_rejected`). +5. Write `schema_linking.json` (the Phase 4 artifact) and close with + `reviewer_confirm kind:"phase"`. Do NOT run `tht session check` (that's Phase 5). -## Fase 5 — Sintesi +## Phase 5 — Synthesis -Prerequisito: Fase 4 completata; `schema_linking.json` presente. +Prerequisite: Phase 4 closed; `schema_linking.json` present. -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"`. +1. `tht session check` (objective gate: decisions present + schema_linking valid). +2. Summarize the schema-linking to the reviewer; if corrections are needed, reopen + Phase 4. +3. **Memory promotion (F5).** If you want to promote memories from this session, use + `tht memory save-one` (workstation) or `tht memory promote` (server). Memories + live ONLY in the vectordb (no local registry). +4. Close with `reviewer_confirm kind:"phase"`. -## Fase 6 — Piano CTE +## Phase 6 — CTE plan -Prerequisito: Fase 5 completata. +Prerequisite: Phase 5 closed. -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"`. +1. Read `cte.md`. Decompose the rewritten question into CTEs (Agent View Generation): + each CTE captures an informative subset with a clear purpose, named in snake_case. +2. Present the full CTE plan to the reviewer (`reviewer_decide` with the plan). +3. For each CTE (in plan order): write `sessions//ctes/.sql` (ONLY the + `WITH ... AS (...)` block, NO trailing SELECT), test with `tht cte test --session + `, present the result in `reviewer_confirm kind:"cte_result"`. The next + CTE is testable ONLY after the previous one is approved (CLI exit 5 if out of + order). Copy table/column names EXACTLY from the schema context; use values + verified with `tht search`. +4. After the last CTE is approved, close with `reviewer_confirm kind:"phase"`. -## Fase 7 — SQL finale +## Phase 7 — Final SQL -Prerequisito: Fase 6 completata. +Prerequisite: Phase 6 closed. -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"`. +1. Read `sql-generation.md`. Recursive divide-and-conquer: the CTEs approved in + Phase 6 are the preferred building blocks (reuse them by name). +2. Compose the final SQL (PostgreSQL dialect, exact names from the schema context). + **Time dimension:** `data_time_key` is the FK to `dim_time.day_key` (NOT declared + in the DWH, must be added by hand to the join); use `JOIN dim_time` and its + columns (`dt.year`, `dt.month`, …), NEVER arithmetic on the key. +3. `tht sql validate` + `tht sql preview` (max 10 rows). On errors / suspicious + results, apply the `sql-generation.md` checklist and correct with the reviewer. +4. `tht sql save` writes `sessions//sql_final.sql` (ONLY clean SQL, no comments). + Close with `reviewer_confirm kind:"sql"`. -## Fase 8 — Datamart +## Phase 8 — Datamart -Prerequisito: Fase 7 completata. +Prerequisite: Phase 7 closed. -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. +1. Ask the reviewer whether they want a datamart (`reviewer_select` yes/no). +2. If yes: `tht datamart generate` (stub — raises NotImplementedError for now). Tell + the reviewer that dbt generation is not implemented yet. +3. If no: close with `reviewer_confirm kind:"phase"`. The session is finalizable. -## Fine sessione +## Session end -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. +When the workflow is complete (Phase 8), `tht session finalize` closes the session +and unlocks input. The persisted state (ledger `review_decisions.jsonl` + artifacts) +is the truth: what is not recorded did not happen. diff --git a/harness/.pi/skills/tht-sessione/cte.md b/harness/.pi/skills/tht-sessione/cte.md index f3d13302..2fb54314 100644 --- a/harness/.pi/skills/tht-sessione/cte.md +++ b/harness/.pi/skills/tht-sessione/cte.md @@ -1,39 +1,38 @@ -# Tecnica di generazione del piano CTE (Agent View Generation) +# CTE plan generation technique (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). +Adapted from the AV-SQL CTE step. CTEs incrementally capture the information needed +to answer the rewritten question, WITHOUT answering the final question (that is the +next phase). -Regole (dalla disciplina AV-SQL, valgono alla lettera): +Rules (from the AV-SQL discipline, hold verbatim): -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. +1. Copy table and column names EXACTLY from the schema context provided + (`tht schema render --format mschema-text --table ...`). Never invent objects + that are not present. +2. Always keep the keys (PK and join columns) in the CTEs: they will be needed later. +3. Better one column too many than one too few: if unsure, include it. +4. One CTE = one informative subset with a clear purpose (e.g. "ricoveri with + ablazione in 2025"), named in a speaking snake_case. +5. CTEs can chain-reference each other; the last one in the file is the one that + `tht cte test` will query. +6. Each file in `sessions//ctes/.sql` contains ONLY the `WITH ... AS (...)` + block (multi-CTE allowed), WITHOUT a trailing SELECT. A `SELECT ...` line after + the WITH block causes an error in `tht cte test`: never add it. CTEs are tested + **and approved (decision `cte_approved`) in plan order**: the next CTE is testable + ONLY after the previous one is approved with `kind:"cte_result"`. The CLI refuses + out-of-order CTEs (exit 5). +7. Filters: use field values verified with `tht search` (LSH match on real values), + not imagined values. -Presentazione al reviewer, per ogni CTE del piano: +Presentation to the reviewer, for each CTE in the plan: -> **** — scopo: -> Tabelle usate: (tutte dal perimetro promosso) +> **** — purpose: +> Tables used: (all from the promoted perimeter) > ```sql -> WITH AS (...) +> 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. +After confirmation (or for direct reviewer review) write the file and test: +`tht cte test --session `. On error or relevant warnings, discuss the +correction with the reviewer before rewriting the file. If the reviewer edits the +file by hand, RE-READ it before re-testing. diff --git a/harness/.pi/skills/tht-sessione/memoria.md b/harness/.pi/skills/tht-sessione/memoria.md index 1cd7d9b2..d47ece28 100644 --- a/harness/.pi/skills/tht-sessione/memoria.md +++ b/harness/.pi/skills/tht-sessione/memoria.md @@ -1,40 +1,39 @@ -# Presentazione delle memorie riapplicabili +# Presenting reusable memories -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). +Memories arrive from `tht memory search "" --session --json`, already +ordered by similarity. You argue them, the reviewer decides. NEVER apply them on +your own. **ALWAYS pass `--session `**: the CLI excludes memories already decided +in this session (applied or rejected), so you don't re-propose what the reviewer has +already discarded (even after a Phase 2 reopen). -Per ogni memoria da presentare in checklist, includi nella `label` e/o -`description` dell'opzione: +For each memory to present in the checklist, include in the option's `label` and/or +`description`: -- **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). +- **What it says**: type + subject + detail (e.g. "table_promoted: + fact_seeablazione — main table for ablazioni"). +- **Where it comes from**: question_context and origin session_id. +- **Why it might apply here**: overlap of concepts/tables with the current question + (fields tables/concepts), similarity score. +- **Out-of-context risk**: what was in the original session that might not hold here + (different period, different population, changed schema). -Regole: +Rules: -- 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). +- Propose at most **5** candidates. Include ONLY memories of the 3 reusable types: + `concept_clarified`, `table_promoted`, `table_excluded`. Query-specific decisions + (e.g. `question_rewritten`, `sql_approved`) are NOT to be proposed: they don't + transfer to other questions. +- All candidate memories go in **a single** `reviewer_decide(multi:true, + advance:true, allow_empty:true)`: each selected option is applied (register the + decision with the appropriate type, citing the memory id in the rationale), each + deselected option is **recorded as `memory_rejected` by the gate** (so the next + `tht memory search --session` won't re-propose it). To enable this, EVERY memory + option MUST carry the field `mem_id:"mem-"` (besides `type`/`subject`/ + `rationale`). The checklist starts pre-selected with the recommended memories. + With `allow_empty:true` an **empty selection is accepted** (no memory applied; the + deselected ones are still recorded as rejected) and the phase advances — no + separate gate. +- For "inspect": show the memory's full JSON record in the prose before presenting + the checklist, if the reviewer asks. +- If no memory clears score 0.5, say so and close the phase quickly (with + `reviewer_confirm kind:"phase"` if the list is empty). diff --git a/harness/.pi/skills/tht-sessione/rewriting.md b/harness/.pi/skills/tht-sessione/rewriting.md index adaf888f..e0df40dc 100644 --- a/harness/.pi/skills/tht-sessione/rewriting.md +++ b/harness/.pi/skills/tht-sessione/rewriting.md @@ -1,25 +1,25 @@ -# Tecnica di riscrittura della domanda +# Question rewriting technique -Adattata dallo step "rephrase question" di AV-SQL: scomporre la domanda in -condizioni esplicite, senza perdere informazione. +Adapted from the "rephrase question" step of AV-SQL: decompose the question into +explicit conditions, without losing information. -La domanda riscritta deve: +The rewritten question must: -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. +1. Make the population explicit (who/what is counted or selected) using the data + model's terms (e.g. "distinct patients in dim_patient"). +2. Make every condition an explicit, numbered clause: time window, included/excluded + events, thresholds, states. +3. Replace ambiguous terms with the concepts clarified in Phase 1, citing the + evidence that defines them when it exists (evidence id in parentheses). +4. Make the expected output explicit: count, list, aggregate, trend. +5. Stay in the workspace language, in a form a second reviewer would understand + without having seen the conversation. -Formato della proposta al reviewer: +Format of the proposal to the reviewer: -> **Domanda riscritta:** -> **Condizioni:** 1) ... 2) ... 3) ... -> **Assunzioni usate:** -> **Output atteso:** +> **Rewritten question:** +> **Conditions:** 1) ... 2) ... 3) ... +> **Assumptions used:** +> **Expected output:** -Itera sulla base delle correzioni. Non procedere alla Fase 4 senza conferma. +Iterate based on corrections. Do not proceed to Phase 4 without confirmation. diff --git a/harness/.pi/skills/tht-sessione/sql-generation.md b/harness/.pi/skills/tht-sessione/sql-generation.md index 1464240f..76d1156e 100644 --- a/harness/.pi/skills/tht-sessione/sql-generation.md +++ b/harness/.pi/skills/tht-sessione/sql-generation.md @@ -1,53 +1,52 @@ -# Tecnica di generazione del SQL finale +# Final SQL generation technique -Adattata dallo step "query generation" di AV-SQL (divide-and-conquer ricorsivo) -e dalla sua checklist di revisione. +Adapted from the "query generation" step of AV-SQL (recursive divide-and-conquer) +and its review checklist. -## Generazione (divide-and-conquer) +## Generation (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. +1. **Divide**: decompose the rewritten question into sub-questions, each aimed at a + piece of information or logic (a population, a filter, an aggregate). +2. **Conquer**: for each sub-question formulate a pseudo-SQL, with placeholders for + sub-questions not yet resolved. The CTEs already tested in Phase 6 are the + preferred building blocks: reuse them by name, with their known outcome. +3. **Recombine**: replace placeholders bottom-up until the full SQL. The final SQL + may include the CTEs in its own WITH. +4. Dialect: PostgreSQL. Copy table and column names EXACTLY from the schema context; + never invent objects. -Il file `sessions//sql_final.sql` deve contenere SOLO il SQL, pulito e -copiabile: niente commenti di razionale (quello vive negli artefatti di audit). +The file `sessions//sql_final.sql` must contain ONLY the SQL, clean and +copy-pasteable: no rationale comments (that lives in the audit artifacts). -## Dimensione tempo (analisi per anno/mese/trimestre) +## Time dimension (analysis by year/month/quarter) -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. +Fact tables have `data_time_key` (`integer`, format `YYYYMMDD`): it is the FK to +`dim_time.day_key`. **This FK is NOT declared** in the DWH (facts have +`foreign_keys: []`), so it will NOT appear in `schema_linking.json`: you must add it +by hand to the 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`, +- To extract year, month, quarter, semester etc. do + `JOIN dim_time dt ON dt.day_key = .data_time_key` and use the dimension's + columns: `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. +- **Do NOT** do arithmetic on the key (e.g. `data_time_key / 10000` for the year): + it works by accident but is fragile and breaks as soon as you need to format a date + or do a cast. Always use `dim_time`. +- "Last N years from the most recent year": + `dt.year >= (SELECT MAX(year) FROM dim_time WHERE day_key IN (SELECT data_time_key FROM )) - (N-1)`, + or compute the max year on the rows actually present in the fact. -## Checklist di revisione (su errori o risultati sospetti) +## Review checklist (on errors or suspicious results) -- 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? +- Do the returned columns answer the question exactly? +- Do the filters (WHERE/HAVING) reflect ALL the conditions of the rewritten question? +- Are aggregations, groupings and orderings the required ones? +- Empty or zero result: almost always indicates a problem in conditions or joins. + Verify the filter values with `tht search ""` (match on real values). +- Do the joins follow those promoted in `schema_linking.json`? (exception: the FK + `data_time_key → dim_time.day_key` is not declared, see the time section.) +- Time analyses: are you using `JOIN dim_time` and not key arithmetic? -Ogni revisione sostanziale va registrata con: -`reviewer_decide(options:[{label:"Registra revisione", type:"sql_revised", -subject:"sql_final", detail:"", rationale:""}], -allow_other:false)`. +Every substantive revision is recorded with: +`reviewer_decide(options:[{label:"Register revision", type:"sql_revised", +subject:"sql_final", detail:"", rationale:""}], allow_other:false)`. diff --git a/harness/tht/config.py b/harness/tht/config.py index 53a7b6f9..2ff845ab 100644 --- a/harness/tht/config.py +++ b/harness/tht/config.py @@ -140,6 +140,10 @@ class Config(BaseModel): # workstation: postazione locale che legge il vectordb via REST; gli upsert remoti # richiedono vector_write_rest, mentre init/clear/rebuild restano solo-server. profile: Literal["server", "workstation"] = "server" + # Language in which table/column descriptions and evidence are written. The skill + # instructions stay in English; only content/output follow this language. Default + # 'en' so Thoth is not bound to any customer's language. + language: str = "en" paths: PathsConfig = PathsConfig() examples: ExamplesConfig = ExamplesConfig() lsh: LshConfig = LshConfig() diff --git a/harness/workspaces/tht-test.yaml b/harness/workspaces/tht-test.yaml index f44483fd..7efb2b48 100644 --- a/harness/workspaces/tht-test.yaml +++ b/harness/workspaces/tht-test.yaml @@ -2,6 +2,8 @@ # Struttura allineata a workspaces/tht.example.yaml e tht/config.py. # I segreti vivono SOLO in .env (${THT_*}). +language: it # descrizioni tabelle/colonne ed evidence sono in italiano (PSD) + database: host: ${THT_DB_HOST} port: ${THT_DB_PORT} diff --git a/harness/workspaces/tht.example.yaml b/harness/workspaces/tht.example.yaml index 97109d9b..b55c8664 100644 --- a/harness/workspaces/tht.example.yaml +++ b/harness/workspaces/tht.example.yaml @@ -3,6 +3,8 @@ # database + rest per il DWH; vector_rest/vector_write_rest per il pgvector (doppia key); # vector_db per il loading diretto (server-only); embeddings + evidence + execution. +language: it # descrizioni tabelle/colonne ed evidence sono in italiano (PSD) + database: host: ${THT_DB_HOST} port: ${THT_DB_PORT} # es. 5437 (Postgres diretto Supabase; 5432 = pooler)