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. */
@@ -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.25",
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",
@@ -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": {