refactor(harness): renaming prodotto tht (Onda -1)
Thoth (tht) è il prodotto, PSD è il cliente. Nessun riferimento al contesto
clinico nel codice.
Rinomine:
- comando+package nsp→tht (dir nsp/→tht/, 46 import, pyproject entry point)
- gate nsp-gate.js→tht-gate.js (+ rewrite token, relayIfNspFails→relayIfThtFails)
- workspace chirone.{example,test}.yaml→tht.{example,test}.yaml (generici)
- env THOTH_→THT_ (19 var) + NSP_ stragglers (NSP_HARNESS_ROOT, NSP_SESSION)
- commenti/docstring chirone/psdwp3/policlinico neutralizzati ('the reference
implementation', 'the DWH')
Aggiunto [tool.setuptools.packages.find] include=['tht*'] (necessario: l'auto-
discovery rompeva con tht/ + workspaces/ come top-level multipli).
.env operatore aggiornato in-place (prefissi THT_, valori preservati, gitignored).
Verifica: pytest 109 passed, npm test 14 pass, tht phase meta --json OK, zero
residui nsp/THOTH_/NSP_/chirone nel package.
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
// Each function turns plain params into a ui_request descriptor object. No Pi
|
||||
// context, no I/O -- this is the part of the gate that is fully testable in L1
|
||||
// (in JS, in-language, no Python mirror). The glue (emission via ctx.sendRaw,
|
||||
// anti-bypass, the no-limbo loop) is in nsp-gate.js and is verified at L2.
|
||||
// anti-bypass, the no-limbo loop) is in tht-gate.js and is verified at L2.
|
||||
//
|
||||
// The 6 widget kinds: info, select, multiselect, freetext, artifact-gate, artifact.
|
||||
// `widget` is an open field (a new kind needs a renderer, not infra changes);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Etichette riservate del gate HITL e helper di deduplica. Modulo PURO (nessuna
|
||||
// dipendenza da pi-tui): condiviso da nsp-gate.js e testabile in isolamento con
|
||||
// dipendenza da pi-tui): condiviso da tht-gate.js e testabile in isolamento con
|
||||
// node --test .pi/extensions/test_reserved_labels.mjs
|
||||
// `Altro — specifica…` e le due vie di fuga (torna indietro / esci) vengono
|
||||
// SEMPRE aggiunte dal gate alle opzioni in arrivo: se il modello ri-propone uno
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// nsp-gate.js -- Pi extension: the HITL gate for the ThothII NL->SQL workflow.
|
||||
// tht-gate.js -- Pi extension: the HITL gate for the ThothII NL->SQL workflow.
|
||||
//
|
||||
// REWRITE of ChironeWp3's nsp-gate.js. Two changes vs the source:
|
||||
// REWRITE of the reference implementation's tht-gate.js. Two changes vs the source:
|
||||
// (1) F2 single source: workflow facts (max_phase, phase names, the schema-linking
|
||||
// phase) come from `nsp phase meta --json`, NOT from JS-mirrored constants.
|
||||
// The ChironeWp3 PHASE_NAMES array (truncated to 7) is gone; F8/datamart can no
|
||||
// phase) come from `tht phase meta --json`, NOT from JS-mirrored constants.
|
||||
// the reference implementation PHASE_NAMES array (truncated to 7) is gone; F8/datamart can no
|
||||
// longer drift out of sync.
|
||||
// (2) D2/D4 widget-descriptor: the reviewer interaction is emitted as a
|
||||
// widget-descriptor JSON (built by ./gate/builders.js) and awaited by id, instead
|
||||
@@ -16,7 +16,7 @@
|
||||
// - the agent_end prose safety net
|
||||
// - the exit-code contracts with the CLI (5 = gate refusal, 6 = needs human,
|
||||
// 7 = not-ready silent no-op)
|
||||
// - textResult / nsp() / relayIfNspFails / advanceIfReady helpers
|
||||
// - textResult / tht() / relayIfThtFails / advanceIfReady helpers
|
||||
//
|
||||
// TESTING: the pure builders are L1-tested (./gate/__tests__/). This file is the
|
||||
// GLUE -- it depends on the Pi runtime (pi.on, pi.registerTool, ctx.sendRaw) and is
|
||||
@@ -37,44 +37,44 @@ import { ALTRO, BACK_LABEL, QUIT_LABEL, CONTROL_LABELS, isReserved, stripReserve
|
||||
|
||||
// --- anti-bypass block lists (spec D4, verbatim from source L169-177) -----------
|
||||
const FORBIDDEN = [
|
||||
/\bnsp\s+phase\s+(advance|reopen)\b/,
|
||||
/\bnsp\s+decision\s+add\b/,
|
||||
/\bnsp\s+cte\s+plan\b/,
|
||||
/\btht\s+phase\s+(advance|reopen)\b/,
|
||||
/\btht\s+decision\s+add\b/,
|
||||
/\btht\s+cte\s+plan\b/,
|
||||
];
|
||||
const PROTECTED_FILES = /(review_decisions\.jsonl|session_manifest\.yaml|cte_plan\.json)/;
|
||||
|
||||
// --- kickoff payloads (verbatim from source L184-212, load-bearing model prose) -
|
||||
const NUOVA_DOMANDA_KICKOFF =
|
||||
"Istruzioni operative — nuova sessione ThothII (workflow human-in-the-middle: tu " +
|
||||
"orchestri, il reviewer decide, la CLI `nsp` persiste; NON sei in modalita' autonoma).\n" +
|
||||
'1. Esegui `nsp session new "<la domanda dell\'utente nel messaggio sopra>"` e annota ' +
|
||||
"orchestri, il reviewer decide, la CLI `tht` persiste; NON sei in modalita' autonoma).\n" +
|
||||
'1. Esegui `tht session new "<la domanda dell\'utente nel messaggio sopra>"` e annota ' +
|
||||
"l'id stampato nell'ultima riga.\n" +
|
||||
"2. Carica la skill leggendo il file con il tool `read`: `.pi/skills/nsp-sessione/SKILL.md` " +
|
||||
"2. Carica la skill leggendo il file con il tool `read`: `.pi/skills/tht-sessione/SKILL.md` " +
|
||||
"(NON come comando di shell ne' come `/skill:...`). Poi segui il suo workflow dalla Fase 1 " +
|
||||
"(Chiarimento), usando l'id di sessione in ogni comando `nsp`.\n" +
|
||||
"(Chiarimento), usando l'id di sessione in ogni comando `tht`.\n" +
|
||||
"Se la skill non si carica (per qualsiasi motivo): FERMATI. Non proseguire da solo, non " +
|
||||
"improvvisare analisi o query. Comunica al reviewer che la skill nsp-sessione non e' " +
|
||||
"improvvisare analisi o query. Comunica al reviewer che la skill tht-sessione non e' " +
|
||||
"disponibile e attendi istruzioni.\n" +
|
||||
"Regole non negoziabili (valgono SEMPRE, anche senza la skill):\n" +
|
||||
"- Una domanda al reviewer per volta; attendi la sua risposta prima di proseguire.\n" +
|
||||
"- MAI promuovere, escludere, correggere o applicare alcunche' senza conferma esplicita.\n" +
|
||||
"- Le interazioni col reviewer passano dai tool reviewer_select/reviewer_decide/" +
|
||||
"reviewer_confirm (widget-descriptor); il reviewer invia testo libero prefissando '!'. " +
|
||||
"NON eseguire mai `nsp phase advance|reopen` ne' `nsp decision add` da shell.\n" +
|
||||
"NON eseguire mai `tht phase advance|reopen` ne' `tht decision add` da shell.\n" +
|
||||
"- Procedi una fase alla volta: a fine fase cedi il turno al reviewer, non incatenare le fasi.";
|
||||
|
||||
const RIPRENDI_KICKOFF =
|
||||
"Istruzioni operative — ripresa di una sessione ThothII esistente (id nel messaggio sopra).\n" +
|
||||
"1. Esegui `nsp session show <id>` e leggi: stato, domanda, decisioni registrate, presenza " +
|
||||
"1. Esegui `tht session show <id>` e leggi: stato, domanda, decisioni registrate, presenza " +
|
||||
"di schema_linking.json.\n" +
|
||||
"2. Carica la skill leggendo `.pi/skills/nsp-sessione/SKILL.md` con il tool `read` (non come " +
|
||||
"2. Carica la skill leggendo `.pi/skills/tht-sessione/SKILL.md` con il tool `read` (non come " +
|
||||
"comando di shell ne' `/skill:...`).\n" +
|
||||
"3. Determina l'ultima fase completata dai fatti persistiti (le decisioni sono la verita': " +
|
||||
"cio' che non e' registrato non e' avvenuto) e riprendi da li'.\n" +
|
||||
"Valgono le stesse regole non negoziabili: una domanda per volta, conferma esplicita, tool " +
|
||||
"reviewer_*, niente phase advance/reopen o decision add da shell, una fase alla volta.";
|
||||
|
||||
// Recovery hint shown when `nsp phase advance` refuses with exit 5 (gate not satisfied).
|
||||
// Recovery hint shown when `tht phase advance` refuses with exit 5 (gate not satisfied).
|
||||
const PHASE_RECOVERY = "Completa i prerequisiti della fase (decisioni/artefatti) e riprova.";
|
||||
|
||||
// --- helpers (verbatim from source) -------------------------------------------
|
||||
@@ -84,15 +84,15 @@ function textResult(text) {
|
||||
}
|
||||
|
||||
// Single chokepoint for all CLI calls. cwd is the Pi project root (harness/).
|
||||
function nsp(ctx, args) {
|
||||
return execFileSync("nsp", args, { cwd: ctx.cwd, encoding: "utf8" });
|
||||
function tht(ctx, args) {
|
||||
return execFileSync("tht", args, { cwd: ctx.cwd, encoding: "utf8" });
|
||||
}
|
||||
|
||||
// Runs a privileged nsp call; converts any failure into an actionable textResult
|
||||
// Runs a privileged tht call; converts any failure into an actionable textResult
|
||||
// (never propagates a raw "Command failed" to the model).
|
||||
function relayIfNspFails(ctx, args, recovery) {
|
||||
function relayIfThtFails(ctx, args, recovery) {
|
||||
try {
|
||||
nsp(ctx, args);
|
||||
tht(ctx, args);
|
||||
return null;
|
||||
} catch (e) {
|
||||
const cliMsg = (e.stderr || e.message || String(e)).toString().trim();
|
||||
@@ -100,13 +100,13 @@ function relayIfNspFails(ctx, args, recovery) {
|
||||
}
|
||||
}
|
||||
|
||||
// F2 single-source: workflow facts from `nsp phase meta --json`. Cached per session.
|
||||
// F2 single-source: workflow facts from `tht phase meta --json`. Cached per session.
|
||||
// Replaces the source's mirrored PHASE_NAMES constant (which drifted to 7 entries)
|
||||
// and the regex-parse of `nsp phase show` text.
|
||||
// and the regex-parse of `tht phase show` text.
|
||||
let _phaseMetaCache = null;
|
||||
function phaseMeta(ctx) {
|
||||
if (_phaseMetaCache) return _phaseMetaCache;
|
||||
const raw = nsp(ctx, ["phase", "meta", "--json"]);
|
||||
const raw = tht(ctx, ["phase", "meta", "--json"]);
|
||||
const meta = JSON.parse(raw);
|
||||
_phaseMetaCache = meta;
|
||||
return meta;
|
||||
@@ -127,15 +127,15 @@ function schemaLinkingPhase(ctx) {
|
||||
}
|
||||
|
||||
function currentPhase(ctx, session) {
|
||||
const out = nsp(ctx, ["phase", "show", "--session", session]);
|
||||
const out = tht(ctx, ["phase", "show", "--session", session]);
|
||||
const m = out.match(/Fase corrente:\s*(\d+)/);
|
||||
return m ? parseInt(m[1], 10) : 1;
|
||||
}
|
||||
|
||||
// nsp phase advance --if-ready: exit 7 = not ready (silent no-op), others propagated.
|
||||
// tht phase advance --if-ready: exit 7 = not ready (silent no-op), others propagated.
|
||||
function advanceIfReady(ctx, session) {
|
||||
try {
|
||||
nsp(ctx, ["phase", "advance", "--if-ready", "--session", session]);
|
||||
tht(ctx, ["phase", "advance", "--if-ready", "--session", session]);
|
||||
return { advanced: true };
|
||||
} catch (e) {
|
||||
if (e.status === 7) return { advanced: false };
|
||||
@@ -146,7 +146,7 @@ function advanceIfReady(ctx, session) {
|
||||
// --- widget emission + wait (NEW: replaces ctx.ui.* blocking primitives) -------
|
||||
//
|
||||
// emitAndWait(ctx, descriptor) sends a ui_request widget and awaits the correlated
|
||||
// ui_response by id. This is the correlation-by-id layer ChironeWp3 never had (its
|
||||
// ui_response by id. This is the correlation-by-id layer the reference implementation never had (its
|
||||
// native TUI primitives handled it implicitly). The descriptor is built by the pure
|
||||
// ./gate/builders.js (L1-tested).
|
||||
//
|
||||
@@ -199,8 +199,8 @@ export default function (pi) {
|
||||
pi.on("tool_call", (event) => {
|
||||
if (event.toolName === "bash") {
|
||||
const cmd = event.input?.command ?? "";
|
||||
// nsp session finalize: legit session-close path -- unlock and let through.
|
||||
if (/\bnsp\s+session\s+finalize\b/.test(cmd)) {
|
||||
// tht session finalize: legit session-close path -- unlock and let through.
|
||||
if (/\btht\s+session\s+finalize\b/.test(cmd)) {
|
||||
lockActive = false;
|
||||
lastSteered = false;
|
||||
return;
|
||||
@@ -350,8 +350,8 @@ export default function (pi) {
|
||||
label: "Decisione di merito (reviewer)",
|
||||
description:
|
||||
"Pone una decisione di merito al reviewer via widget multiselect e PERSISTE le " +
|
||||
"scelte (nsp decision add). Ogni opzione porta un payload decision {type, subject, " +
|
||||
"detail, rationale}. Dopo la conferma, se p.advance e' vero tenta nsp phase advance --if-ready.",
|
||||
"scelte (tht decision add). Ogni opzione porta un payload decision {type, subject, " +
|
||||
"detail, rationale}. Dopo la conferma, se p.advance e' vero tenta tht phase advance --if-ready.",
|
||||
parameters: Type.Object({
|
||||
session: Type.String(),
|
||||
title: Type.String(),
|
||||
@@ -397,7 +397,7 @@ export default function (pi) {
|
||||
"--subject", d.subject];
|
||||
if (d.detail) args.push("--detail", d.detail);
|
||||
if (d.rationale) args.push("--rationale", d.rationale);
|
||||
const err = relayIfNspFails(ctx, args, "");
|
||||
const err = relayIfThtFails(ctx, args, "");
|
||||
if (err) return err;
|
||||
toAdd.push(d);
|
||||
}
|
||||
@@ -415,7 +415,7 @@ export default function (pi) {
|
||||
label: "Gate di avanzamento (reviewer)",
|
||||
description:
|
||||
"Checkpoint di fase/CTE/SQL: presenta un widget artifact-gate (artefatto + " +
|
||||
"Approva/Rifiuta/Altro) ed esegue l'azione privilegiata (nsp phase advance, cte plan, " +
|
||||
"Approva/Rifiuta/Altro) ed esegue l'azione privilegiata (tht phase advance, cte plan, " +
|
||||
"decision add sql_approved) solo su approvazione. kind: phase | cte_plan | cte_result | sql.",
|
||||
parameters: Type.Object({
|
||||
session: Type.String(),
|
||||
@@ -459,13 +459,13 @@ export default function (pi) {
|
||||
return textResult(`Fase approvata (sessione ${session}).`);
|
||||
}
|
||||
if (kind === "cte_plan") {
|
||||
const err = relayIfNspFails(ctx, ["cte", "plan", "--session", session], "");
|
||||
const err = relayIfThtFails(ctx, ["cte", "plan", "--session", session], "");
|
||||
if (err) return err;
|
||||
return textResult(`CTE plan approvato (sessione ${session}).`);
|
||||
}
|
||||
if (kind === "cte_result" || kind === "sql") {
|
||||
const dt = kind === "sql" ? "sql_approved" : "cte_approved";
|
||||
const err = relayIfNspFails(
|
||||
const err = relayIfThtFails(
|
||||
ctx,
|
||||
["decision", "add", "--session", session, "--type", dt, "--subject", `phase:${currentPhase(ctx, session)}`],
|
||||
"",
|
||||
@@ -481,7 +481,7 @@ export default function (pi) {
|
||||
name: "rewrite_question",
|
||||
label: "Riscrittura domanda (deterministica)",
|
||||
description:
|
||||
"Scrive deterministicamente question.md via nsp session set-question (evita il tool " +
|
||||
"Scrive deterministicamente question.md via tht session set-question (evita il tool " +
|
||||
"di edit unreliable). assumptions puo' essere array o stringa JSON.",
|
||||
parameters: Type.Object({
|
||||
session: Type.String(),
|
||||
@@ -503,7 +503,7 @@ export default function (pi) {
|
||||
if (Array.isArray(assumps)) {
|
||||
for (const a of assumps) args.push("--assumption", String(a));
|
||||
}
|
||||
const err = relayIfNspFails(ctx, args, "");
|
||||
const err = relayIfThtFails(ctx, args, "");
|
||||
if (err) return err;
|
||||
return textResult(`Domanda riscritta per la sessione ${session}.`);
|
||||
},
|
||||
@@ -516,7 +516,7 @@ export default function (pi) {
|
||||
const parts = args.trim().split(/\s+/).filter(Boolean);
|
||||
const sessionId =
|
||||
parts.length >= 2 ? parts[0] : parts.length === 1 && /^\d/.test(parts[0]) ? undefined : parts[0];
|
||||
const sid = sessionId ?? activeSessionId ?? process.env.NSP_SESSION;
|
||||
const sid = sessionId ?? activeSessionId ?? process.env.THT_SESSION;
|
||||
if (!sid) {
|
||||
await ctx.ui.notify("Uso: /torna <session_id> [N]", "warning");
|
||||
return;
|
||||
@@ -528,7 +528,7 @@ export default function (pi) {
|
||||
await ctx.ui.notify(`Target non valido (fase corrente ${cur}).`, "warning");
|
||||
return;
|
||||
}
|
||||
nsp(ctx, ["phase", "reopen", "--session", sid, "--phase", String(target)]);
|
||||
tht(ctx, ["phase", "reopen", "--session", sid, "--phase", String(target)]);
|
||||
await pi.sendUserMessage(
|
||||
`Ho riaperto la Fase ${target} della sessione ${sid}. Riprendi il protocollo da quella fase.`,
|
||||
{ deliverAs: "followUp" },
|
||||
Reference in New Issue
Block a user