persist judge transcript to .pi/judge/<stamp>.md and reference it from the sign-off log line

'Did the judge really re-run verify?' was unanswerable post-hoc; now every sign-off's full
judge output survives on disk.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
This commit is contained in:
wassname
2026-07-03 11:59:00 +08:00
co-authored by Claudypoo
parent 92d3f6d725
commit 924910e942
2 changed files with 18 additions and 6 deletions
+2
View File
@@ -96,6 +96,8 @@ committing before sign-off is for durable evidence, not for the judge's visibili
- accept: a sign-off line is appended to `## Log` and the goal is ticked `[x]` in the same write
(exact goal-line match only; on wording drift the result asks the agent to tick it). The
tool-written log line is the audit trail; a hand-tick without one shows in the diff.
- every run saves the judge's full transcript to `.pi/judge/<stamp>.md`, referenced from the log
line, so "did the judge really re-run verify?" stays answerable after the fact.
- reject: the goal stays open and the agent gets the missing list.
- judge ran but failed/errored/timed out, or returned no VERDICT line: accepted inconclusive,
logged as such. There is no pre-emptive "no model" path -- a null judgeModel just omits
+16 -6
View File
@@ -228,11 +228,21 @@ export default function piGoalsExtension(pi: ExtensionAPI): void {
// decideSignOff runs the judge and derives the outcome + the one log line. judgeModel is never
// checked pre-emptively: null just means pi's configured default runs (buildJudgeArgs omits
// --model), so accepted_inconclusive always means "the judge ran but failed", never "no model".
const outcome = await decideSignOff(
{ goal: params.goal, plan, judgeModel },
signal,
(task) => runJudge(task, judgeModel, ctx.cwd, signal),
);
let judgeRaw: JudgeResult | null = null;
const outcome = await decideSignOff({ goal: params.goal, plan, judgeModel }, signal, async (task) => {
judgeRaw = await runJudge(task, judgeModel, ctx.cwd, signal);
return judgeRaw;
});
// Persist the judge's full transcript so "did the judge really re-run verify?" is answerable
// after the fact (dogfood finding: with only the one log line, an accept is unauditable).
let transcriptNote = "";
if (judgeRaw !== null) {
const raw: JudgeResult = judgeRaw;
mkdirSync(join(ctx.cwd, ".pi", "judge"), { recursive: true });
const rel = `.pi/judge/${stamp().replace(/[: ]/g, "-")}.md`;
writeFileSync(join(ctx.cwd, rel), `goal: ${params.goal}\nmodel: ${judgeModel ?? "pi default"}\nerror: ${raw.error ?? "none"}\n\n${raw.output}\n`);
transcriptNote = ` (${rel})`;
}
if (outcome.logEntry) {
// Sign-off write: tick the goal [x] (exact-subject match; dogfood showed agent bookkeeping
// is the drift point) and append the audit log line, one write. On wording drift the tick
@@ -246,7 +256,7 @@ export default function piGoalsExtension(pi: ExtensionAPI): void {
? `\n\nGoal ticked [x] in ${PLAN_REL}.`
: `\n\nNo exact goal line matched your wording -- tick it [x] in ${PLAN_REL} yourself.`;
}
writePlan(ctx, appendLog(updated, `${stamp()} ${outcome.logEntry}`));
writePlan(ctx, appendLog(updated, `${stamp()} ${outcome.logEntry}${transcriptNote}`));
updateWidget(ctx);
return result(outcome.resultText + tickNote, outcome.isError);
}