pi-goal-list-loop-audit 0.28.10 → 0.28.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.
@@ -144,11 +144,11 @@ export function buildStatusText(state: State, audit?: AuditDisplayProgress | nul
144
144
  return `glla: ${g.policy} ${paint(theme, "error", "⚠ interrupted — stale handle · auto-resumes on pi restart")}`;
145
145
  }
146
146
  // v0.24.7: list policy gets its own wording — a queue item is not a goal.
147
- // Before: "glla: list ● 3m 19s · list 29" (policy label AND queue counter
148
- // both said "list"). After: "glla: list ● 3m 19s · 29 queued". Goal
149
- // policy keeps the bare "list N" suffix (no duplication there).
147
+ // v0.28.11 (U10): goal policy joins it — "list 29" read as a command
148
+ // fragment; "29 queued" says what the number IS. Both policies now
149
+ // render "… · N queued".
150
150
  const n = state.list?.length ?? 0;
151
- const queue = n === 0 ? "" : g.policy === "list" ? ` · ${n} queued` : ` · list ${n}`;
151
+ const queue = n === 0 ? "" : ` · ${n} queued`;
152
152
  const tasks = g.taskList ? ` ${countDone(g)}/${countTotal(g)} tasks ·` : "";
153
153
  return `glla: ${g.policy} ${paint(theme, "success", "●")}${tasks} ${fmtElapsed(now - Date.parse(g.createdAt))}${queue}`;
154
154
  }
@@ -275,7 +275,7 @@ function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | u
275
275
  const queue = state.list?.length ?? 0;
276
276
  const footer = isList
277
277
  ? `${queue > 0 ? `${queue} queued · ` : ""}/list · /glla`
278
- : `${queue > 0 ? `list ${queue} · ` : ""}/goal status · /glla`;
278
+ : `${queue > 0 ? `${queue} queued · ` : ""}/goal status · /glla`;
279
279
  lines.push(`└─ ${paint(theme, "dim", footer)}`);
280
280
  return lines;
281
281
  }
@@ -758,6 +758,9 @@ function persistState(ctx: ExtensionContext): void {
758
758
  // once per recovery. The TUI flag (buildWidgetLines) carries the standing
759
759
  // state; these notifies are the LOUD part.
760
760
  let persistenceDegradedNotified = false;
761
+
762
+ /** v0.28.11 (U9): objective-first notifies — truncate long objectives. */
763
+ const shortObj = (s: string): string => (s.length > 90 ? `${s.slice(0, 87)}…` : s);
761
764
  function notifyPersistenceState(ctx: ExtensionContext): void {
762
765
  if (isPersistenceDegraded() && !persistenceDegradedNotified) {
763
766
  persistenceDegradedNotified = true;
@@ -885,7 +888,7 @@ function fireReviewer(
885
888
  // to still count as "proposed" in the report + notify. Now the
886
889
  // failure is LOUD and the proposal goes uncounted.
887
890
  ctx.ui.notify(
888
- `Reviewer /goal proposal NOT delivered: ${err instanceof Error ? err.message : String(err)} — the follow-up never reached the session. Restart pi if the session was just replaced.`,
891
+ `Postaudit /goal proposal NOT delivered: ${err instanceof Error ? err.message : String(err)} — the follow-up never reached the session. Restart pi if the session was just replaced.`,
889
892
  "warning",
890
893
  );
891
894
  return false;
@@ -895,7 +898,7 @@ function fireReviewer(
895
898
  ledger: (type, value) => appendLedger(ctx.cwd, type, value),
896
899
  });
897
900
  if (!outcome.fired && outcome.suppressedReason && opts.manual) {
898
- ctx.ui.notify(`Reviewer suppressed: ${outcome.suppressedReason}`, "info");
901
+ ctx.ui.notify(`Postaudit suppressed: ${outcome.suppressedReason}`, "info");
899
902
  }
900
903
  // v0.27.5: surface the silent review to interactive users. The internal
901
904
  // runReviewer notify fires DURING the goal-completion handler, easy to
@@ -911,7 +914,7 @@ function fireReviewer(
911
914
  );
912
915
  }
913
916
  } catch (err) {
914
- ctx.ui.notify(`Reviewer failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`, "warning");
917
+ ctx.ui.notify(`Postaudit failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`, "warning");
915
918
  }
916
919
  }
