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). 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/". 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. 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. 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. 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 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 You are the orchestrator of a **human-in-the-middle** workflow: you propose, the
decide, la CLI `tht` persiste. **NON sei in modalità autonoma.** Una domanda alla reviewer decides, the `tht` CLI persists. **You are NEVER in autonomous mode.**
volta al reviewer; attendi la sua risposta prima di proseguire; MAI promuovere, One question to the reviewer at a time; wait for their answer before proceeding;
escludere, correggere o applicare alcunché senza conferma esplicita. 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`): The reviewer answers via the gate's **widgets** (built by `tht-gate.js`):
`reviewer_select` (scelta singola), `reviewer_decide` (multiselect con payload `reviewer_select` (single pick, no decision recorded), `reviewer_decide`
decisione), `reviewer_confirm` (gate su artefatto). Il testo libero arriva via (multiselect, each selected option IS a decision — the choice is the confirmation),
"Altro" o prefissando `!` nella chat. Le invarianti (Altro sempre presente, `reviewer_confirm` (gate on an artifact / phase transition). Free text arrives via
no-limbo, nessuno step senza esito) sono garantite dal gate. 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 ## Disciplines (hold in every phase)
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 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 + Prerequisite: you must already be in Phase 1.
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).
## 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> Prerequisite: Phase 1 closed.
--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.
## 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 ## Phase 3 — Rewriting
prima di Fase 3 (exit 5 dalla CLI).
1. Leggi `rewriting.md`. Produci la domanda riscritta (popolazione esplicita nei Prerequisite: Phase 2 closed; the `question_rewritten` decision is refused before
termini del modello, ogni condizione come clausola numerabile, termini ambigui Phase 3 (CLI exit 5).
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.
## 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 Prerequisite: Phase 3 closed.
contesto schema.
2. Proponi tabelle/colonne/join in `reviewer_decide(advance:true)`. Registra 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`. `table_promoted`/`table_excluded`/`column_corrected`/`join_modified`.
3. **Value grounding (D14a).** Se un valore citato (es. "ablazione") matcha 3. **Value grounding (D14a).** If a cited value (e.g. "ablazione") matches MULTIPLE
**più colonne** (flag + testo patologia), presenta un `reviewer_decide` con columns (a boolean flag + a free-text patologia field), present a `reviewer_decide`
opzioni `value_grounded` per ogni colonna candidata (l'LSH le espone tutte, with a `value_grounded` option for each candidate column (the LSH exposes all of
non collassa al miglior match). Il reviewer sceglie l'ancora. them, not collapsed to the best match). The reviewer chooses the anchor(s).
4. **Formula di concetto (D14b).** Se un concetto (es. "fascia pediatrica", 4. **Concept formula (D14b).** If a concept (e.g. "fascia pediatrica", "stesso anno")
"stesso anno") ha una formula SQL candidata (ricerca nelle evidence o derivata has a candidate SQL formula (found in the evidence or derived from context),
dal contesto), presentala e il reviewer approva/rifiuta present it and let the reviewer approve/reject
(`concept_formula_approved`/`rejected`). (`concept_formula_approved`/`concept_formula_rejected`).
5. Scrivi `schema_linking.json` (l'artefatto di Fase 4) e chiudi con 5. Write `schema_linking.json` (the Phase 4 artifact) and close with
`reviewer_confirm kind:"phase"`. Non eseguire `tht session check` (è Fase 5). `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). 1. `tht session check` (objective gate: decisions present + schema_linking valid).
2. Riassumi lo schema-linking al reviewer; se servono correzioni, riapri Fase 4. 2. Summarize the schema-linking to the reviewer; if corrections are needed, reopen
3. **Promozione memorie (F5).** Se vuoi promuovere memorie da questa sessione, usa Phase 4.
`tht memory save-one` (workstation) o `tht memory promote` (server). Le memory 3. **Memory promotion (F5).** If you want to promote memories from this session, use
vivono SOLO nel vectordb (niente registry locale). `tht memory save-one` (workstation) or `tht memory promote` (server). Memories
4. Chiudi con `reviewer_confirm kind:"phase"`. 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): 1. Read `cte.md`. Decompose the rewritten question into CTEs (Agent View Generation):
ogni CTE cattura un sottoinsieme informativo con scopo chiaro, in snake_case. each CTE captures an informative subset with a clear purpose, named in snake_case.
2. Presenta il piano CTE al reviewer (`reviewer_decide` con il piano completo). 2. Present the full CTE plan to the reviewer (`reviewer_decide` with the plan).
3. Per ogni CTE (nell'ordine del piano): scrivi `sessions/<id>/ctes/<nome>.sql` 3. For each CTE (in plan order): write `sessions/<id>/ctes/<name>.sql` (ONLY the
(SOLO `WITH ... AS (...)`, niente SELECT finale), testa con `WITH ... AS (...)` block, NO trailing SELECT), test with `tht cte test --session
`tht cte test --session <id> <nome>`, presenta il risultato in <id> <name>`, present the result in `reviewer_confirm kind:"cte_result"`. The next
`reviewer_confirm kind:"cte_result"`. Il CTE successivo è testabile SOLO dopo CTE is testable ONLY after the previous one is approved (CLI exit 5 if out of
che il precedente è approvato (exit 5 se fuori ordine). Copia ESATTAMENTE i order). Copy table/column names EXACTLY from the schema context; use values
nomi di tabelle/colonne dal contesto schema; usa valori verificati con `tht search`. verified with `tht search`.
4. Dopo l'ultimo CTE approvato, chiudi con `reviewer_confirm kind:"phase"`. 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 1. Read `sql-generation.md`. Recursive divide-and-conquer: the CTEs approved in
Fase 6 sono i mattoni preferenziali (riusali per nome). Phase 6 are the preferred building blocks (reuse them by name).
2. Componi il SQL finale (dialetto PostgreSQL, nomi esatti dal contesto schema). 2. Compose the final SQL (PostgreSQL dialect, exact names from the schema context).
**Dimensione tempo:** `data_time_key` è la FK verso `dim_time.day_key` (NON **Time dimension:** `data_time_key` is the FK to `dim_time.day_key` (NOT declared
dichiarata nel DWH, va aggiunta a mano nel join); usa `JOIN dim_time` e le sue in the DWH, must be added by hand to the join); use `JOIN dim_time` and its
colonne (`dt.year`, `dt.month`, ...), MAI aritmetica sulla chiave. columns (`dt.year`, `dt.month`, …), NEVER arithmetic on the key.
3. `tht sql validate` + `tht sql preview` (max 10 righe). Su errori/risultati 3. `tht sql validate` + `tht sql preview` (max 10 rows). On errors / suspicious
sospetti, applica la checklist di `sql-generation.md` e correggi col reviewer. results, apply the `sql-generation.md` checklist and correct with the reviewer.
4. `tht sql save` scrive `sessions/<id>/sql_final.sql` (SOLO SQL pulito, niente 4. `tht sql save` writes `sessions/<id>/sql_final.sql` (ONLY clean SQL, no comments).
commenti). Chiudi con `reviewer_confirm kind:"sql"`. 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). 1. Ask the reviewer whether they want a datamart (`reviewer_select` yes/no).
2. Se sì: `tht datamart generate` (stub — alza NotImplementedError per ora). 2. If yes: `tht datamart generate` (stub — raises NotImplementedError for now). Tell
Comunica al reviewer che la generazione dbt non è ancora implementata. the reviewer that dbt generation is not implemented yet.
3. Se no: chiudi con `reviewer_confirm kind:"phase"`. La sessione è finalizzabile. 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 When the workflow is complete (Phase 8), `tht session finalize` closes the session
e sblocca l'input. Lo stato persistito (ledger `review_decisions.jsonl` + artefatti) and unlocks input. The persisted state (ledger `review_decisions.jsonl` + artifacts)
è la verità: ciò che non è registrato non è avvenuto. 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 Adapted from the AV-SQL CTE step. CTEs incrementally capture the information needed
l'informazione necessaria a rispondere alla domanda riscritta, SENZA to answer the rewritten question, WITHOUT answering the final question (that is the
rispondere alla domanda finale (quella e' la fase successiva). 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 1. Copy table and column names EXACTLY from the schema context provided
(`tht schema render --format mschema-text --table ...`). Mai inventare (`tht schema render --format mschema-text --table ...`). Never invent objects
oggetti non presenti. that are not present.
2. Tieni sempre le chiavi (PK e colonne di join) nei CTE: serviranno dopo. 2. Always keep the keys (PK and join columns) in the CTEs: they will be needed later.
3. Meglio una colonna in piu' che una in meno: se non sei sicuro, includila. 3. Better one column too many than one too few: if unsure, include it.
4. Un CTE = un sottoinsieme informativo con uno scopo chiaro (es. "ricoveri 4. One CTE = one informative subset with a clear purpose (e.g. "ricoveri with
con ablazione nel 2025"), nominato in snake_case parlante. ablazione in 2025"), named in a speaking snake_case.
5. I CTE possono referenziarsi in catena; l'ultimo del file e' quello che 5. CTEs can chain-reference each other; the last one in the file is the one that
`tht cte test` interroghera'. `tht cte test` will query.
6. Ogni file in sessions/<id>/ctes/<nome>.sql contiene SOLO il blocco 6. Each file in `sessions/<id>/ctes/<name>.sql` contains ONLY the `WITH ... AS (...)`
`WITH ... AS (...)` (anche multi-CTE), SENZA SELECT finale. Una riga block (multi-CTE allowed), WITHOUT a trailing SELECT. A `SELECT ...` line after
`SELECT ...` dopo il blocco WITH causa un errore in `tht cte test`: non the WITH block causes an error in `tht cte test`: never add it. CTEs are tested
aggiungerla mai. I CTE vengono testati **e approvati (decisione **and approved (decision `cte_approved`) in plan order**: the next CTE is testable
`cte_approved`) nell'ordine del piano**: il CTE successivo è testabile ONLY after the previous one is approved with `kind:"cte_result"`. The CLI refuses
SOLO dopo che il precedente è stato approvato con `kind:"cte_result"`. La out-of-order CTEs (exit 5).
CLI rifiuta i CTE fuori ordine (exit 5). 7. Filters: use field values verified with `tht search` (LSH match on real values),
7. Filtri: usa i valori di campo verificati con `tht search` (match LSH sui not imagined values.
valori reali), non valori immaginati.
Presentazione al reviewer, per ogni CTE del piano: Presentation to the reviewer, for each CTE in the plan:
> **<nome>** — scopo: <una riga> > **<name>** — purpose: <one line>
> Tabelle usate: <elenco> (tutte dal perimetro promosso) > Tables used: <list> (all from the promoted perimeter)
> ```sql > ```sql
> WITH <nome> AS (...) > WITH <name> AS (...)
> ``` > ```
Dopo la conferma (o per revisione diretta del reviewer) scrivi il file e After confirmation (or for direct reviewer review) write the file and test:
testa: `tht cte test --session <id> <nome>`. Su errore o warning rilevanti, `tht cte test --session <id> <name>`. On error or relevant warnings, discuss the
discuti la correzione col reviewer prima di riscrivere il file. Se il correction with the reviewer before rewriting the file. If the reviewer edits the
reviewer modifica il file a mano, RILEGGILO prima di ritestare. 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`, Memories arrive from `tht memory search "<question>" --session <id> --json`, already
gia' ordinate per similarita'. Tu le argomenti, il reviewer decide. MAI ordered by similarity. You argue them, the reviewer decides. NEVER apply them on
applicarle da solo. **Passa SEMPRE `--session <id>`**: la CLI esclude le memorie your own. **ALWAYS pass `--session <id>`**: the CLI excludes memories already decided
gia' decise in questa sessione (applicate o rifiutate), cosi' non riproponi cio' in this session (applied or rejected), so you don't re-propose what the reviewer has
che il reviewer ha gia' scartato (anche dopo un reopen della Fase 2). already discarded (even after a Phase 2 reopen).
Per ogni memoria da presentare in checklist, includi nella `label` e/o For each memory to present in the checklist, include in the option's `label` and/or
`description` dell'opzione: `description`:
- **Cosa dice**: tipo + soggetto + detail (es. "table_promoted: - **What it says**: type + subject + detail (e.g. "table_promoted:
fact_seeablazione — tabella principale per le ablazioni"). fact_seeablazione — main table for ablazioni").
- **Da dove viene**: question_context e session_id di origine. - **Where it comes from**: question_context and origin session_id.
- **Perche' potrebbe valere qui**: sovrapposizione di concetti/tabelle con la - **Why it might apply here**: overlap of concepts/tables with the current question
domanda corrente (campi tables/concepts), score di similarita'. (fields tables/concepts), similarity score.
- **Rischio fuori-contesto**: cosa c'era nella sessione originale che qui - **Out-of-context risk**: what was in the original session that might not hold here
potrebbe non valere (periodo diverso, popolazione diversa, schema cambiato). (different period, different population, changed schema).
Regole: Rules:
- Proponi al massimo **5** candidate. Includi SOLO memorie dei 3 tipi - Propose at most **5** candidates. Include ONLY memories of the 3 reusable types:
riusabili: `concept_clarified`, `table_promoted`, `table_excluded`. Le `concept_clarified`, `table_promoted`, `table_excluded`. Query-specific decisions
decisioni query-specifiche (es. `question_rewritten`, `sql_approved`) NON (e.g. `question_rewritten`, `sql_approved`) are NOT to be proposed: they don't
vanno proposte: non si trasferiscono ad altre domande. transfer to other questions.
- Tutte le memorie candidate vanno in **un solo** `reviewer_decide(multi:true, - All candidate memories go in **a single** `reviewer_decide(multi:true,
advance:true, allow_empty:true)`: ogni opzione selezionata viene applicata advance:true, allow_empty:true)`: each selected option is applied (register the
(registra la decisione col tipo appropriato, citando l'id memoria nel decision with the appropriate type, citing the memory id in the rationale), each
rationale), ogni opzione deselezionata viene **registrata come `memory_rejected` deselected option is **recorded as `memory_rejected` by the gate** (so the next
dal gate** (così la prossima `tht memory search --session` non la ripropone). `tht memory search --session` won't re-propose it). To enable this, EVERY memory
Per abilitare questo, OGNI opzione memoria DEVE portare il campo option MUST carry the field `mem_id:"mem-<id>"` (besides `type`/`subject`/
`mem_id:"mem-<id>"` (oltre a `type`/`subject`/`rationale`). La checklist parte `rationale`). The checklist starts pre-selected with the recommended memories.
pre-selezionata con le memorie raccomandate. Con `allow_empty:true` una With `allow_empty:true` an **empty selection is accepted** (no memory applied; the
selezione **vuota è accettata** (nessuna memoria applicata; le deselezionate deselected ones are still recorded as rejected) and the phase advances — no
restano comunque registrate come rifiutate) e la fase avanza — nessun gate separate gate.
separato. - For "inspect": show the memory's full JSON record in the prose before presenting
- Per "ispeziona": mostra il record JSON integrale della memoria nella prosa the checklist, if the reviewer asks.
prima di presentare la checklist, se il reviewer lo richiede. - If no memory clears score 0.5, say so and close the phase quickly (with
- Se nessuna memoria supera score 0.5, dillo e chiudi la fase rapidamente `reviewer_confirm kind:"phase"` if the list is empty).
(con `reviewer_confirm kind:"phase"` se la lista è vuota).
+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 Adapted from the "rephrase question" step of AV-SQL: decompose the question into
condizioni esplicite, senza perdere informazione. 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 1. Make the population explicit (who/what is counted or selected) using the data
del modello dati (es. "pazienti distinti in dim_patient"). model's terms (e.g. "distinct patients in dim_patient").
2. Esplicitare ogni condizione come clausola separata e numerabile: 2. Make every condition an explicit, numbered clause: time window, included/excluded
periodo temporale, eventi inclusi/esclusi, soglie, stati. events, thresholds, states.
3. Sostituire i termini ambigui con i concetti chiariti in Fase 1, citando 3. Replace ambiguous terms with the concepts clarified in Phase 1, citing the
l'evidence che li definisce quando esiste (id evidence tra parentesi). evidence that defines them when it exists (evidence id in parentheses).
4. Esplicitare l'output atteso: conteggio, elenco, aggregato, trend. 4. Make the expected output explicit: count, list, aggregate, trend.
5. Restare in italiano, in una forma che un secondo reviewer capirebbe senza 5. Stay in the workspace language, in a form a second reviewer would understand
aver visto la conversazione. without having seen the conversation.
Formato della proposta al reviewer: Format of the proposal to the reviewer:
> **Domanda riscritta:** <testo> > **Rewritten question:** <text>
> **Condizioni:** 1) ... 2) ... 3) ... > **Conditions:** 1) ... 2) ... 3) ...
> **Assunzioni usate:** <elenco con riferimento alle decisioni> > **Assumptions used:** <list with reference to decisions>
> **Output atteso:** <forma del risultato> > **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) Adapted from the "query generation" step of AV-SQL (recursive divide-and-conquer)
e dalla sua checklist di revisione. and its review checklist.
## Generazione (divide-and-conquer) ## Generation (divide-and-conquer)
1. **Dividi**: scomponi la domanda riscritta in sotto-domande, ognuna mirata a 1. **Divide**: decompose the rewritten question into sub-questions, each aimed at a
un pezzo di informazione o logica (una popolazione, un filtro, un aggregato). piece of information or logic (a population, a filter, an aggregate).
2. **Conquista**: per ogni sotto-domanda formula uno pseudo-SQL, con segnaposto 2. **Conquer**: for each sub-question formulate a pseudo-SQL, with placeholders for
per le sotto-domande non ancora risolte. I CTE gia' testati in Fase 6 sono i sub-questions not yet resolved. The CTEs already tested in Phase 6 are the
mattoni preferenziali: riusali per nome, col loro esito noto. preferred building blocks: reuse them by name, with their known outcome.
3. **Ricombina**: sostituisci i segnaposto dal basso verso l'alto fino al SQL 3. **Recombine**: replace placeholders bottom-up until the full SQL. The final SQL
completo. Il SQL finale puo' includere i CTE nel proprio WITH. may include the CTEs in its own WITH.
4. Dialetto: PostgreSQL. Copia ESATTAMENTE i nomi di tabelle e colonne dal 4. Dialect: PostgreSQL. Copy table and column names EXACTLY from the schema context;
contesto schema; mai inventare oggetti. never invent objects.
Il file `sessions/<id>/sql_final.sql` deve contenere SOLO il SQL, pulito e The file `sessions/<id>/sql_final.sql` must contain ONLY the SQL, clean and
copiabile: niente commenti di razionale (quello vive negli artefatti di audit). 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 Fact tables have `data_time_key` (`integer`, format `YYYYMMDD`): it is the FK to
verso `dim_time.day_key`. **Questa FK non è dichiarata** nel DWH (le fact hanno `dim_time.day_key`. **This FK is NOT declared** in the DWH (facts have
`foreign_keys: []`), quindi non comparirà in `schema_linking.json`: vai aggiunta `foreign_keys: []`), so it will NOT appear in `schema_linking.json`: you must add it
a mano nel join. by hand to the join.
- Per estrarre anno, mese, trimestre, semestre ecc. fai - To extract year, month, quarter, semester etc. do
`JOIN dim_time dt ON dt.day_key = <fact>.data_time_key` e usa le colonne della `JOIN dim_time dt ON dt.day_key = <fact>.data_time_key` and use the dimension's
dimensione: `dt.year`, `dt.month`, `dt.quarter`, `dt.semester`, `dt.full_date`, columns: `dt.year`, `dt.month`, `dt.quarter`, `dt.semester`, `dt.full_date`,
`dt.month_name_it`, `dt.year_month`. `dt.month_name_it`, `dt.year_month`.
- **Non** fare aritmetica sulla chiave (es. `data_time_key / 10000` per l'anno): - **Do NOT** do arithmetic on the key (e.g. `data_time_key / 10000` for the year):
funziona per caso ma è fragile e si rompe appena serve formattare una data o it works by accident but is fragile and breaks as soon as you need to format a date
fare cast. Usa sempre `dim_time`. or do a cast. Always use `dim_time`.
- "Ultimi N anni dall'anno più recente": `dt.year >= (SELECT MAX(year) FROM - "Last N years from the most recent year":
dim_time WHERE day_key IN (SELECT data_time_key FROM <fact>)) - (N-1)`, oppure `dt.year >= (SELECT MAX(year) FROM dim_time WHERE day_key IN (SELECT data_time_key FROM <fact>)) - (N-1)`,
calcola il max anno sulle righe effettivamente presenti nella fact. 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? - Do the returned columns answer the question exactly?
- I filtri (WHERE/HAVING) riflettono tutte le condizioni della domanda riscritta? - Do the filters (WHERE/HAVING) reflect ALL the conditions of the rewritten question?
- Aggregazioni, raggruppamenti e ordinamenti sono quelli richiesti? - Are aggregations, groupings and orderings the required ones?
- Risultato vuoto o zero: quasi sempre indica un problema in condizioni o join. - Empty or zero result: almost always indicates a problem in conditions or joins.
Verifica i valori dei filtri con `tht search "<valore>"` (match sui valori reali). Verify the filter values with `tht search "<value>"` (match on real values).
- I join seguono quelli promossi in schema_linking.json? (eccezione: la FK - Do the joins follow those promoted in `schema_linking.json`? (exception: the FK
`data_time_key → dim_time.day_key` non è dichiarata, vedi sezione tempo.) `data_time_key → dim_time.day_key` is not declared, see the time section.)
- Analisi temporali: stai usando `JOIN dim_time` e non aritmetica sulla chiave? - Time analyses: are you using `JOIN dim_time` and not key arithmetic?
Ogni revisione sostanziale va registrata con: Every substantive revision is recorded with:
`reviewer_decide(options:[{label:"Registra revisione", type:"sql_revised", `reviewer_decide(options:[{label:"Register revision", type:"sql_revised",
subject:"sql_final", detail:"<cosa e' cambiato>", rationale:"<perche'>"}], subject:"sql_final", detail:"<what changed>", rationale:"<why>"}], allow_other:false)`.
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 # workstation: postazione locale che legge il vectordb via REST; gli upsert remoti
# richiedono vector_write_rest, mentre init/clear/rebuild restano solo-server. # richiedono vector_write_rest, mentre init/clear/rebuild restano solo-server.
profile: Literal["server", "workstation"] = "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() paths: PathsConfig = PathsConfig()
examples: ExamplesConfig = ExamplesConfig() examples: ExamplesConfig = ExamplesConfig()
lsh: LshConfig = LshConfig() lsh: LshConfig = LshConfig()
+2
View File
@@ -2,6 +2,8 @@
# Struttura allineata a workspaces/tht.example.yaml e tht/config.py. # Struttura allineata a workspaces/tht.example.yaml e tht/config.py.
# I segreti vivono SOLO in .env (${THT_*}). # I segreti vivono SOLO in .env (${THT_*}).
language: it # descrizioni tabelle/colonne ed evidence sono in italiano (PSD)
database: database:
host: ${THT_DB_HOST} host: ${THT_DB_HOST}
port: ${THT_DB_PORT} 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); # 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. # vector_db per il loading diretto (server-only); embeddings + evidence + execution.
language: it # descrizioni tabelle/colonne ed evidence sono in italiano (PSD)
database: database:
host: ${THT_DB_HOST} host: ${THT_DB_HOST}
port: ${THT_DB_PORT} # es. 5437 (Postgres diretto Supabase; 5432 = pooler) port: ${THT_DB_PORT} # es. 5437 (Postgres diretto Supabase; 5432 = pooler)