From 075883370e2c634f75bdde687d15a920aadeece9 Mon Sep 17 00:00:00 2001 From: mptyl Date: Mon, 20 Jul 2026 01:26:27 +0200 Subject: [PATCH] =?UTF-8?q?fix(harness):=20close=20the=20Pi-crash=20class?= =?UTF-8?q?=20=E2=80=94=20top-level=20try/catch=20on=20ALL=20gate=20tools?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audit finding 1.1 (high): reviewer_memory_promote, rewrite_question, write_schema_linking, write_cte_sql and write_final_sql still ran execute without a top-level catch — the same unhandled-rejection class that killed Pi in reviewer_schema_linking (fixed in 87cb806 for the four reviewer_* tools). All 9 registered tools now share the pattern: any uncaught throw becomes a textResult the model can react to. Co-Authored-By: Claude Fable 5 --- harness/.pi/extensions/tht-gate.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/harness/.pi/extensions/tht-gate.js b/harness/.pi/extensions/tht-gate.js index 9a77350c..b07044ad 100644 --- a/harness/.pi/extensions/tht-gate.js +++ b/harness/.pi/extensions/tht-gate.js @@ -1339,6 +1339,7 @@ export default function (pi) { }), async execute(_id, params, _signal, _onUpdate, ctx) { lockActive = true; + try { const { session } = params; const curNum = currentPhase(ctx, session); const phase = phaseId(ctx, curNum); @@ -1410,6 +1411,10 @@ export default function (pi) { const closed = closeAfterPromotion(ctx, session, curNum, summary); if (closed) return closed; return textResult(summary); + } catch (fatal) { + const msg = (fatal.stderr || fatal.message || String(fatal)).toString().trim(); + return textResult(`[reviewer_memory_promote ERRORE INTERNO] ${msg}. Riprova o usa un approccio diverso.`); + } }, }); @@ -1427,6 +1432,7 @@ export default function (pi) { }), async execute(_id, params, _signal, _onUpdate, ctx) { lockActive = true; + try { const { session, question, assumptions } = params; const curNum = currentPhase(ctx, session); if (curNum !== 3) { @@ -1462,6 +1468,10 @@ export default function (pi) { const advanced = advancePhaseAndFinalize(ctx, session, curNum); if (advanced.err) return advanced.err; return textResult(`Domanda riscritta e Fase 3 completata (sessione ${session}).`); + } catch (fatal) { + const msg = (fatal.stderr || fatal.message || String(fatal)).toString().trim(); + return textResult(`[rewrite_question ERRORE INTERNO] ${msg}. Riprova o usa un approccio diverso.`); + } }, }); @@ -1480,6 +1490,7 @@ export default function (pi) { }), async execute(_id, params, _signal, _onUpdate, ctx) { lockActive = true; + try { const { session } = params; // schema_linking is Type.Any(): a stringified JSON object passes validation // but would be double-encoded here and rejected by the CLI. Normalize first. @@ -1497,6 +1508,10 @@ export default function (pi) { const cliMsg = (e.stderr || e.message || String(e)).toString().trim(); return textResult(`${cliMsg} Correggi schema_linking e riprova.`); } + } catch (fatal) { + const msg = (fatal.stderr || fatal.message || String(fatal)).toString().trim(); + return textResult(`[write_schema_linking ERRORE INTERNO] ${msg}. Riprova o usa un approccio diverso.`); + } }, }); @@ -1506,6 +1521,7 @@ export default function (pi) { description: "Persiste un blocco CTE tramite tht cte save; non scrivere mai file di sessione direttamente.", parameters: Type.Object({ session: Type.String(), name: Type.String(), sql: Type.String() }), async execute(_id, params, _signal, _onUpdate, ctx) { + try { const err = relayIfThtFails( ctx, ["cte", "save", "--session", params.session, "--name", params.name, "--file", "-"], @@ -1513,6 +1529,10 @@ export default function (pi) { params.sql, ); return err || textResult(`CTE ${params.name} salvato per la sessione ${params.session}.`); + } catch (fatal) { + const msg = (fatal.stderr || fatal.message || String(fatal)).toString().trim(); + return textResult(`[write_cte_sql ERRORE INTERNO] ${msg}. Riprova o usa un approccio diverso.`); + } }, }); @@ -1522,6 +1542,7 @@ export default function (pi) { description: "Persiste il SQL finale tramite tht sql set-final; non scrivere mai file di sessione direttamente.", parameters: Type.Object({ session: Type.String(), sql: Type.String() }), async execute(_id, params, _signal, _onUpdate, ctx) { + try { const err = relayIfThtFails( ctx, ["sql", "set-final", "--session", params.session, "--file", "-"], @@ -1529,6 +1550,10 @@ export default function (pi) { params.sql, ); return err || textResult(`SQL finale salvato per la sessione ${params.session}.`); + } catch (fatal) { + const msg = (fatal.stderr || fatal.message || String(fatal)).toString().trim(); + return textResult(`[write_final_sql ERRORE INTERNO] ${msg}. Riprova o usa un approccio diverso.`); + } }, });