pi-goal-list-loop-audit 0.28.25 → 0.28.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -176,6 +176,15 @@ export interface Goal {
|
|
|
176
176
|
* At 3 the goal pauses loudly — a broken auditor model must not spin a
|
|
177
177
|
* silent retry-forever loop. Cleared on any real auditor run. */
|
|
178
178
|
auditInfraStreak?: number;
|
|
179
|
+
/** v0.28.26: the completion claim captured when an audit attempt is
|
|
180
|
+
* quota-blocked. The quota retry re-runs the AUDITOR directly with this
|
|
181
|
+
* stored claim instead of re-engaging the agent — re-engaging produced a
|
|
182
|
+
* hallucinated-closure repetition loop in the field (π-games 2026-07-29:
|
|
183
|
+
* the agent concluded the goal was closed, stopped calling complete_goal,
|
|
184
|
+
* and repeated the same essay until the stall brake fired). Cleared when
|
|
185
|
+
* the retry resolves. Only consumed while paused with an "auditor quota:"
|
|
186
|
+
* reason, so a stale value is unreachable by construction. */
|
|
187
|
+
pendingCompletion?: { completionSummary?: string; verificationSummary?: string; at: string };
|
|
179
188
|
/** v0.25.0 (contract item 22): auditor objections extracted as TODOs when
|
|
180
189
|
* aggressiveMode keeps the goal active past the disapproval cap. Rendered
|
|
181
190
|
* into every continuation prompt until the next audit clears them. */
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -947,6 +947,146 @@ function archiveCurrentGoal(ctx: ExtensionContext, status: Status, stopReason?:
|
|
|
947
947
|
}
|
|
948
948
|
}
|
|
949
949
|
|
|
950
|
+
/**
|
|
951
|
+
* v0.28.26: quota-window retry for a STORED completion claim. The auditor
|
|
952
|
+
* was quota-blocked at complete_goal time; the claim (completionSummary +
|
|
953
|
+
* verificationSummary) was persisted on the goal, and when the quota window
|
|
954
|
+
* elapses we re-run the AUDITOR directly — no agent turn. Re-engaging the
|
|
955
|
+
* agent to re-submit an unchanged claim produced a hallucinated-closure
|
|
956
|
+
* repetition loop in the field (π-games: the model concluded the goal was
|
|
957
|
+
* closed, repeated the same essay 4×+, stormed continuations, compacted 14×
|
|
958
|
+
* in 35 minutes, and burned the stall brake).
|
|
959
|
+
*
|
|
960
|
+
* Outcomes: approved → close + cascade (archiveCurrentGoal handles list
|
|
961
|
+
* advance + reviewer); quota again → re-pause with a fresh scheduled retry
|
|
962
|
+
* (claim preserved); anything else (disapproved, impossible, non-quota
|
|
963
|
+
* infra) → hand back to the agent: resume active + continuation, verdict
|
|
964
|
+
* durable in auditHistory.
|
|
965
|
+
*/
|
|
966
|
+
async function retryStoredCompletionAudit(ctx: ExtensionContext): Promise<void> {
|
|
967
|
+
const goal = state.goal;
|
|
968
|
+
if (!goal?.pendingCompletion) return;
|
|
969
|
+
if (completionAuditInFlight) return;
|
|
970
|
+
const liveCtx = freshCtx() ?? ctx;
|
|
971
|
+
const claim = goal.pendingCompletion;
|
|
972
|
+
updateGoal({ status: "auditing" }, liveCtx);
|
|
973
|
+
appendLedger(liveCtx.cwd, "goal_resumed", { via: "quota-retry-direct-audit" });
|
|
974
|
+
liveCtx.ui.notify("Auditor quota window elapsed — retrying the audit with your stored completion claim (no agent turn needed).", "info");
|
|
975
|
+
const settings = loadSettings(liveCtx.cwd);
|
|
976
|
+
const { model: auditorModel, error: modelError, via } = resolveAuditorModel(liveCtx, settings.auditorModel);
|
|
977
|
+
if (modelError) liveCtx.ui.notify(`Auditor model issue: ${modelError}`, "warning");
|
|
978
|
+
latestAuditProgress = { label: "quota-retry", lastEventAt: Date.now() };
|
|
979
|
+
completionAuditInFlight = true;
|
|
980
|
+
const auditStartMs = Date.now();
|
|
981
|
+
let result: Awaited<ReturnType<typeof runGoalCompletionAuditor>>;
|
|
982
|
+
try {
|
|
983
|
+
({ result } = await runWithInfraRetry(
|
|
984
|
+
() =>
|
|
985
|
+
runGoalCompletionAuditor({
|
|
986
|
+
ctx: liveCtx,
|
|
987
|
+
goal: state.goal!,
|
|
988
|
+
completionSummary: claim.completionSummary,
|
|
989
|
+
verificationSummary: claim.verificationSummary,
|
|
990
|
+
model: auditorModel,
|
|
991
|
+
thinkingLevel: settings.auditorThinkingLevel ?? getSessionThinkingLevel(),
|
|
992
|
+
onProgress: (progress) => {
|
|
993
|
+
latestAuditProgress = { currentTool: progress.currentTool, label: progress.label, elapsedMs: progress.elapsedMs, lastEventAt: Date.now() };
|
|
994
|
+
refreshUI(liveCtx);
|
|
995
|
+
},
|
|
996
|
+
}),
|
|
997
|
+
{ onRetry: (err) => appendLedger(liveCtx.cwd, "audit_infra_retry", { goalId: state.goal?.id, error: err.slice(0, 200) }) },
|
|
998
|
+
));
|
|
999
|
+
} finally {
|
|
1000
|
+
completionAuditInFlight = false;
|
|
1001
|
+
latestAuditProgress = null;
|
|
1002
|
+
}
|
|
1003
|
+
if (!state.goal) return; // aborted mid-audit
|
|
1004
|
+
|
|
1005
|
+
// Record the run in history (same compact shape as the tool path).
|
|
1006
|
+
const auditorRan = result.output.trim().length > 0;
|
|
1007
|
+
const history = state.goal.auditHistory ?? [];
|
|
1008
|
+
if (auditorRan) {
|
|
1009
|
+
result.output = stripThinkBlocks(result.output);
|
|
1010
|
+
history.push({
|
|
1011
|
+
at: nowIso(),
|
|
1012
|
+
approved: result.approved,
|
|
1013
|
+
disapproved: result.disapproved,
|
|
1014
|
+
impossible: result.impossible,
|
|
1015
|
+
impossibleReason: result.impossibleReason,
|
|
1016
|
+
model: result.model,
|
|
1017
|
+
thinkingLevel: result.thinkingLevel,
|
|
1018
|
+
report: result.output,
|
|
1019
|
+
error: result.error,
|
|
1020
|
+
regressionShieldPassed: result.regressionShieldPassed,
|
|
1021
|
+
regressionShieldMissing: result.regressionShieldMissing,
|
|
1022
|
+
durationMs: Date.now() - auditStartMs,
|
|
1023
|
+
} as any);
|
|
1024
|
+
if (history.length > 20) history.splice(0, history.length - 20);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
if (result.approved) {
|
|
1028
|
+
updateGoal({ auditHistory: history, pendingCompletion: undefined }, liveCtx);
|
|
1029
|
+
const objective = state.goal.objective;
|
|
1030
|
+
archiveCurrentGoal(liveCtx, "complete", `auditor ${result.model} approved (quota-retry)`);
|
|
1031
|
+
liveCtx.ui.notify(`Goal complete — auditor ${result.model} approved on the quota retry.`, "info");
|
|
1032
|
+
notifyExternal(liveCtx, `Goal complete (auditor approved on quota-retry): ${objective.slice(0, 120)}`);
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
if (result.error && !result.disapproved && isQuotaError(result.error)) {
|
|
1037
|
+
// Still quota'd — re-pause with a fresh window, claim preserved.
|
|
1038
|
+
const settingsNow = loadSettings(liveCtx.cwd);
|
|
1039
|
+
const defaultSec = (settingsNow.quotaRetryMinutes ?? DEFAULT_QUOTA_RETRY_MINUTES) * 60;
|
|
1040
|
+
const quota = parseQuotaError(result.error, defaultSec);
|
|
1041
|
+
const retryMin = Math.max(1, Math.round(quota.retryAfterSec / 60));
|
|
1042
|
+
updateGoal({
|
|
1043
|
+
status: "paused",
|
|
1044
|
+
auditHistory: history,
|
|
1045
|
+
pauseKind: "wait",
|
|
1046
|
+
pauseResumeAt: new Date(Date.now() + quota.retryAfterSec * 1000).toISOString(),
|
|
1047
|
+
pauseReason: `auditor quota: ${result.error}`,
|
|
1048
|
+
pauseSuggestedAction: `Quota auto-retry in ${retryMin}m — or /goal resume to retry now`,
|
|
1049
|
+
}, liveCtx);
|
|
1050
|
+
appendLedger(liveCtx.cwd, "goal_paused", { reason: `auditor quota: retry in ${quota.retryAfterSec}s (stored-claim retry)` });
|
|
1051
|
+
liveCtx.ui.notify(`Auditor still quota-limited — next auto-retry in ${retryMin}m (your completion claim is stored; no action needed).`, "warning");
|
|
1052
|
+
scheduleQuotaRetry(liveCtx, quota.retryAfterSec, result.error, () => {
|
|
1053
|
+
if (state.goal && state.goal.status === "paused" && (state.goal.pauseReason ?? "").startsWith("auditor quota:") && state.goal.pendingCompletion) {
|
|
1054
|
+
void retryStoredCompletionAudit(liveCtx);
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// Any other outcome — disapproved, impossible, non-quota infra — belongs
|
|
1061
|
+
// to the agent: resume and let the continuation drive the next step. The
|
|
1062
|
+
// verdict is durable in auditHistory + /goal status.
|
|
1063
|
+
updateGoal({
|
|
1064
|
+
status: "active",
|
|
1065
|
+
auditHistory: history,
|
|
1066
|
+
pendingCompletion: undefined,
|
|
1067
|
+
pauseReason: result.disapproved
|
|
1068
|
+
? `auditor disapproved on quota-retry — see /goal status`
|
|
1069
|
+
: result.impossible
|
|
1070
|
+
? `auditor verdict: IMPOSSIBLE on quota-retry — ${(result.impossibleReason ?? "").slice(0, 120)}`
|
|
1071
|
+
: `auditor infrastructure error on quota-retry: ${(result.error ?? "").slice(0, 120)}`,
|
|
1072
|
+
}, liveCtx);
|
|
1073
|
+
liveCtx.ui.notify(
|
|
1074
|
+
result.disapproved
|
|
1075
|
+
? `Auditor (quota-retry) DISAPPROVED — resuming; the report is in /goal status.`
|
|
1076
|
+
: result.impossible
|
|
1077
|
+
? `Auditor (quota-retry): goal IMPOSSIBLE — ${(result.impossibleReason ?? "").slice(0, 100)}. Resuming; consider /goal tweak.`
|
|
1078
|
+
: `Auditor (quota-retry) hit an infrastructure error — resuming; re-call complete_goal when ready.`,
|
|
1079
|
+
"warning",
|
|
1080
|
+
);
|
|
1081
|
+
appendLedger(liveCtx.cwd, "quota_retry_audit_verdict", {
|
|
1082
|
+
approved: false,
|
|
1083
|
+
disapproved: result.disapproved,
|
|
1084
|
+
impossible: result.impossible,
|
|
1085
|
+
error: result.error?.slice(0, 160),
|
|
1086
|
+
});
|
|
1087
|
+
scheduleContinuation(liveCtx, true);
|
|
1088
|
+
}
|
|
1089
|
+
|
|
950
1090
|
/**
|
|
951
1091
|
* v0.26.0: bind the reviewer to the live session. Sources for finding
|
|
952
1092
|
* extraction: the archived goal markdown + its audit reports + the
|
|
@@ -2574,6 +2714,9 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
2574
2714
|
status: "paused",
|
|
2575
2715
|
auditHistory: history,
|
|
2576
2716
|
auditInfraStreak: undefined, // quota reached the auditor — infra streak broken
|
|
2717
|
+
// v0.28.26: store the claim — the quota retry re-runs the
|
|
2718
|
+
// auditor DIRECTLY with it (no agent turn to confuse).
|
|
2719
|
+
pendingCompletion: { completionSummary: p.completionSummary, verificationSummary: p.verificationSummary, at: nowIso() },
|
|
2577
2720
|
pauseKind: "wait",
|
|
2578
2721
|
pauseResumeAt: new Date(Date.now() + quota.retryAfterSec * 1000).toISOString(),
|
|
2579
2722
|
pauseReason: `auditor quota: ${result.error}`,
|
|
@@ -2584,6 +2727,13 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
2584
2727
|
// Re-check: only auto-resume if STILL paused for the quota
|
|
2585
2728
|
// reason (a user /goal pause during the window is not stomped).
|
|
2586
2729
|
if (state.goal && state.goal.status === "paused" && (state.goal.pauseReason ?? "").startsWith("auditor quota:")) {
|
|
2730
|
+
// v0.28.26: a stored claim retries the AUDITOR directly — the
|
|
2731
|
+
// agent is not needed to re-submit an unchanged claim, and
|
|
2732
|
+
// re-engaging it produced hallucinated-closure loops.
|
|
2733
|
+
if (state.goal.pendingCompletion) {
|
|
2734
|
+
void retryStoredCompletionAudit(ctx);
|
|
2735
|
+
return;
|
|
2736
|
+
}
|
|
2587
2737
|
updateGoal({ status: "active" }, ctx);
|
|
2588
2738
|
appendLedger(ctx.cwd, "goal_resumed", { via: "quota-retry" });
|
|
2589
2739
|
if (resolveEffectiveAggressiveSettings(loadSettings(ctx.cwd)).aggressiveMode) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.28.
|
|
3
|
+
"version": "0.28.26",
|
|
4
4
|
"description": "Goal. Loop. Audit. Done. \u2014 a pi-coding-agent extension that supervises long-running work, with isolated auditor on each completion. Beat bamboozling by design: the auditor runs in a fresh session with no extensions, no skills, no editor \u2014 only the read tools needed to verify your goal.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "dracon",
|
package/schemas/goal.schema.json
CHANGED
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"interruptedAt": { "type": "string" },
|
|
54
54
|
"interruptedReason": { "type": "string" },
|
|
55
55
|
"auditInfraStreak": { "type": "number" },
|
|
56
|
+
"pendingCompletion": { "type": "object" },
|
|
56
57
|
"activePath": { "type": "string" },
|
|
57
58
|
"archivedPath": { "type": "string" },
|
|
58
59
|
"usage": {
|