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
+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.