917
920
 
@@ -1088,10 +1091,10 @@ async function cmdSet(args: string, ctx: ExtensionContext, skipDraft = false): P
1088
1091
  // v0.28.1 (S3): the goal is persisted — mark the interrupt so the next
1089
1092
  // fresh session auto-resumes, and tell the truth instead of "starting now".
1090
1093
  updateGoal({ interruptedAt: nowIso(), interruptedReason: "created in a stale session" }, ctx);
1091
- ctx.ui.notify(`Goal ${goal.id} created and safe in .pi-glla/ — this stale process can't send continuations. Restart pi and it auto-resumes.`, "warning");
1094
+ ctx.ui.notify(`Goal saved: ${shortObj(goal.objective)} — safe in .pi-glla/, but this stale process can't send continuations. Restart pi and it auto-resumes. (id: ${goal.id})`, "warning");
1092
1095
  return;
1093
1096
  }
1094
- ctx.ui.notify(`Goal ${goal.id} created — starting now. Auditor will verify on completion.`, "info");
1097
+ ctx.ui.notify(`Goal started: ${shortObj(goal.objective)} — the auditor will verify on completion. (id: ${goal.id})`, "info");
1095
1098
  scheduleContinuation(ctx, true);
1096
1099
  }
1097
1100
 
@@ -3453,17 +3456,17 @@ async function cmdToolOverride(args: string, ctx: ExtensionContext): Promise<voi
3453
3456
  if (action === "allow") {
3454
3457
  const allow = current.allow ?? [];
3455
3458
  if (!allow.includes(tool)) apply({ allow: [...allow, tool] });
3456
- ctx.ui.notify(`toolOverrides.allow += ${tool}`, "info");
3459
+ ctx.ui.notify(`"${tool}" is now always visible to the agent (project override saved).`, "info");
3457
3460
  } else if (action === "hide") {
3458
3461
  const hide = current.hide ?? [];
3459
3462
  if (!hide.includes(tool)) apply({ hide: [...hide, tool] });
3460
- ctx.ui.notify(`toolOverrides.hide += ${tool}`, "info");
3463
+ ctx.ui.notify(`"${tool}" is now always hidden from the agent (project override saved).`, "info");
3461
3464
  } else if (action === "unallow") {
3462
3465
  apply({ allow: (current.allow ?? []).filter((t) => t !== tool) });
3463
- ctx.ui.notify(`toolOverrides.allow -= ${tool}`, "info");
3466
+ ctx.ui.notify(`"${tool}" visibility override removed — the session decides again.`, "info");
3464
3467
  } else {
3465
3468
  apply({ hide: (current.hide ?? []).filter((t) => t !== tool) });
3466
- ctx.ui.notify(`toolOverrides.hide -= ${tool}`, "info");
3469
+ ctx.ui.notify(`"${tool}" hide override removed — the session decides again.`, "info");
3467
3470
  }
3468
3471
  return;
3469
3472
  }
@@ -3490,7 +3493,12 @@ async function cmdToolOverride(args: string, ctx: ExtensionContext): Promise<voi
3490
3493
  }
3491
3494
  cfg[tool] = toolCfg;
3492
3495
  apply({ perToolConfig: cfg });
3493
- ctx.ui.notify(`toolOverrides.perToolConfig.${tool} ${action === "set" ? "set" : "unset"}`, "info");
3496
+ ctx.ui.notify(
3497
+ action === "set"
3498
+ ? `"${tool}" setting saved: ${kv.slice(0, kv.indexOf("="))} = ${JSON.stringify(toolCfg[kv.slice(0, kv.indexOf("="))])} (project override).`
3499
+ : `"${tool}" setting "${kv}" removed — back to the built-in default.`,
3500
+ "info",
3501
+ );
3494
3502
  return;
3495
3503
  }
3496
3504
  ctx.ui.notify(`Unknown tooloverride action: ${action}. Use: list | allow | hide | unallow | unhide | set | unset.`, "warning");
