pi-goal-list-loop-audit 0.29.7 → 0.29.9
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.
package/README.md
CHANGED
|
@@ -39,10 +39,11 @@ Five top-level commands — `/goal`, `/list`, `/loop`, `/glla`, `/review`:
|
|
|
39
39
|
/goal resume # resume
|
|
40
40
|
/goal cancel # abort
|
|
41
41
|
/goal decide # re-open the decision picker (v0.28.23)
|
|
42
|
-
/goal audit
|
|
42
|
+
/goal audit ["focus on payments"] # one-shot project audit (v0.29.8): fix the non-decisions, present the decisions — findings in .pi-glla/audit-loop/findings.md
|
|
43
|
+
/goal verify # run the isolated auditor on the current goal now — no agent turn (v0.28.27, renamed from /goal audit in v0.29.8)
|
|
43
44
|
/goal tweak "<new objective>" # edit in place (Confirm dialog)
|
|
44
45
|
/goal archive # archived goals, newest first
|
|
45
|
-
/glla # settings UI table · /glla key=value · /glla stats · /glla audits [N|full] · /glla postaudit · /glla wipe (nuclear reset, Confirm-gated) · /glla autoaccept=on
|
|
46
|
+
/glla # settings UI table · /glla status (unified what's-running view) · /glla key=value · /glla stats · /glla audits [N|full] · /glla postaudit · /glla wipe (nuclear reset, Confirm-gated) · /glla autoaccept=on
|
|
46
47
|
/list fix the login bug, add dark mode, write docs # dump it — the agent shapes it into items, one Confirm
|
|
47
48
|
/list plan.md # file detected → bulk import, one Confirm (sisyphus/Ralph style)
|
|
48
49
|
/list <paste a checklist> # multi-line paste → same batch flow
|
|
@@ -66,7 +67,7 @@ matches `/list show`.
|
|
|
66
67
|
/loop # draft the loop (agent grills; measure is test-run before you confirm)
|
|
67
68
|
/loop start "keep polishing the UI" # infinite metricless loop (v0.23.6): no plateau, no cap — ends at time=/tokens= or /loop stop
|
|
68
69
|
/loop respec # infinite metricless loop reconciling the codebase against the root SPEC.md / spec.md (v0.24.3) — 2 specs = you pick, 0 specs = drafting, 1 spec = auto-start (v0.24.4)
|
|
69
|
-
/loop audit # project-audit loop (v0.29.0): each iteration audits fresh, appends findings to .pi-glla/audit-loop/findings.md, fixes the top ones — the orchestrator counts open findings and the plateau stop ends it when the well is dry
|
|
70
|
+
/loop audit # project-audit loop (v0.29.0): each iteration audits fresh, appends findings to .pi-glla/audit-loop/findings.md, fixes the top ones — the orchestrator counts open findings and the plateau stop ends it when the well is dry (one-shot version: /goal audit)
|
|
70
71
|
/loop start "reduce TODOs" measure="grep -c TODO src.txt | head -1" direction=min
|
|
71
72
|
/loop start "shrink the bundle" measure="..." direction=min time=4 tokens=500000 # arbitrary bounds
|
|
72
73
|
/loop start "reduce TODOs" measure="..." direction=min branch=1 # scratch-branch mode
|
|
@@ -361,7 +362,7 @@ prompts/
|
|
|
361
362
|
goal-loop-forever-draft.md # /loop drafting prompt
|
|
362
363
|
scripts/
|
|
363
364
|
smoke.sh # live integration harness (tmux + real models)
|
|
364
|
-
tests/ #
|
|
365
|
+
tests/ # 614 tests across 58 files, no live pi required (mock-ctx harness drives the orchestrator)
|
|
365
366
|
docs/DESIGN.md # architectural decisions
|
|
366
367
|
PLAN.md # milestones, decisions, gates
|
|
367
368
|
```
|
|
@@ -9,6 +9,18 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
export const BACKOFF_HARD_CAP_MS = 5 * 60 * 1000;
|
|
12
|
+
|
|
13
|
+
// v0.29.9: ms until the next clock-hour boundary (+ grace). Coding-plan
|
|
14
|
+
// rate-limit windows typically expire at the top of the hour, so a probe
|
|
15
|
+
// scheduled there catches the reset within seconds instead of mid-window.
|
|
16
|
+
// Robust when the premise is wrong too: a non-clock-aligned window is
|
|
17
|
+
// still caught within the hour.
|
|
18
|
+
export function msUntilNextHourBoundary(nowMs: number, graceMs = 60_000): number {
|
|
19
|
+
const d = new Date(nowMs);
|
|
20
|
+
d.setMinutes(0, 0, 0);
|
|
21
|
+
d.setHours(d.getHours() + 1);
|
|
22
|
+
return d.getTime() + graceMs - nowMs;
|
|
23
|
+
}
|
|
12
24
|
export const BACKOFF_IDLE_RETRY_MS = 50; // when adding another iter to queue
|
|
13
25
|
export const BACKOFF_ERROR_BASE_MS = 5_000; // first error retry
|
|
14
26
|
export const BACKOFF_ERROR_MAX_MS = 60_000; // max error retry (separate from stuck cap)
|
|
@@ -216,10 +216,14 @@ export interface Goal {
|
|
|
216
216
|
export type GoalRoute =
|
|
217
217
|
| { kind: "draft" }
|
|
218
218
|
| { kind: "set"; text: string }
|
|
219
|
-
| { kind: "sub"; name: "status" | "pause" | "resume" | "cancel" | "decide" | "audit" | "tweak" | "archive" | "start"; rest: string };
|
|
219
|
+
| { kind: "sub"; name: "status" | "pause" | "resume" | "cancel" | "decide" | "verify" | "audit" | "tweak" | "archive" | "start"; rest: string };
|
|
220
220
|
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
// v0.29.8: "audit" moved to ARG subs ("/goal audit [focus]" is the one-shot
|
|
222
|
+
// project audit — user: "/goal audit IS the audit goal"); the v0.28.27
|
|
223
|
+
// manual current-goal verification moved to "verify" (it happens
|
|
224
|
+
// automatically at completion anyway — verify is the on-demand handle).
|
|
225
|
+
const GOAL_EXACT_SUBS = new Set(["status", "pause", "resume", "cancel", "decide", "verify"]);
|
|
226
|
+
const GOAL_ARG_SUBS = new Set(["audit", "tweak", "archive", "start"]);
|
|
223
227
|
|
|
224
228
|
export function routeGoalArgs(raw: string): GoalRoute {
|
|
225
229
|
const trimmed = raw.trim();
|
|
@@ -228,10 +232,10 @@ export function routeGoalArgs(raw: string): GoalRoute {
|
|
|
228
232
|
const first = (space === -1 ? trimmed : trimmed.slice(0, space)).toLowerCase();
|
|
229
233
|
const rest = space === -1 ? "" : trimmed.slice(space + 1).trim();
|
|
230
234
|
if (GOAL_EXACT_SUBS.has(first) && rest === "") {
|
|
231
|
-
return { kind: "sub", name: first as "status" | "pause" | "resume" | "cancel" | "decide", rest: "" };
|
|
235
|
+
return { kind: "sub", name: first as "status" | "pause" | "resume" | "cancel" | "decide" | "verify", rest: "" };
|
|
232
236
|
}
|
|
233
237
|
if (GOAL_ARG_SUBS.has(first)) {
|
|
234
|
-
return { kind: "sub", name: first as "tweak" | "archive" | "start", rest };
|
|
238
|
+
return { kind: "sub", name: first as "audit" | "tweak" | "archive" | "start", rest };
|
|
235
239
|
}
|
|
236
240
|
return { kind: "set", text: trimmed };
|
|
237
241
|
}
|
|
@@ -423,3 +423,22 @@ export function auditMeasureCmd(): string {
|
|
|
423
423
|
export function auditTarget(): string {
|
|
424
424
|
return `Audit the project for real problems and fix them, iteration by iteration. Every iteration: (1) run a FRESH audit pass over the codebase — spawn Explore subagents for breadth — hunting real issues: bugs, broken flows, regressions, drift between docs and code, dead code, security holes. Not style nits, not speculative refactors. (2) Append every NEW finding as one checkbox line "- [ ] SEVERITY: short description (file:line)" to ${AUDIT_FINDINGS_REL} (create the file on the first finding; append-only — never delete, rewrite, or reorder existing lines; never re-report a finding already listed). (3) Fix the highest-severity OPEN finding(s) — real fixes, committed — then check the box: "- [x] … — fixed in <commit>". (4) Honesty law: never fabricate findings to look busy; never mark a finding fixed without the fix commit existing. When a full audit pass surfaces nothing new AND no open findings remain, say so plainly — the orchestrator counts open findings every iteration and the plateau stop ends the loop when the well is dry.`;
|
|
425
425
|
}
|
|
426
|
+
|
|
427
|
+
// ---- /goal audit-project (v0.29.8) ----
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* The one-shot project audit (user design 2026-07-30): "/loop audit keeps
|
|
431
|
+
* firing — this would be fire and address what you can, present what is to
|
|
432
|
+
* be decided; whether to fix a bug is not a decision." Same findings file
|
|
433
|
+
* as the audit loop (one ledger per project — the loop can keep working
|
|
434
|
+
* what the one-shot surfaced), but exactly ONE pass with a finish line the
|
|
435
|
+
* isolated auditor verifies, and the triage law the loop doesn't have:
|
|
436
|
+
* FIX findings (bugs, polish — nobody would say "leave that bug in") are
|
|
437
|
+
* fixed autonomously; DECIDE findings (direction, trade-offs — two
|
|
438
|
+
* reasonable answers exist) are presented, never touched. DECIDE lines use
|
|
439
|
+
* "- [?]" so they never inflate the loop's open-findings measure.
|
|
440
|
+
*/
|
|
441
|
+
export function projectAuditTarget(focus?: string): string {
|
|
442
|
+
const scope = focus && focus.trim() ? focus.trim() : "the whole project";
|
|
443
|
+
return `Run ONE project audit pass and leave the project in a known state. Scope: ${scope}. (1) Run a FRESH audit pass over the codebase — spawn Explore subagents for breadth — hunting real problems: bugs, broken flows, regressions, drift between docs and code, dead code, security holes. Not style nits, not speculative refactors. (2) Append every NEW finding to ${AUDIT_FINDINGS_REL} (create the file on the first finding; append-only — never delete, rewrite, or reorder existing lines; never re-report a finding already listed), classified: "- [ ] FIX: SEVERITY: short description (file:line)" for bugs and polish — whether to fix these is NOT a decision — and "- [?] DECIDE: short description (what the choice is, what each side costs)" for direction, trade-offs, and scope questions where two reasonable answers exist. (3) Fix every NEW FIX finding from this pass — real fixes, committed with the repo's configured identity on the current branch (no invented identities or branches) — then check the box: "- [x] … — fixed in <commit>". (4) Change NOTHING for DECIDE findings — present them in the completion report instead. (5) Honesty law: never fabricate findings to look busy; never check a box without the fix commit existing; never silently turn a DECIDE into a fix. Done when: the audit pass is complete, every new FIX finding has a fix commit and a checked box in ${AUDIT_FINDINGS_REL}, and every DECIDE finding is listed in the file and presented in the completion report.`;
|
|
444
|
+
}
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -165,6 +165,7 @@ import {
|
|
|
165
165
|
respecTarget,
|
|
166
166
|
auditMeasureCmd,
|
|
167
167
|
auditTarget,
|
|
168
|
+
projectAuditTarget,
|
|
168
169
|
HELD_ON_RESTORE,
|
|
169
170
|
type LoopState,
|
|
170
171
|
} from "../goal-loop-forever.js";
|
|
@@ -182,6 +183,7 @@ import {
|
|
|
182
183
|
shouldWedgeAlert,
|
|
183
184
|
PENDING_LATCH_STUCK_MS,
|
|
184
185
|
shouldFirePendingLatchWatchdog,
|
|
186
|
+
msUntilNextHourBoundary,
|
|
185
187
|
} from "../goal-loop-backoff.js";
|
|
186
188
|
|
|
187
189
|
// =================================================================
|
|
@@ -1106,7 +1108,7 @@ async function retryStoredCompletionAudit(ctx: ExtensionContext, origin: "quota-
|
|
|
1106
1108
|
updateGoal({ status: "auditing" }, liveCtx);
|
|
1107
1109
|
appendLedger(liveCtx.cwd, "goal_resumed", { via: origin === "manual" ? "manual-audit" : "quota-retry-direct-audit" });
|
|
1108
1110
|
liveCtx.ui.notify(origin === "manual"
|
|
1109
|
-
? "Manual /goal
|
|
1111
|
+
? "Manual /goal verify — running the isolated auditor now (no agent turn needed)."
|
|
1110
1112
|
: "Auditor quota window elapsed — retrying the audit with your stored completion claim (no agent turn needed).", "info");
|
|
1111
1113
|
const settings = loadSettings(liveCtx.cwd);
|
|
1112
1114
|
const { model: auditorModel, error: modelError, via } = resolveAuditorModel(liveCtx, settings.auditorModel);
|
|
@@ -1164,7 +1166,7 @@ async function retryStoredCompletionAudit(ctx: ExtensionContext, origin: "quota-
|
|
|
1164
1166
|
updateGoal({ auditHistory: history, pendingCompletion: undefined }, liveCtx);
|
|
1165
1167
|
const objective = state.goal.objective;
|
|
1166
1168
|
archiveCurrentGoal(liveCtx, "complete", `auditor ${result.model} approved (${origin})`);
|
|
1167
|
-
liveCtx.ui.notify(`Goal complete — auditor ${result.model} approved${origin === "manual" ? " on /goal
|
|
1169
|
+
liveCtx.ui.notify(`Goal complete — auditor ${result.model} approved${origin === "manual" ? " on /goal verify" : " on the quota retry"}.`, "info");
|
|
1168
1170
|
notifyExternal(liveCtx, `Goal complete (auditor approved, ${origin}): ${objective.slice(0, 120)}`);
|
|
1169
1171
|
return;
|
|
1170
1172
|
}
|
|
@@ -1445,14 +1447,24 @@ async function cmdGoal(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
1445
1447
|
if (!shown) ctx.ui.notify("No pending decision — the goal isn't paused on a choice (or no UI).", "info");
|
|
1446
1448
|
return;
|
|
1447
1449
|
}
|
|
1448
|
-
// v0.
|
|
1450
|
+
// v0.29.8: /goal audit [focus] — the ONE-SHOT project audit (user:
|
|
1451
|
+
// "/goal audit IS the audit goal — we are not auditing the current
|
|
1452
|
+
// goal, that happens automatically"). Fire-and-address: one audit
|
|
1453
|
+
// pass, FIX findings fixed autonomously (a bug is not a decision),
|
|
1454
|
+
// DECIDE findings presented, untouched. Runs as a normal goal through
|
|
1455
|
+
// cmdSet — the isolated auditor verifies the finish line.
|
|
1456
|
+
if (route.name === "audit") {
|
|
1457
|
+
return cmdSet(projectAuditTarget(route.rest || undefined), ctx, true);
|
|
1458
|
+
}
|
|
1459
|
+
// v0.28.27 (renamed /goal audit → /goal verify in v0.29.8): run the
|
|
1460
|
+
// isolated auditor on the current goal
|
|
1449
1461
|
// RIGHT NOW, without engaging the agent. The user's "the work looks
|
|
1450
1462
|
// done — just verify it" handle (and the manual counterpart of the
|
|
1451
1463
|
// v0.28.26 stored-claim quota retry). Seeds a synthesized claim so a
|
|
1452
1464
|
// quota block falls into the same pendingCompletion retry machinery.
|
|
1453
|
-
if (route.name === "
|
|
1465
|
+
if (route.name === "verify") {
|
|
1454
1466
|
if (!state.goal) {
|
|
1455
|
-
ctx.ui.notify("No active goal — /goal
|
|
1467
|
+
ctx.ui.notify("No active goal — /goal verify needs a goal to verify.", "warning");
|
|
1456
1468
|
return;
|
|
1457
1469
|
}
|
|
1458
1470
|
if (completionAuditInFlight) {
|
|
@@ -1461,7 +1473,7 @@ async function cmdGoal(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
1461
1473
|
}
|
|
1462
1474
|
updateGoal({
|
|
1463
1475
|
pendingCompletion: {
|
|
1464
|
-
completionSummary: "Manual audit requested by the user via /goal
|
|
1476
|
+
completionSummary: "Manual audit requested by the user via /goal verify (no agent completion claim). Verify the objective against the repo directly.",
|
|
1465
1477
|
at: nowIso(),
|
|
1466
1478
|
},
|
|
1467
1479
|
}, ctx);
|
|
@@ -4551,6 +4563,37 @@ function cmdAudits(args: string, ctx: ExtensionContext): void {
|
|
|
4551
4563
|
ctx.ui.notify(`glla audits — last ${entries.length} verdict(s) in ${ctx.cwd}\n${formatAuditLog(entries)}`, "info");
|
|
4552
4564
|
}
|
|
4553
4565
|
|
|
4566
|
+
// v0.29.8: /glla status — the unified "what's running" surface (user: "we
|
|
4567
|
+
// need to type goal status [to check], so that command at least is missing
|
|
4568
|
+
// for checking on whatever active process we have"). Read-only aggregate of
|
|
4569
|
+
// the ONE state — goal, list queue, loop, pending decisions — with pointers
|
|
4570
|
+
// to the deep surfaces.
|
|
4571
|
+
function cmdGllaStatus(ctx: ExtensionContext): void {
|
|
4572
|
+
const lines: string[] = [];
|
|
4573
|
+
const g = state.goal;
|
|
4574
|
+
if (g) {
|
|
4575
|
+
const tok = (g.usage?.tokensUsed ?? 0) > 0 ? ` · ${g.usage!.tokensUsed} tok` : "";
|
|
4576
|
+
const audit = g.status === "auditing" ? " (auditor running…)" : "";
|
|
4577
|
+
const pause = g.status === "paused" && g.pauseReason ? ` — ${g.pauseReason.slice(0, 90)}` : "";
|
|
4578
|
+
lines.push(`goal [${g.policy}] ${g.status}${audit}${tok}: ${g.objective.slice(0, 90)}${pause}`);
|
|
4579
|
+
} else {
|
|
4580
|
+
lines.push("goal: none");
|
|
4581
|
+
}
|
|
4582
|
+
const q = listQueue();
|
|
4583
|
+
lines.push(`list: ${q.length === 0 ? "empty" : `${q.length} queued — head: ${(q[0]?.objective ?? "").slice(0, 70)}`}`);
|
|
4584
|
+
const l = state.loop;
|
|
4585
|
+
if (l) {
|
|
4586
|
+
lines.push(`loop: ${l.active ? "ACTIVE" : `held/stopped — ${l.stopReason ?? "n/a"}`} · iter ${l.iteration}/${l.maxIterations > 0 ? l.maxIterations : "∞"} · best ${l.bestValue ?? "n/a"} · stall ${l.stallCount} — ${l.target.slice(0, 60)}`);
|
|
4587
|
+
} else {
|
|
4588
|
+
lines.push("loop: none");
|
|
4589
|
+
}
|
|
4590
|
+
if (g?.status === "paused" && g.pauseKind === "decision" && g.pauseOptions?.length) {
|
|
4591
|
+
lines.push(`decision pending (${g.pauseOptions.length} options) — /goal decide`);
|
|
4592
|
+
}
|
|
4593
|
+
lines.push("deep: /goal status · /list · /loop status · /glla stats · /glla audits · /glla log");
|
|
4594
|
+
ctx.ui.notify(`glla status\n${lines.join("\n")}`, "info");
|
|
4595
|
+
}
|
|
4596
|
+
|
|
4554
4597
|
async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
|
|
4555
4598
|
// The plugin's ONE config surface — global by default, rarely opened.
|
|
4556
4599
|
// /glla show effective values + where each comes from
|
|
@@ -4573,6 +4616,11 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
4573
4616
|
cmdAudits(trimmed.slice("audits".length).trim(), ctx);
|
|
4574
4617
|
return;
|
|
4575
4618
|
}
|
|
4619
|
+
// v0.29.8: /glla status — the unified what's-running view.
|
|
4620
|
+
if (/^status\b/.test(trimmed)) {
|
|
4621
|
+
cmdGllaStatus(ctx);
|
|
4622
|
+
return;
|
|
4623
|
+
}
|
|
4576
4624
|
// v0.28.28: /glla log [N] — the raw event trail, human-readable. "Log it
|
|
4577
4625
|
// so we can look back and see where we are doing things wrong."
|
|
4578
4626
|
if (/^log\b/.test(trimmed)) {
|
|
@@ -4970,10 +5018,13 @@ export default function (pi: ExtensionAPI): void {
|
|
|
4970
5018
|
description: "Set/draft a goal, or /goal status|pause|resume|cancel|tweak <text>|archive|start <objective>. Objectives without a 'Done when:' clause are grilled into a contract first; include the clause or use /goal start to skip the interview and activate instantly.",
|
|
4971
5019
|
getArgumentCompletions: completions([
|
|
4972
5020
|
["start", "skip drafting — /goal start <objective> activates immediately"],
|
|
5021
|
+
["audit", "one-shot project audit goal: /goal audit [focus] — fix the non-decisions, present the decisions (v0.29.8)"],
|
|
5022
|
+
["verify", "run the isolated auditor on the current goal NOW (v0.28.27, renamed from /goal audit)"],
|
|
4973
5023
|
["status", "show the active goal and its task list"],
|
|
4974
5024
|
["pause", "pause the active goal"],
|
|
4975
5025
|
["resume", "resume a paused goal (and the list, when items are queued)"],
|
|
4976
5026
|
["cancel", "abort the active goal"],
|
|
5027
|
+
["decide", "re-open the decision picker for a decision pause"],
|
|
4977
5028
|
["tweak", "change the objective: /goal tweak <text>"],
|
|
4978
5029
|
["archive", "list archived goals"],
|
|
4979
5030
|
]),
|
|
@@ -4990,6 +5041,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
4990
5041
|
["autoresume=", "default: hold when a session is loaded, auto-resume on reload/fork; on: always auto-resume; off: never"],
|
|
4991
5042
|
["decisionpopup=", "on|off: decision pauses pop the select() picker (default on; the widget card always lists the options, /goal decide reopens the picker)"],
|
|
4992
5043
|
["auditcap=", "N: pause goal after N consecutive auditor disapprovals (default 5, 0 = unlimited)"],
|
|
5044
|
+
["status", "unified what's-running view: goal + list queue + loop + pending decisions (v0.29.8)"],
|
|
4993
5045
|
["log", "event-trail tail: /glla log [N] — who created/resumed/paused what, from where (v0.28.28)"],
|
|
4994
5046
|
["wipe", "WIPE live glla state (goal archived, list cleared, loop stopped) — one-shot cleanup for leftover-laden projects"],
|
|
4995
5047
|
["resume", "resume WHATEVER is paused/held (goal, list item, or held loop) — no need to know the type"],
|
|
@@ -5489,17 +5541,40 @@ export default function (pi: ExtensionAPI): void {
|
|
|
5489
5541
|
// v0.29.1: brake-cycle CAP. The v0.28.25 ladder slows the thrash
|
|
5490
5542
|
// (1m→16m) but never STOPS it — junk-runner/hellhunter/pully each
|
|
5491
5543
|
// burned 4+ pause↔retry cycles against provider windows that last
|
|
5492
|
-
// hours. After 6 consecutive brakes: park
|
|
5544
|
+
// hours. After 6 consecutive brakes: park. v0.29.9: the park keeps
|
|
5545
|
+
// probing at the top of each hour (clock-aligned window resets).
|
|
5493
5546
|
if (errorBrakeStreak >= 6) {
|
|
5547
|
+
// v0.29.9: park — but keep probing at the top of each hour
|
|
5548
|
+
// (user: "simply adding an hourly retry … just to pick up work
|
|
5549
|
+
// faster assuming the retry expired"). Coding-plan rate-limit
|
|
5550
|
+
// windows typically expire on clock-hour boundaries, so a probe
|
|
5551
|
+
// scheduled for :01 catches the reset within seconds. One dunk
|
|
5552
|
+
// per hour, free (429s are rejected pre-billing); a successful
|
|
5553
|
+
// probe resets the whole error cycle. If the wall is something
|
|
5554
|
+
// else (auth, outage), the hourly probe is a harmless failed
|
|
5555
|
+
// resume attempt that re-parks via the same brake.
|
|
5494
5556
|
updateGoal({
|
|
5495
5557
|
status: "paused",
|
|
5496
5558
|
pauseKind: "error",
|
|
5497
5559
|
pauseReason: `${reason} — 6 error-brakes in a row; the provider has been erroring for an extended window`,
|
|
5498
|
-
pauseSuggestedAction: "
|
|
5560
|
+
pauseSuggestedAction: "Probing at the top of each hour — rate-limit windows typically expire on clock-hour boundaries. /goal resume retries now.",
|
|
5499
5561
|
}, ctx);
|
|
5500
|
-
ctx.ui.notify(`${goalNoun()} parked: ${reason} — 6 brakes in a row
|
|
5501
|
-
notifyExternal(ctx, `${goalNoun()} parked: provider erroring across 6 error-brake cycles.`);
|
|
5562
|
+
ctx.ui.notify(`${goalNoun()} parked: ${reason} — 6 brakes in a row. Hourly top-of-hour probes will pick work back up when the window opens; /goal resume retries now.`, "warning");
|
|
5563
|
+
notifyExternal(ctx, `${goalNoun()} parked: provider erroring across 6 error-brake cycles — hourly top-of-hour probes scheduled.`);
|
|
5502
5564
|
appendLedger(ctx.cwd, "error_brake_capped", { streak: errorBrakeStreak, reason });
|
|
5565
|
+
const probeMs = msUntilNextHourBoundary(Date.now());
|
|
5566
|
+
scheduleQuotaRetry(ctx, probeMs / 1000, reason, () => {
|
|
5567
|
+
// Re-check: only probe if STILL parked by the error-brake cap —
|
|
5568
|
+
// a user pause/resume/cancel meanwhile is never stomped.
|
|
5569
|
+
if (state.goal && state.goal.status === "paused" && state.goal.pauseKind === "error"
|
|
5570
|
+
&& (state.goal.pauseReason ?? "").includes("error-brakes in a row")) {
|
|
5571
|
+
appendLedger(ctx.cwd, "hourly_rate_probe", { goalId: state.goal.id, streak: errorBrakeStreak });
|
|
5572
|
+
updateGoal({ status: "active" }, ctx);
|
|
5573
|
+
appendLedger(ctx.cwd, "goal_resumed", { via: "hourly-rate-probe" });
|
|
5574
|
+
ctx.ui.notify("Hourly probe: resuming (rate-limit windows typically expire at the top of the hour).", "info");
|
|
5575
|
+
scheduleContinuation(ctx, true);
|
|
5576
|
+
}
|
|
5577
|
+
}, "Hourly rate-limit probe");
|
|
5503
5578
|
return;
|
|
5504
5579
|
}
|
|
5505
5580
|
// v0.28.25: the cooldown escalates per CONSECUTIVE brake — a fleet-wide
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.9",
|
|
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",
|