refactor(disambiguation): extract F3 rewrite path (#26)

This commit is contained in:
2026-08-24 01:44:40 +02:00
parent eccf6212f1
commit 2ed55ef131
4 changed files with 247 additions and 61 deletions
+22 -57
View File
@@ -45,6 +45,7 @@ import {
appendLedgerSection,
} from "./gate/enrich.js";
import { createMemoryGate } from "./gate/memory/index.js";
import { createDisambiguationGate } from "./gate/disambiguation/index.js";
import { isReserved } from "./reserved-labels.mjs";
// Load the workflow contract once when Pi loads the extension. Asking the model to
@@ -619,6 +620,26 @@ export default function (pi) {
waitForReviewer: emitAndWait,
toTextResult: textResult,
});
const disambiguationGate = createDisambiguationGate({
workflow: {
activate: () => {
lockActive = true;
},
phase: (ctx, session) => ({ number: currentPhase(ctx, session) }),
advance: advancePhaseAndFinalize,
},
session: {
mutate: (ctx, args, recovery) => relayIfThtFails(
ctx, ["session", ...args], recovery,
),
},
ledger: {
record: (ctx, session, decision, recovery) => relayIfThtFails(
ctx, decisionAddArgs(session, decision), recovery,
),
},
toTextResult: textResult,
});
// 1) ANTI-BYPASS tool_call hook (spec D4, verbatim). Blocks direct phase/decision
// calls and writes to protected files so the reviewer cannot bypass the gate.
@@ -1580,63 +1601,7 @@ export default function (pi) {
}
memoryGate.install(pi);
pi.registerTool({
name: "rewrite_question",
label: "Riscrittura domanda e chiusura F3 (deterministica)",
description:
"In Fase 3 scrive deterministicamente question.md, registra question_rewritten e " +
"chiude la fase senza chiedere conferma al reviewer. assumptions puo' essere array " +
"o stringa JSON.",
parameters: Type.Object({
session: Type.String(),
question: Type.String(),
assumptions: Type.Optional(Type.Any()),
}),
async execute(_id, params, _signal, _onUpdate, ctx) {
lockActive = true;
try {
const { session, question, assumptions } = params;
const curNum = currentPhase(ctx, session);
if (curNum !== 3) {
return textResult("La riscrittura automatica e' disponibile solo in Fase 3.");
}
let assumps = assumptions;
if (typeof assumps === "string") {
try {
assumps = JSON.parse(assumps);
} catch {
assumps = [assumps];
}
}
// `tht session set-question` takes the session id as a positional argument
// (the `session` command group uses positional ids, unlike phase/cte/decision
// which use --session).
const args = ["session", "set-question", session, "--question", question];
if (Array.isArray(assumps)) {
for (const a of assumps) args.push("--assumption", String(a));
}
const err = relayIfThtFails(ctx, args, "");
if (err) return err;
const decisionErr = relayIfThtFails(
ctx,
decisionAddArgs(session, {
type: "question_rewritten",
subject: "domanda",
detail: question,
}),
"",
);
if (decisionErr) return decisionErr;
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.`);
}
},
});
disambiguationGate.install(pi);
pi.registerTool({
name: "write_schema_linking",