@@ -3530,7 +3538,7 @@ async function cmdReviewerSettings(ctx: ExtensionContext): Promise<void> {
3530
3538
  const cfg = load();
3531
3539
  let choice: string | undefined;
3532
3540
  try {
3533
- choice = await ctx.ui.select("Reviewer — post-completion follow-up enqueuer (project settings)", reviewerMenuOptions(cfg));
3541
+ choice = await ctx.ui.select("Postaudit — post-completion follow-up enqueuer (project settings)", reviewerMenuOptions(cfg));
3534
3542
  } catch {
3535
3543
  return;
3536
3544
  }
@@ -3557,8 +3565,10 @@ async function cmdReviewerSettings(ctx: ExtensionContext): Promise<void> {
3557
3565
  const n = Number(v?.trim());
3558
3566
  if (Number.isSafeInteger(n) && n >= 1 && n <= 100) save({ maxReviewsPerDay: n });
3559
3567
  }
3560
- } catch {
3561
- /* individual save failures are non-fatal */
3568
+ } catch (err) {
3569
+ // v0.28.11 (E7): a swallowed save failure made the user believe the
3570
+ // toggle landed. Loud now.
3571
+ ctx.ui.notify(`Postaudit setting NOT saved: ${err instanceof Error ? err.message : String(err)} — check .pi-glla/settings.json permissions.`, "warning");
3562
3572
  }
3563
3573
  }
3564
3574
  }
@@ -4041,7 +4051,7 @@ export default function (pi: ExtensionAPI): void {
4041
4051
  handler: settingsHandler,
4042
4052
  });
4043
4053
  pi.registerCommand("review", {
4044
- description: "Manually run the reviewer on an archived goal: /review <goal-id> [off|on|auto|aggressive] — extracts findings, writes a report to .pi-glla/reviews/, cascades per the mode (auto/aggressive = no Confirms). Bypasses the trigger gates (explicit user request).",
4054
+ description: "Manually run the postaudit on an archived goal: /review <goal-id> [off|on|auto|aggressive] — extracts findings, writes a report to .pi-glla/reviews/, cascades per the mode (auto/aggressive = no Confirms). Bypasses the trigger gates (explicit user request).",
4045
4055
  handler: (args: string, ctx: ExtensionContext) => { rememberCtx(ctx); return cmdReview(args, ctx); },
4046
4056
  });
