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 <cmd> citati registrati, 165 passed.
This commit is contained in:
2026-06-27 14:50:30 +02:00
parent de61034a8d
commit 292048f777
9 changed files with 317 additions and 262 deletions
@@ -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-<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. **`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.
+183 -134
View File
@@ -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
"<value>"` (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 "<termine>"` (evidence +
schema) e `tht search --kind evidence "<termine>"`. 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 "<term>"` (evidence + schema, LSH
over real values) and `tht search --kind evidence "<term>"`. 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 "<domanda>" --session <id>
--json`. **Passa SEMPRE `--session <id>`**: 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 "<question>" --session <id> --json`.
**ALWAYS pass `--session <id>`**: 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-<id>"` 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:"<domanda riscritta integrale>"}`.
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:"<full rewritten question>"}`.
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/<id>/ctes/<nome>.sql`
(SOLO `WITH ... AS (...)`, niente SELECT finale), testa con
`tht cte test --session <id> <nome>`, 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/<id>/ctes/<name>.sql` (ONLY the
`WITH ... AS (...)` block, NO trailing SELECT), test with `tht cte test --session
<id> <name>`, 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/<id>/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/<id>/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.
+30 -31
View File
@@ -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/<id>/ctes/<nome>.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/<id>/ctes/<name>.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:
> **<nome>** — scopo: <una riga>
> Tabelle usate: <elenco> (tutte dal perimetro promosso)
> **<name>** — purpose: <one line>
> Tables used: <list> (all from the promoted perimeter)
> ```sql
> WITH <nome> AS (...)
> WITH <name> AS (...)
> ```
Dopo la conferma (o per revisione diretta del reviewer) scrivi il file e
testa: `tht cte test --session <id> <nome>`. 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 <id> <name>`. 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.
+34 -35
View File
@@ -1,40 +1,39 @@
# Presentazione delle memorie riapplicabili
# Presenting reusable memories
Le memorie arrivano da `tht memory search "<domanda>" --session <id> --json`,
gia' ordinate per similarita'. Tu le argomenti, il reviewer decide. MAI
applicarle da solo. **Passa SEMPRE `--session <id>`**: 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 "<question>" --session <id> --json`, already
ordered by similarity. You argue them, the reviewer decides. NEVER apply them on
your own. **ALWAYS pass `--session <id>`**: 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-<id>"` (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-<id>"` (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).
+19 -19
View File
@@ -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:** <testo>
> **Condizioni:** 1) ... 2) ... 3) ...
> **Assunzioni usate:** <elenco con riferimento alle decisioni>
> **Output atteso:** <forma del risultato>
> **Rewritten question:** <text>
> **Conditions:** 1) ... 2) ... 3) ...
> **Assumptions used:** <list with reference to decisions>
> **Expected output:** <shape of the result>
Itera sulla base delle correzioni. Non procedere alla Fase 4 senza conferma.
Iterate based on corrections. Do not proceed to Phase 4 without confirmation.
@@ -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/<id>/sql_final.sql` deve contenere SOLO il SQL, pulito e
copiabile: niente commenti di razionale (quello vive negli artefatti di audit).
The file `sessions/<id>/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 = <fact>.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 = <fact>.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 <fact>)) - (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 <fact>)) - (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 "<valore>"` (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 "<value>"` (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:"<cosa e' cambiato>", rationale:"<perche'>"}],
allow_other:false)`.
Every substantive revision is recorded with:
`reviewer_decide(options:[{label:"Register revision", type:"sql_revised",
subject:"sql_final", detail:"<what changed>", rationale:"<why>"}], allow_other:false)`.
+4
View File
@@ -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()
+2
View File
@@ -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}
+2
View File
@@ -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)