mirror of
https://github.com/wassname/pi-plan.git
synced 2026-08-14 12:40:19 +08:00
sign-off ticks the goal [x] itself + document that the judge reads the working tree, not HEAD
Dogfood exit interview: agent bookkeeping is the drift point (tick after accept was the step most likely forgotten), and 'committed artifact' language implied the judge sees HEAD. Tick is exact-subject match via the existing GOAL_LINE regex; on drift the result explicitly asks the agent to tick, so neither path is silent. Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
This commit is contained in:
+33
-7
@@ -15,8 +15,9 @@
|
||||
*
|
||||
* The judge subsumes what v1 did in code: goal matching (no findGoal), evidence validation (a
|
||||
* placeholder gets rejected in words), verify execution, and format reading. The extension's only
|
||||
* write is appending a sign-off line to ## Log — the audit trail. The agent ticks [x] itself; a
|
||||
* hand-tick without a matching tool-written log line is visible in the diff.
|
||||
* writes are the sign-off: append a log line to ## Log (the audit trail) and tick the goal [x] when
|
||||
* an exact goal line matches (on drift the agent ticks, and the result says so). A hand-tick
|
||||
* without a matching tool-written log line is visible in the diff either way.
|
||||
*
|
||||
* Judge ran but failed/errored/timed out, or returned no VERDICT line => accepted_inconclusive: the
|
||||
* working agent is never blocked on judge infra; the log line says the judge ran but failed. There
|
||||
@@ -233,8 +234,21 @@ export default function piGoalsExtension(pi: ExtensionAPI): void {
|
||||
(task) => runJudge(task, judgeModel, ctx.cwd, signal),
|
||||
);
|
||||
if (outcome.logEntry) {
|
||||
writePlan(ctx, appendLog(readPlan(ctx), `${stamp()} ${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
|
||||
// falls to the agent and the result says so -- both paths are explicit, never silent.
|
||||
let updated = readPlan(ctx);
|
||||
let tickNote = "";
|
||||
if (outcome.logEntry.startsWith("signed off")) {
|
||||
const ticked = tickGoal(updated, params.goal);
|
||||
updated = ticked ?? updated;
|
||||
tickNote = ticked
|
||||
? `\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}`));
|
||||
updateWidget(ctx);
|
||||
return result(outcome.resultText + tickNote, outcome.isError);
|
||||
}
|
||||
return result(outcome.resultText, outcome.isError);
|
||||
},
|
||||
@@ -300,7 +314,7 @@ export async function decideSignOff(
|
||||
if (judge.error) {
|
||||
const partial = judge.output ? `\n\npartial judge output:\n${judge.output}` : "";
|
||||
return {
|
||||
resultText: `Judge ran but failed (${judge.error}). Accepted inconclusive — logged. Tick the goal [x] in ${PLAN_REL} yourself.${partial}`,
|
||||
resultText: `Judge ran but failed (${judge.error}). Accepted inconclusive — logged.${partial}`,
|
||||
isError: false,
|
||||
logEntry: `signed off "${input.goal}" (judge inconclusive: ran but failed: ${oneLine(judge.error)})`,
|
||||
};
|
||||
@@ -312,7 +326,7 @@ export async function decideSignOff(
|
||||
|
||||
if (verdict === "accept") {
|
||||
return {
|
||||
resultText: `Sign-off ACCEPTED. Tick the goal [x] in ${PLAN_REL} (the log line is already appended).\n\n--- judge ---\n${reasoning}`,
|
||||
resultText: `Sign-off ACCEPTED (log line appended).\n\n--- judge ---\n${reasoning}`,
|
||||
isError: false,
|
||||
logEntry: `signed off "${input.goal}" (judge accept)`,
|
||||
};
|
||||
@@ -327,13 +341,25 @@ export async function decideSignOff(
|
||||
}
|
||||
// No VERDICT line: same fail-forward as a judge error -- the judge ran but didn't answer.
|
||||
return {
|
||||
resultText: `Judge returned no VERDICT line. Accepted inconclusive — logged. Tick the goal [x] in ${PLAN_REL} yourself.\n\n--- judge ---\n${reasoning || "(no output)"}`,
|
||||
resultText: `Judge returned no VERDICT line. Accepted inconclusive — logged.\n\n--- judge ---\n${reasoning || "(no output)"}`,
|
||||
isError: false,
|
||||
logEntry: `signed off "${input.goal}" (judge inconclusive: no VERDICT line)`,
|
||||
};
|
||||
}
|
||||
|
||||
/** Append one line under ## Log (creating the section at EOF if absent). The extension's only write. */
|
||||
/** Tick the goal line whose subject exactly matches `goal` (trimmed, case-insensitive) to [x].
|
||||
* Null when there is no unique exact match (wording drift / duplicates) -- the caller then asks the
|
||||
* agent to tick it itself. Reuses GOAL_LINE; deliberately NOT fuzzy, that's the judge's job. */
|
||||
export function tickGoal(plan: string, goal: string): string | null {
|
||||
const lines = plan.split("\n");
|
||||
const want = goal.trim().toLowerCase();
|
||||
const hits = lines.flatMap((l, i) => (GOAL_LINE.exec(l)?.[2].trim().toLowerCase() === want ? [i] : []));
|
||||
if (hits.length !== 1) return null;
|
||||
lines[hits[0]] = lines[hits[0]].replace(/\[[ xX/-]\]/, "[x]");
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
/** Append one line under ## Log (creating the section at EOF if absent). */
|
||||
export function appendLog(text: string, entry: string): string {
|
||||
const lines = text.split("\n");
|
||||
const line = `- ${entry}`;
|
||||
|
||||
+5
-3
@@ -96,9 +96,11 @@ export const completeGoalDescription =
|
||||
"table plus how to read it, a metric plus what it shows -- not a bare claim). The read must show " +
|
||||
"success POSITIVELY happened, not just that failures were avoided. Then call this with the goal's " +
|
||||
"text (the line after 'goal:'; small wording drift is fine). A fresh read-only judge inspects the " +
|
||||
"repo, runs the goal's verify command if it names one, and returns accept or reject with what's " +
|
||||
"missing. On accept (or if the judge itself is unavailable), a sign-off line is appended to ## Log " +
|
||||
"and YOU tick the goal [x]. On reject the goal stays open.";
|
||||
"LIVE WORKING TREE (uncommitted changes included; committing first is for durability, not " +
|
||||
"visibility), runs the goal's verify command if it names one, and returns accept or reject with " +
|
||||
"what's missing. On accept (or if the judge itself failed), a sign-off line is appended to ## Log " +
|
||||
"and the goal is ticked [x] for you; the result says if you must tick it yourself. On reject the " +
|
||||
"goal stays open.";
|
||||
|
||||
export const completeGoalParamDescription = "The goal's text: the line after 'goal:' in the plan file.";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user