4047
4057
  pi.registerCommand("list", {
@@ -226,27 +226,27 @@ export function runReviewer(
226
226
  deps: ReviewerDeps,
227
227
  ): ReviewerOutcome {
228
228
  const none = (suppressedReason: string): ReviewerOutcome => ({ fired: false, suppressedReason, enqueued: 0, proposed: 0 });
229
- if (!config.enabled && !deps.manual) return none("reviewer disabled");
229
+ if (!config.enabled && !deps.manual) return none("the postaudit is disabled (/glla postaudit → Enabled)");
230
230
  // v0.27.5: "off" mode is the user-friendly way to silence the postaudit
231
231
  // — equivalent to enabled=false but exposed via the postaudit menu.
232
- if (config.mode === "off" && !deps.manual) return none("postaudit mode = off");
232
+ if (config.mode === "off" && !deps.manual) return none("postaudit mode is off (/glla postaudit → Mode)");
233
233
  const event = source.kind === "goal" ? `${source.terminal}` : "list-complete";
234
234
  if (!deps.manual) {
235
- if (config.doNotFireOn.includes(event)) return none(`doNotFireOn: ${event}`);
236
- if (source.kind === "goal" && source.terminal !== "goal-complete") return none(`not a completion: ${source.terminal}`);
237
- if (!config.fireOn.includes(source.kind === "goal" ? "goal-complete" : "list-complete")) return none("fireOn excludes this event");
235
+ if (config.doNotFireOn.includes(event)) return none(`this event type (${event}) is excluded in /glla postaudit → fire-on`);
236
+ if (source.kind === "goal" && source.terminal !== "goal-complete") return none(`the goal ended as ${source.terminal}, not a completion — no follow-up fires`);
237
+ if (!config.fireOn.includes(source.kind === "goal" ? "goal-complete" : "list-complete")) return none("this event type is excluded in /glla postaudit → fire-on");
238
238
  // v0.26.2: in auto mode the queue emptying is the cascade's natural
239
239
  // rhythm, not a runaway — the refire window must not strangle it.
240
240
  // (The per-day cap below still bounds everything.)
241
241
  const refireWindowApplies = !(config.mode === "auto" && source.kind === "list");
242
242
  if (refireWindowApplies && reviewerFiredRecently(deps.ledgerEntries, REVIEWER_REFIRE_WINDOW_MS, deps.nowMs)) {
243
243
  deps.ledger("reviewer_suppressed", { reason: "refire-window", goalId: source.goalId });
244
- return none("reviewer fired within the last 5 minutes (runaway prevention)");
244
+ return none("a postaudit ran within the last 5 minutes (runaway prevention)");
245
245
  }
246
246
  const today = reviewsToday(deps.ledgerEntries, deps.nowMs);
247
247
  if (today >= config.maxReviewsPerDay) {
248
248
  deps.ledger("reviewer_suppressed", { reason: "day-cap", count: today, cap: config.maxReviewsPerDay, goalId: source.goalId });
249
- return none(`day cap reached (${today}/${config.maxReviewsPerDay})`);
249
+ return none(`daily postaudit cap reached (${today}/${config.maxReviewsPerDay}) — /glla postaudit → Max reviews`);
250
250
  }
251
251
  }
252
252
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goal-list-loop-audit",
3
- "version": "0.28.10",
3
+ "version": "0.28.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",
@@ -93,21 +93,28 @@ Do NOT spawn more subagents of the failed type until quota resets — you will j
93
93
 
94
94
  ## DETACHED COMMIT DETECTION
95
95
 
96
- If your commits keep getting rewritten away (same content, new SHA — or worse, content reverted), check the auto-committer daemon BEFORE diagnosing yourself as stuck or broken:
96
+ If your commits keep getting rewritten away (same content, new SHA — or worse, content reverted), check for an **auto-commit daemon** BEFORE diagnosing yourself as stuck or broken. **Skip this section entirely if your rig has no auto-committer** — most rigs don't. (The maintainer's rigs run one called `dracon-sync`; yours may run a different one, or none.)
97
+
98
+ Generic forensics (safe everywhere):
97
99
 
98
100
  ```bash
99
101
  git reflog --date=iso | grep -E "filter-branch|filter-repo"
100
102
  git reflog --date=iso | grep -E ": reset:"
103
+ ```
104
+
105
+ If those show rewrites you didn't make, find the daemon (example for the maintainer's rig — adapt the pattern to yours):
106
+
107
+ ```bash
101
108
  ps -fea | grep -E "dracon-sync|filter-repo" | grep -v grep
102
109
  ```
103
110
 
104
- The auto-committer (`dracon-sync daemon`) may be running `auto_rewrite_large_blobs` on your commits. The fix is NOT to keep re-committing — it is to:
111
+ An auto-committer may be rewriting your commits (e.g. `dracon-sync daemon` running `auto_rewrite_large_blobs`). The fix is NOT to keep re-committing — it is to:
105
112
 
106
- 1. Pause the daemon (`dracon-sync pause`, or write the `.pi-glla/.pause-auto-commit` sentinel via the goal tools).
107
- 2. Investigate the rewrite trigger (daemon config `max_push_blob_bytes`, `auto_rewrite_large_blobs`).
113
+ 1. Pause the daemon if one exists (on the maintainer's rigs: `dracon-sync pause`, or write the `.pi-glla/.pause-auto-commit` sentinel via the goal tools).
114
+ 2. Investigate the rewrite trigger (for `dracon-sync`: daemon config `max_push_blob_bytes`, `auto_rewrite_large_blobs`).
108
115
  3. Add `.pi-glla/` to the daemon's exclude list.
109
116
 
110
- Do NOT conclude "the loop is too eager" or "I am broken" before checking what the daemon is doing — the daemon rewriting your commits makes YOUR loop look broken when it is not.
117
+ Do NOT conclude "the loop is too eager" or "I am broken" before checking what a daemon is doing — a daemon rewriting your commits makes YOUR loop look broken when it is not.
111
118
 
112
119
  ## TASK WORKFLOW
113
120