fix: auto-approve phase 3 rewrite

This commit is contained in:
User
2026-07-14 13:51:39 +02:00
parent c7d586aaf7
commit 1b1554b353
5 changed files with 169 additions and 13 deletions
@@ -0,0 +1,83 @@
const test = require("node:test");
const assert = require("node:assert");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const { createFakePi } = require("./fake_pi_runtime.js");
globalThis.require = require;
const installGatePromise = import("../../tht-gate.js").then((m) => m.default);
function setupFakeTht(t, phase) {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "fake-tht-rewrite-"));
const log = path.join(dir, "calls.log");
fs.writeFileSync(log, "");
const bin = path.join(dir, "tht");
fs.writeFileSync(
bin,
`#!/bin/bash
echo "$@" >> "${log}"
case "$1 $2" in
"phase show") echo "Fase corrente: ${phase}" ;;
"phase meta") echo '{"max_phase":8,"phases":[{"num":3,"id":"F3","emits":["question_rewritten"]}]}' ;;
*) echo "OK" ;;
esac
`,
{ mode: 0o755 },
);
const oldPath = process.env.PATH;
process.env.PATH = `${dir}:${oldPath}`;
t.after(() => {
process.env.PATH = oldPath;
fs.rmSync(dir, { recursive: true, force: true });
});
return { dir, calls: () => fs.readFileSync(log, "utf8").trim().split("\n").filter(Boolean) };
}
test("rewrite question completes F3 without a reviewer widget", async (t) => {
const fake = setupFakeTht(t, 3);
const installGate = await installGatePromise;
const { pi, ctx, tools } = createFakePi();
ctx.cwd = fake.dir;
installGate(pi);
const { def } = tools.get("rewrite_question");
const result = await def.execute(
"rewrite-1",
{ session: "s1", question: "pazienti con ablazione", assumptions: ["anno 2025"] },
null,
null,
ctx,
);
assert.deepEqual(fake.calls(), [
"phase show --session s1",
"session set-question s1 --question pazienti con ablazione --assumption anno 2025",
"decision add --session s1 --type question_rewritten --subject domanda --detail pazienti con ablazione",
"phase advance --session s1",
"phase meta --json",
]);
assert.equal(ctx.uiCalls.length, 0);
assert.match(result.content[0].text, /Fase 3 completata/);
});
test("rewrite question refuses calls outside F3", async (t) => {
const fake = setupFakeTht(t, 4);
const installGate = await installGatePromise;
const { pi, ctx, tools } = createFakePi();
ctx.cwd = fake.dir;
installGate(pi);
const { def } = tools.get("rewrite_question");
const result = await def.execute(
"rewrite-2",
{ session: "s1", question: "pazienti con ablazione" },
null,
null,
ctx,
);
assert.deepEqual(fake.calls(), ["phase show --session s1"]);
assert.match(result.content[0].text, /solo in Fase 3/);
});
+21 -4
View File
@@ -1266,10 +1266,11 @@ export default function (pi) {
pi.registerTool({
name: "rewrite_question",
label: "Riscrittura domanda (deterministica)",
label: "Riscrittura domanda e chiusura F3 (deterministica)",
description:
"Scrive deterministicamente question.md via tht session set-question (evita il tool " +
"di edit unreliable). assumptions puo' essere array o stringa JSON.",
"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(),
@@ -1278,6 +1279,10 @@ export default function (pi) {
async execute(_id, params, _signal, _onUpdate, ctx) {
lockActive = true;
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 {
@@ -1295,7 +1300,19 @@ export default function (pi) {
}
const err = relayIfThtFails(ctx, args, "");
if (err) return err;
return textResult(`Domanda riscritta per la sessione ${session}.`);
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}).`);
},
});
+6 -9
View File
@@ -243,15 +243,12 @@ Phase 3 (CLI exit 5).
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:false, allow_other:true)`. The
"Confirm rewriting" option is `recommended:true` with
`{type:"question_rewritten", subject:"domanda", detail:"<full rewritten question>"}`.
3. **Order matters:** (a) the `reviewer_decide` records `question_rewritten` → (b) call the
gate's `rewrite_question` tool, which runs `tht session set-question` to write
`question.md` (regenerates question + an "## Assunzioni" section; never edit it by hand)
→ (c) close the phase with `reviewer_confirm kind:"phase"`. F3 does NOT auto-advance:
the `question_rewritten` decision alone does not move the phase.
4. "Altro" iterates (re-propose a new `reviewer_decide`). "Torna indietro" reopens F1.
2. Call `rewrite_question` once with the completed rewritten question and assumptions. The
gate writes `question.md` (including `## Assunzioni`), records `question_rewritten`, and
closes F3 automatically.
3. Do **not** call `reviewer_decide` or `reviewer_confirm` in F3: the rewrite is assumed
approved. Continue at F4 only after `rewrite_question` reports success. To revise the
rewrite, use "Torna indietro" to reopen F1.
## Phase 4 — Schema linking