From 20bc3de95009927fb43d2378be694cc17349c89d Mon Sep 17 00:00:00 2001 From: mptyl Date: Wed, 1 Jul 2026 16:49:18 +0200 Subject: [PATCH] feat(gate): write_schema_linking tool (validated F4 artifact via CLI) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tht() gains an optional stdin arg; the tool pipes the schema-linking object to 'tht session set-schema-linking --file -', which validates against SchemaLinking and returns the exact error on failure — so the model stops hand-writing the artifact and validating with ad-hoc python. Co-Authored-By: Claude Opus 4.8 --- harness/.pi/extensions/tht-gate.js | 36 ++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/harness/.pi/extensions/tht-gate.js b/harness/.pi/extensions/tht-gate.js index fed1490c..5ab8d41f 100644 --- a/harness/.pi/extensions/tht-gate.js +++ b/harness/.pi/extensions/tht-gate.js @@ -144,9 +144,9 @@ function loadEnvFromDotenv(ctx) { } // Single chokepoint for all CLI calls. cwd is the Pi project root (harness/). -function tht(ctx, args) { +function tht(ctx, args, input) { loadEnvFromDotenv(ctx); - return execFileSync("tht", args, { cwd: ctx.cwd, encoding: "utf8" }); + return execFileSync("tht", args, { cwd: ctx.cwd, encoding: "utf8", input }); } // Runs a privileged tht call; converts any failure into an actionable textResult @@ -701,6 +701,38 @@ export default function (pi) { }, }); + pi.registerTool({ + name: "write_schema_linking", + label: "Scrittura schema_linking.json (validata)", + description: + "Scrive deterministicamente schema_linking.json validandolo contro il modello " + + "SchemaLinking via tht session set-schema-linking (evita edit a mano e la " + + "validazione manuale). schema_linking e' l'oggetto JSON completo: {question, " + + "candidates:[{kind:'table'|'column', name, evidence?, decision?}], joins:[{from, " + + "to, source?}], excluded:[{kind, name}], open_questions:[], concept_formulas:[]}.", + parameters: Type.Object({ + session: Type.String(), + schema_linking: Type.Any(), + }), + async execute(_id, params, _signal, _onUpdate, ctx) { + lockActive = true; + const { session, schema_linking } = params; + try { + tht( + ctx, + ["session", "set-schema-linking", session, "--file", "-"], + JSON.stringify(schema_linking), + ); + return textResult( + `schema_linking.json scritto e validato per la sessione ${session}.`, + ); + } catch (e) { + const cliMsg = (e.stderr || e.message || String(e)).toString().trim(); + return textResult(`${cliMsg} Correggi schema_linking e riprova.`); + } + }, + }); + // --- slash command: /torna [session_id] [N] (rollback to a previous phase) -- pi.registerCommand("torna", { description: