pi-goal-list-loop-audit 0.29.10 → 0.29.11

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.
@@ -173,10 +173,11 @@ export function buildStatusText(state: State, audit?: AuditDisplayProgress | nul
173
173
  return `glla: ${paint(theme, pauseIsError(g) ? "error" : "warning", label)}${heldSuffix}`;
174
174
  }
175
175
  if (g.status === "active") {
176
- // v0.28.1 (S1/S2): a stale-handle interrupt keeps the goal ACTIVE (the
177
- // next fresh session auto-resumes it) — say so instead of looking healthy.
176
+ // v0.28.1 (S1/S2): a stale-handle interrupt keeps the goal ACTIVE.
177
+ // v0.29.11: the fresh session HOLDS it (hold-everything restore gate;
178
+ // autoresume=on resumes for you) — name the verb, don't promise auto.
178
179
  if (g.interruptedAt) {
179
- return `glla: ${g.policy} ${paint(theme, "error", "⚠ interrupted — stale handle · auto-resumes on pi restart")}${heldSuffix}`;
180
+ return `glla: ${g.policy} ${paint(theme, "error", "⚠ interrupted — stale handle · restart pi → /glla resume")}${heldSuffix}`;
180
181
  }
181
182
  // v0.24.7: list policy gets its own wording — a queue item is not a goal.
182
183
  // v0.28.11 (U10): goal policy joins it — "list 29" read as a command
@@ -219,7 +219,7 @@ function goStaleTerminal(ctx: ExtensionContext, where: string): void {
219
219
  if (extensionApiStale) return; // already terminal — don't re-spam
220
220
  extensionApiStale = true;
221
221
  appendLedger(ctx.cwd, "extension_api_stale", { where, kind: isLoopActive() ? "loop" : "goal" });
222
- const guidance = "pi invalidated this session's extension handle (session replacement — compaction triggers it in pi 0.82.x). Sends can never land in this process. Restart pi (or reload extensions) — an active goal auto-resumes on the fresh session; loops need /loop start.";
222
+ const guidance = "pi invalidated this session's extension handle (session replacement — compaction triggers it in pi 0.82.x). Sends can never land in this process. Restart pi (or reload extensions) — the goal/loop holds on the fresh session: /glla resume continues it (autoresume=on resumes for you).";
223
223
  if (isLoopActive()) {
224
224
  clearLoopTimer();
225
225
  state.loop = { ...state.loop!, active: false, stopReason: `extension api stale: ${guidance}` };
@@ -260,7 +260,7 @@ function warnIfStaleAtEntry(ctx: ExtensionContext, what: string): boolean {
260
260
  if (!probeExtensionApiStale()) return false;
261
261
  appendLedger(ctx.cwd, "extension_api_stale", { where: `entry probe (${what})` });
262
262
  ctx.ui.notify(
263
- `glla: this session's extension handle is stale (pi session replacement) — ${what} can't send continuations in this process. State is safe in .pi-glla/ — restart pi and the active goal auto-resumes.`,
263
+ `glla: this session's extension handle is stale (pi session replacement) — ${what} can't send continuations in this process. State is safe in .pi-glla/ — restart pi, then /glla resume (autoresume=on resumes for you).`,
264
264
  "warning",
265
265
  );
266
266
  return true;
@@ -544,9 +544,9 @@ function escalateSendRearmStorm(ctx: ExtensionContext, kind: "continuation" | "l
544
544
  appendLedger(ctx.cwd, "send_rearm_escalated", { kind, afterMinutes: mins, silentMinutes: silent });
545
545
  if (kind === "loop" && isLoopActive()) {
546
546
  clearLoopTimer();
547
- state.loop = { ...state.loop!, active: false, stopReason: `send-retry storm: ${mins}m of re-arms with no session activity for ${silent}m — the session is wedged. Restart pi, then /loop start again.` };
547
+ state.loop = { ...state.loop!, active: false, stopReason: `send-retry storm: ${mins}m of re-arms with no session activity for ${silent}m — the session is wedged. Restart pi, then /loop resume (the loop holds on restore).` };
548
548
  persistState(ctx);
549
- ctx.ui.notify(`Loop stopped: send-retry storm (${mins}m, session silent ${silent}m). Restart pi and /loop start.`, "warning");
549
+ ctx.ui.notify(`Loop stopped: send-retry storm (${mins}m, session silent ${silent}m). Restart pi, then /loop resume (the loop holds on restore).`, "warning");
550
550
  notifyExternal(ctx, "Loop stopped: send-retry storm.");
551
551
  return;
552
552
  }
@@ -583,9 +583,9 @@ function escalateStallNow(ctx: ExtensionContext, threshold: number): boolean {
583
583
  appendLedger(ctx.cwd, "stall_escalated", { threshold, kind: isLoopActive() ? "loop" : "goal" });
584
584
  if (isLoopActive()) {
585
585
  clearLoopTimer();
586
- state.loop = { ...state.loop!, active: false, stopReason: `stalled: ${threshold} continuation refires landed no turn — the session is not continuing (wedged message queue or stale API). Restart pi, then /loop start again.` };
586
+ state.loop = { ...state.loop!, active: false, stopReason: `stalled: ${threshold} continuation refires landed no turn — the session is not continuing (wedged message queue or stale API). Restart pi, then /loop resume (the loop holds on restore).` };
587
587
  persistState(ctx);
588
- ctx.ui.notify(`Loop stopped: ${threshold} refires produced no turn — the continuation is not landing. Restart pi and /loop start.`, "warning");
588
+ ctx.ui.notify(`Loop stopped: ${threshold} refires produced no turn — the continuation is not landing. Restart pi, then /loop resume (the loop holds on restore).`, "warning");
589
589
  notifyExternal(ctx, "Loop stopped: stalled (continuation not landing).");
590
590
  return true;
591
591
  }
@@ -619,12 +619,16 @@ function heartbeatTick(): void {
619
619
  // machinery below stays quiet for 3 minutes while the replaced session
620
620
  // settles (latch watchdog, wedge alert, refire counting all resume after).
621
621
  if (Date.now() < compactionGraceUntil) return;
622
- // v0.28.27: a stale (session-replaced) handle can never land a send —
623
- // the terminal warning already fired once. ALL stall machinery stays
624
- // quiet from here on: refiring into a dead process is misleading, and
625
- // worse, the stall escalation would PAUSE the goal — silently cancelling
626
- // the interruptedAt → auto-resume-on-restart promise the footer shows.
627
- if (extensionApiStale) return;
622
+ // v0.29.11: PROBE the handle before the stall machinery runs — a
623
+ // session-replaced handle can never land a refire, so detect it on the
624
+ // first tick after replacement and go terminal immediately, instead of
625
+ // burning refires into the void until a send happens to throw (field:
626
+ // polis stall 3/5, endless-td stall 1/5 before the warning fired).
627
+ // v0.28.27: once terminal, ALL stall machinery stays quiet from here
628
+ // on: refiring into a dead process is misleading, and worse, the stall
629
+ // escalation would PAUSE the goal — silently cancelling the
630
+ // interruptedAt → hold-on-restart promise the footer shows.
631
+ if (probeExtensionApiStale()) { goStaleTerminal(ctx, "heartbeat probe"); return; }
628
632
  // v0.29.1: stranded-audit recovery. A goal left in "auditing" with NO
629
633
  // in-flight audit means the auditor's result never landed (wedged queue
630
634
  // ate the tool result; compaction/restart mid-audit). Field-observed in
@@ -5328,6 +5332,16 @@ export default function (pi: ExtensionAPI): void {
5328
5332
  ) {
5329
5333
  ctx.ui.notify("Auto-resume fired (event: session start). Continue working.", "info");
5330
5334
  }
5335
+ // v0.29.11: loops stopped by the stale-handle terminal or the stall
5336
+ // escalation told the user "restart pi, then /loop start" — but a
5337
+ // fresh start discards iteration/best/history. Hold them on load like
5338
+ // any restore-held loop: /loop resume continues from the saved state.
5339
+ if (state.loop && !state.loop.active &&
5340
+ (state.loop.stopReason?.startsWith("extension api stale") || state.loop.stopReason?.startsWith("stalled:") || state.loop.stopReason?.startsWith("send-retry storm:"))) {
5341
+ appendLedger(ctx.cwd, "loop_held_for_resume", { was: (state.loop.stopReason ?? "").slice(0, 40) });
5342
+ state.loop = { ...state.loop, stopReason: HELD_ON_RESTORE };
5343
+ persistState(ctx);
5344
+ }
5331
5345
  // v0.29.10: reseed live/held audit loops stuck on the degenerate
5332
5346
  // pre-discovery baseline-0 — best can never go below 0, so every
5333
5347
  // iteration stalls and the prompt cries REGRESSED on real progress
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goal-list-loop-audit",
3
- "version": "0.29.10",
3
+ "version": "0.29.11",
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",