F — reviewer_select options may now carry a `decision` payload {type, subject,
detail?, rationale?} plus an optional `advance`. Picking such an option IS the
confirmation: the gate persists it directly (tht decision add) and optionally
advances, with no redundant reviewer_decide/reviewer_confirm follow-up gate.
Options without a payload stay ask-only; back/exit/Other never persist.
Pure logic extracted + exported for unit tests: resolveSelectOutcome (classifies
the response) and decisionAddArgs (shared with reviewer_decide, DRY). Gate JS
suite 33/33 (gate_select_decision.test.js, +5); harness pytest 269 unchanged.
Contract docs updated together: reviewer_select tool description, SKILL.md
(widget summary, disciplines 2-3, Phase-1 single-pick), and the CLAUDE.md gate
note. Live verification (model truly emits reviewer_select+decision, decision in
review_decisions.jsonl, no follow-up gate) deferred to workstream G — it is
model-behavior-dependent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
254 lines
15 KiB
Markdown
254 lines
15 KiB
Markdown
---
|
|
name: tht-sessione
|
|
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.
|
|
---
|
|
|
|
# Thoth session workflow (phases 1-8)
|
|
|
|
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.
|
|
|
|
The reviewer answers via the gate's **widgets** (built by `tht-gate.js`):
|
|
`reviewer_select` (single pick; a chosen option carrying a `decision` payload IS the
|
|
confirmation and is persisted directly — an option without a payload only asks),
|
|
`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.
|
|
|
|
**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.
|
|
|
|
## Disciplines (hold in every phase)
|
|
|
|
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`, and for a
|
|
`reviewer_select` whose chosen option carries a `decision`, do NOT add a separate
|
|
confirmation gate after — the choice 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 recorded choice.
|
|
3. **Single pick vs multi-answer.** For a single-pick clarification or decision, use
|
|
`reviewer_select` and attach a `decision` payload (`{type, subject, detail?,
|
|
rationale?}`) to each concrete option: picking it persists that decision directly —
|
|
no follow-up `reviewer_decide`/`reviewer_confirm`. Options WITHOUT a payload only
|
|
ask (use for pure iteration before you commit). For genuinely multi-answer
|
|
decisions (several options simultaneously true) use `reviewer_decide` (multiselect).
|
|
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 find
|
|
"<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.
|
|
|
|
## Phase 0 — Resume (cold start)
|
|
|
|
When launched with `/riprendi-sessione <id>` you have NO prior conversation — the
|
|
persisted state is your only context. Bootstrap before doing anything else:
|
|
|
|
1. `tht session show <id> --json` → read `phase` (the current phase N), `status`, and
|
|
the manifest (`question`, `database`, `schema`).
|
|
2. Load the artifacts produced so far, as needed for phase N: `question.md` (revised
|
|
question), `schema_linking.json` (F4 output), `ctes/*.sql` + `cte_tests.json` (F6),
|
|
`sql_final.sql` (F7). The decision ledger is summarized by `tht session show`.
|
|
3. **Resume at phase N reviewing the existing artifacts** (same discipline as rollback,
|
|
§Disciplines 11). Do NOT restart from Phase 1, do NOT re-run `tht` commands for
|
|
artifacts that already exist and are valid, and do NOT treat this as a new question.
|
|
4. Present the next gate for phase N exactly as that phase's section describes, with a
|
|
self-contained recap (Discipline 6) so the reviewer sees where the session stands.
|
|
|
|
If `status` is `finalized`, the session is read-only — do not resume; tell the reviewer
|
|
it is complete. (The backend already refuses resume for finalized/archived sessions.)
|
|
|
|
## Phase 1 — Clarification
|
|
|
|
Prerequisite: you must already be in Phase 1.
|
|
|
|
1. Explore the DWH and knowledge base: `tht search find "<term>"` (evidence + schema, LSH
|
|
over real values) and `tht search find --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 the
|
|
candidate interpretations (`recommended:true` on the best) + "Altro". Pick the widget
|
|
by the question's shape:
|
|
- **Exactly one interpretation is correct** (mutually exclusive) → `reviewer_select`
|
|
with a `concept_clarified` `decision` on each concrete option: the reviewer's pick
|
|
IS the confirmation and is recorded directly (no follow-up `reviewer_decide`).
|
|
- **Several answers can be simultaneously true** (e.g. more than one valid population,
|
|
procedure code, or time window) → do NOT use `reviewer_select`: single-pick buttons
|
|
force one answer and mislead the reviewer. Use `reviewer_decide` directly (it emits a
|
|
**multiselect checkbox** widget), one option per candidate, each carrying its own
|
|
`concept_clarified` decision; the reviewer checks all that apply. Keep `advance:false`
|
|
(Phase 1 still closes via the phase gate in step 3).
|
|
|
|
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 confirmation after each individual
|
|
clarification — each is already recorded by its `reviewer_select`/`reviewer_decide`
|
|
(`concept_clarified`) choice, 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).
|
|
|
|
## Phase 2 — Memories
|
|
|
|
Prerequisite: Phase 1 closed.
|
|
|
|
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).
|
|
|
|
## Phase 3 — Rewriting
|
|
|
|
Prerequisite: Phase 2 closed; the `question_rewritten` decision is refused before
|
|
Phase 3 (CLI exit 5).
|
|
|
|
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.
|
|
|
|
## Phase 4 — Schema linking
|
|
|
|
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).** 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, retrieve it with `tht search find --kind formula
|
|
"<concept>"` (or derive it from the evidence/context), present it, and let the
|
|
reviewer approve/reject (`concept_formula_approved`/`concept_formula_rejected`).
|
|
Reflect the approved formula in `schema_linking.json` (`concept_formulas`).
|
|
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).
|
|
|
|
## Phase 5 — Synthesis
|
|
|
|
Prerequisite: Phase 4 closed; `schema_linking.json` present.
|
|
|
|
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"`.
|
|
|
|
## Phase 6 — CTE plan
|
|
|
|
Prerequisite: Phase 5 closed.
|
|
|
|
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"`.
|
|
|
|
## Phase 7 — Final SQL
|
|
|
|
Prerequisite: Phase 6 closed.
|
|
|
|
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"`.
|
|
|
|
## Phase 8 — Datamart
|
|
|
|
Prerequisite: Phase 7 closed.
|
|
|
|
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.
|
|
|
|
## Session end
|
|
|
|
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.
|