pi-goal-list-loop-audit 0.34.13 → 0.34.14

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.
@@ -411,6 +411,30 @@ function attemptAutoRecovery(ctx: ExtensionContext, where: string): boolean {
411
411
  return true;
412
412
  }
413
413
 
414
+ /** v0.34.14: /reload rebind detector. The extension runs INSIDE pi, so
415
+ * process.pid IS pi's pid: an instance that boots and finds its OWN pid
416
+ * already in the owner file is a /reload rebuild of a live session, not a
417
+ * cold boot. Rebinds always resume active goals/loops — holding mid-work
418
+ * after an in-place rebuild is pure friction (user directive: keep going
419
+ * unless we must stop; "the list is not continuing" after /reload,
420
+ * hellhunter 2026-08-01). Cold boots (new pid) still honor autoresume=off.
421
+ * Sidecar, not the ledger: read-before-write must be atomic-ish and the
422
+ * ledger is append-only. */
423
+ const SESSION_OWNER_FILE = "session-owner.json";
424
+ function claimSessionOwnerAndDetectRebind(cwd: string): boolean {
425
+ try {
426
+ const p = path.join(piGlaDir(cwd), SESSION_OWNER_FILE);
427
+ let prevPid: number | null = null;
428
+ try {
429
+ prevPid = (JSON.parse(fs.readFileSync(p, "utf-8")) as { pid?: number }).pid ?? null;
430
+ } catch { /* absent or corrupt — first boot */ }
431
+ fs.writeFileSync(p, JSON.stringify({ pid: process.pid, at: new Date().toISOString() }));
432
+ return prevPid !== null && prevPid === process.pid;
433
+ } catch {
434
+ return false;
435
+ }
436
+ }
437
+
414
438
  /** v0.34.13: consume the sidecar marker on session restore. Single-use,
415
439
  * freshness-bounded — a stale marker from an abandoned recovery must not
416
440
  * surprise-resume a later session. */
@@ -680,7 +704,7 @@ const ZOMBIE_RUN_ALERT_THROTTLE_MS = 10 * 60_000;
680
704
  // (sendMessage never threw; session reported idle) but started NO turn —
681
705
  // transcript frozen, tokens flat, 10+ minutes of refires into the void.
682
706
  // Same family as the post-compaction dropped trigger (v0.26.5), but the
683
- // pending-latch watchdog needs idle&&pending and pi reported no pending
707
+ // the pending latch needs idle&&pending and pi reported no pending
684
708
  // here, and the zombie watchdog needs busy — this shape falls between both
685
709
  // chairs. Disarm signal = real activity (agent_end/tool_call) AFTER the
686
710
  // last send; a landed turn — even a lazy text-only one — disarms it.
@@ -3679,7 +3703,13 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
3679
3703
  // logged as disapprovals.
3680
3704
  const auditorRan = result.output.trim().length > 0;
3681
3705
  // v0.28.5 (E2): a REAL auditor run clears the infra-error streak.
3682
- if (auditorRan && (state.goal.auditInfraStreak ?? 0) > 0) updateGoal({ auditInfraStreak: undefined }, ctx);
3706
+ // v0.34.14: …but only a CLEAN one. A STALLED run returns the partial
3707
+ // output it streamed before the abort — non-empty, so auditorRan is
3708
+ // true — while result.error still marks it an infrastructure failure.
3709
+ // Clearing the streak on those meant the 3-strike breaker at :3874
3710
+ // NEVER engaged: pully 2026-08-01 looped 10-min stall cycles for 4h
3711
+ // (the auditor hung on an ssh/sudo verification every attempt).
3712
+ if (auditorRan && !result.error && (state.goal.auditInfraStreak ?? 0) > 0) updateGoal({ auditInfraStreak: undefined }, ctx);
3683
3713
  const history = state.goal.auditHistory ?? [];
3684
3714
  if (auditorRan) {
3685
3715
  // v0.25.4: strip think-block leakage (MiniMax-M3 `</think>`
@@ -3878,11 +3908,11 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
3878
3908
  auditHistory: history,
3879
3909
  auditInfraStreak: infraStreak,
3880
3910
  pauseKind: "error",
3881
- pauseReason: `auditor infrastructure failed ${infraStreak}× in a row — the auditor model is likely broken (last: ${result.error.slice(0, 120)})`,
3911
+ pauseReason: `auditor infrastructure failed ${infraStreak}× in a row — the auditor model is likely broken OR a verification command is hanging (ssh/sudo/long test runs stall the stream) (last: ${result.error.slice(0, 120)})`,
3882
3912
  pauseSuggestedAction: "Fix the auditor model (/glla model=provider/id) or restart pi, then /goal resume. Your work was NOT judged.",
3883
3913
  }, ctx);
3884
3914
  appendLedger(ctx.cwd, "goal_paused", { reason: `auditor infra streak ${infraStreak}: ${result.error.slice(0, 120)}` });
3885
- ctx.ui.notify(`${goalNoun()} paused: auditor infrastructure failed ${infraStreak}× in a row. Fix the auditor model (/glla model=...), then /goal resume.`, "warning");
3915
+ ctx.ui.notify(`${goalNoun()} paused: auditor infrastructure failed ${infraStreak}× in a row model broken or a verification command hanging (ssh/sudo/long runs). Fix with /glla model=... or unblock the command, then /goal resume.`, "warning");
3886
3916
  notifyExternal(ctx, `${goalNoun()} paused: auditor infrastructure ${infraStreak}× — model likely broken.`);
3887
3917
  return {
3888
3918
  content: [{
@@ -6418,9 +6448,13 @@ export default function (pi: ExtensionAPI): void {
6418
6448
  // v0.34.13: an auto-recovery /reload carries its own resume consent —
6419
6449
  // the sidecar marker overrides autoresume=off for THIS restore only.
6420
6450
  const recoveryResume = consumeRecoveryResume(ctx.cwd);
6451
+ // v0.34.14: a /reload rebind (same pi pid) ALWAYS resumes — the session
6452
+ // is live mid-work; holding is the "list is not continuing" bug.
6453
+ const rebindResume = claimSessionOwnerAndDetectRebind(ctx.cwd);
6454
+ if (rebindResume) appendLedger(ctx.cwd, "rebind_resume", { pid: process.pid });
6421
6455
  if (isLoopActive()) {
6422
6456
  const l = state.loop!;
6423
- if (autoResume || recoveryResume) {
6457
+ if (autoResume || recoveryResume || rebindResume) {
6424
6458
  ctx.ui.notify(
6425
6459
  `Resuming loop (iteration ${l.iteration}/${l.maxIterations > 0 ? l.maxIterations : "∞"}, best ${l.bestValue ?? "n/a"}, stall ${l.stallCount}/${l.plateauWindow}): ${l.target.slice(0, 60)}`,
6426
6460
  "info",
@@ -6441,7 +6475,7 @@ export default function (pi: ExtensionAPI): void {
6441
6475
  // "load it but not auto start it"). Interrupted goals hold like
6442
6476
  // everything else; autoresume=on (unattended rigs) still auto-resumes
6443
6477
  // them, and the marker is cleared only on that promised auto-resume.
6444
- if (autoResume || recoveryResume) {
6478
+ if (autoResume || recoveryResume || rebindResume) {
6445
6479
  // v0.28.1 (S2): clear the stale-handle interrupt marker — this IS
6446
6480
  // the auto-resume the marker promised.
6447
6481
  if (wasInterrupted) updateGoal({ interruptedAt: undefined, interruptedReason: undefined }, ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goal-list-loop-audit",
3
- "version": "0.34.13",
3
+ "version": "0.34.14",
4
4
  "description": "Mission control for autonomous pi: interview-drafted goals, an audited task queue, and forever-loops (metric, spec, project-audit) that run for hours. An isolated extension-less auditor re-verifies every completion with raw evidence; confirmed drafts, decision pauses and consent gates keep you in charge.",
5
5
  "license": "MIT",
6
6
  "author": "dracon",