pi-goal-list-loop-audit 0.38.19 → 0.38.20

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.38.20 — approval-notify cleanup (2026-09-04)
4
+
5
+ ### Fixed
6
+ - Approval chat notify no longer reprints the agent's pre-verdict recap verbatim: the stale `Next: <verdict pending>` line (which read as complete-before-verify next to the approval trailer) is stripped on every approval surface, and the chat notify is outcome + at most two informing details + approval trailer + archive record pointer (`buildApprovalChatLines`, `withoutStaleNext`). The transcript notice keeps the informing details minus the stale Next. Applied to all three `✓ done` paths (detached approval, manual-verify approval, no-audit complete).
7
+ - Field note: the 19:20 `...` cuts were v0.38.13 word-boundary `…` clips working as designed (every cut lands on a word boundary), not mid-word mangling — but five 120-char label lines still scan as soup, which the cap above addresses. The same session showed zero `terminal_completion_notice_*` events because the tab runs pre-v0.38.18 loaded code — `/reload` to pick up the notice path.
8
+
3
9
  ## 0.38.19 — disapproval response: dispatch stall root-cause + state clear (2026-09-04)
4
10
 
5
11
  ### Fixed
package/docs/INDEX.md CHANGED
@@ -18,7 +18,7 @@ For shipped docs, the relevant entry points are:
18
18
  failback; v0.35.9 hardened cross-version npm tarball checks; v0.35.10
19
19
  handles multi-entry npm dry-run reports; v0.35.11 accepts both npm report
20
20
  shapes; v0.35.12 supports npm 12's keyed pack reports; v0.35.13 fixes stale-API recovery loops.
21
- v0.35.14–v0.38.19 continue through the supervisor freeze (`/glla pause`),
21
+ v0.35.14–v0.38.20 continue through the supervisor freeze (`/glla pause`),
22
22
  load hold, auditor picker parity, Windows launch fix, zombie-watchdog
23
23
  subagent carve-out, due-wait backstop, the `/glla agents` visibility panel,
24
24
  durable state-root selection, blank-until-resume auditor context, frozen
@@ -159,6 +159,34 @@ export function humanCompletionBrief(
159
159
  return { outcome, details };
160
160
  }
161
161
 
162
+ /** v0.38.20: the agent's `Next:` recap line goes stale the moment the
163
+ * verdict lands — reprinting it next to the approval trailer reads as
164
+ * complete-before-verify (field 2026-09-04: `Next: detached auditor
165
+ * verdict decides.` printed above `— auditor … approved.`). Every approval
166
+ * surface strips it; the full six-label record stays in the archive. */
167
+ export function withoutStaleNext(details: string[] | undefined): string[] {
168
+ return (details ?? []).filter((d) => !/^\s*Next\s*:/i.test(d));
169
+ }
170
+
171
+ /** v0.38.20: the approval chat notify — outcome first, at most two
172
+ * informing details (the full record lives in the archive and the
173
+ * transcript notice), then the approval trailer and the record pointer.
174
+ * Five 120-char label lines scan as soup, not a summary (field
175
+ * 2026-09-04); the stale Next never reaches the chat. */
176
+ export function buildApprovalChatLines(notice: {
177
+ outcome: string;
178
+ details: string[] | undefined;
179
+ approval: string;
180
+ record: string;
181
+ }): string[] {
182
+ return [
183
+ `✓ done — ${notice.outcome}`,
184
+ ...withoutStaleNext(notice.details).slice(0, 2),
185
+ notice.approval,
186
+ notice.record,
187
+ ];
188
+ }
189
+
162
190
  /** Multi-line projection: one `Label: value` line per label with generous
163
191
  * word-bounded values. This is the user-facing `✓ done` block — six short
164
192
  * facts that stay scannable in chat. The single-line projection remains
@@ -241,7 +241,7 @@ import {
241
241
  pushCapped as pushRepetitionCapped,
242
242
  } from "../goal-loop-repetition.js";
243
243
  import { buildStatusText, buildWidgetLines, type AuditDisplayProgress } from "../goal-loop-display.js";
244
- import { compactCompletionSummary, compactTerminalCompletionSummary, isGenericCompletionSummary, missingCompletionSummaryLabels, terminalHumanBrief } from "../completion-summary.js";
244
+ import { buildApprovalChatLines, compactCompletionSummary, compactTerminalCompletionSummary, isGenericCompletionSummary, missingCompletionSummaryLabels, terminalHumanBrief, withoutStaleNext } from "../completion-summary.js";
245
245
  import {
246
246
  defaultAgentDir,
247
247
  resolveEffectiveSubagentModel,
@@ -1424,6 +1424,9 @@ async function retryStoredCompletionAudit(origin: CompletionAuditOrigin = "provi
1424
1424
  archivePath: path.relative(liveCtx.cwd, archivedGoalPath(liveCtx.cwd, state.goal.id)) || archivedGoalPath(liveCtx.cwd, state.goal.id),
1425
1425
  }, state.goal.completionSummary);
1426
1426
  const approvalVia = `${origin === "manual" ? " on /goal verify" : origin === "session-recovery" ? " after session recovery" : " on the provider retry"}${fallbackUsed ? " after an auditor-model fallback" : ""}`;
1427
+ // v0.38.20: the chat record pointer. Computed pre-archive like the
1428
+ // recap/brief above (archiveCurrentGoal clears state.goal).
1429
+ const approvalRecord = `— record: ${path.relative(liveCtx.cwd, archivedGoalPath(liveCtx.cwd, state.goal.id)) || archivedGoalPath(liveCtx.cwd, state.goal.id)}`;
1427
1430
  const archived = archiveCurrentGoal(liveCtx, "complete", `auditor ${result.model} approved (${origin})`);
1428
1431
  if (!archived) {
1429
1432
  // archiveCurrentGoal already preserved the live record and warned the
@@ -1441,8 +1444,15 @@ async function retryStoredCompletionAudit(origin: CompletionAuditOrigin = "provi
1441
1444
  }
1442
1445
  // v0.38.14: the chat notify is the human briefing — outcome first,
1443
1446
  // filler labels dropped — while the external notify keeps the compact
1444
- // single line (pager/sound safe).
1445
- liveCtx.ui.notify(`✓ done — ${brief.outcome}\n${[...brief.details, `— auditor ${result.model} approved${approvalVia}.`].join("\n")}`, "info");
1447
+ // single line (pager/sound safe). v0.38.20: outcome + at most two
1448
+ // details + approval + record pointer; the agent's pre-verdict `Next:`
1449
+ // is stale the moment the verdict lands and never reaches the chat.
1450
+ liveCtx.ui.notify(buildApprovalChatLines({
1451
+ outcome: brief.outcome,
1452
+ details: brief.details,
1453
+ approval: `— auditor ${result.model} approved${approvalVia}.`,
1454
+ record: approvalRecord,
1455
+ }).join("\n"), "info");
1446
1456
  notifyExternal(liveCtx, `Goal complete (auditor approved, ${origin}): ${recap}`);
1447
1457
  // v0.38.18 (track 3): the toast above is ephemeral — without a
1448
1458
  // transcript entry the session keeps narrating "waiting on the
@@ -1454,7 +1464,9 @@ async function retryStoredCompletionAudit(origin: CompletionAuditOrigin = "provi
1454
1464
  sendTerminalCompletionNotice(liveCtx, {
1455
1465
  goalId,
1456
1466
  outcome: brief.outcome,
1457
- details: [...brief.details, `— auditor ${result.model} approved${approvalVia}.`],
1467
+ // v0.38.20: the transcript keeps the informing details, but the
1468
+ // stale pre-verdict `Next:` is stripped here too.
1469
+ details: [...withoutStaleNext(brief.details), `— auditor ${result.model} approved${approvalVia}.`],
1458
1470
  });
1459
1471
  }
1460
1472
  return;
@@ -252,7 +252,7 @@ import {
252
252
  pushCapped as pushRepetitionCapped,
253
253
  } from "../goal-loop-repetition.js";
254
254
  import { buildStatusText, buildWidgetLines, type AuditDisplayProgress } from "../goal-loop-display.js";
255
- import { compactCompletionSummary, compactTerminalCompletionSummary, resolveCompletionSummary, terminalHumanBrief } from "../completion-summary.js";
255
+ import { buildApprovalChatLines, compactCompletionSummary, compactTerminalCompletionSummary, resolveCompletionSummary, terminalHumanBrief, withoutStaleNext } from "../completion-summary.js";
256
256
  import {
257
257
  defaultAgentDir,
258
258
  resolveEffectiveSubagentModel,
@@ -1224,8 +1224,16 @@ function registerAgentTools(pi: any): void {
1224
1224
  stopReason: terminalReason,
1225
1225
  archivePath: path.relative(ctx.cwd, archivedGoalPath(ctx.cwd, terminalGoal.id)) || archivedGoalPath(ctx.cwd, terminalGoal.id),
1226
1226
  });
1227
- const briefBlock = [...brief.details, `— completed without audit (your choice).`].join("\n");
1228
- ctx.ui.notify(`✓ done — ${brief.outcome}\n${briefBlock}`, "info");
1227
+ // v0.38.20: the command output keeps the informing details (stale
1228
+ // `Next:` stripped); the chat notify is outcome + approval +
1229
+ // record pointer like every other approval path.
1230
+ const briefBlock = [...withoutStaleNext(brief.details), `— completed without audit (your choice).`].join("\n");
1231
+ ctx.ui.notify(buildApprovalChatLines({
1232
+ outcome: brief.outcome,
1233
+ details: brief.details,
1234
+ approval: `— completed without audit (your choice).`,
1235
+ record: `— record: ${path.relative(ctx.cwd, archivedGoalPath(ctx.cwd, terminalGoal.id)) || archivedGoalPath(ctx.cwd, terminalGoal.id)}`,
1236
+ }).join("\n"), "info");
1229
1237
  notifyExternal(ctx, `Goal complete without audit (user choice): ${recap}`);
1230
1238
  return { content: [{ type: "text", text: `Goal marked complete without audit (user choice).\n\n${briefBlock}` }], details: {} };
1231
1239
  }
@@ -1259,6 +1267,9 @@ function registerAgentTools(pi: any): void {
1259
1267
  stopReason: terminalReason,
1260
1268
  archivePath: path.relative(ctx.cwd, archivedGoalPath(ctx.cwd, state.goal.id)) || archivedGoalPath(ctx.cwd, state.goal.id),
1261
1269
  }, state.goal.completionSummary);
1270
+ // v0.38.20: captured pre-archive — archiveCurrentGoal clears
1271
+ // state.goal, so the record pointer must be computed here.
1272
+ const manualArchiveRecord = `— record: ${path.relative(ctx.cwd, archivedGoalPath(ctx.cwd, state.goal.id)) || archivedGoalPath(ctx.cwd, state.goal.id)}`;
1262
1273
  const archived = archiveCurrentGoal(ctx, "complete", terminalReason);
1263
1274
  if (!archived) {
1264
1275
  // The archive helper preserves the live objective and emits the
@@ -1274,7 +1285,14 @@ function registerAgentTools(pi: any): void {
1274
1285
  appendLedger(ctx.cwd, "goal_archive_failed_after_approval", { goalId: state.goal?.id, origin: "manual-verify", model: result.model });
1275
1286
  return { content: [{ type: "text", text: "The auditor approved, but the terminal archive could not be persisted. The goal is paused; fix persistence, resume, and retry complete_goal." }], details: {} };
1276
1287
  }
1277
- ctx.ui.notify(`✓ done — ${brief.outcome}\n${[...brief.details, `— auditor ${result.model} approved.`].join("\n")}`, "info");
1288
+ // v0.38.20: same approval voice as the detached path — the stale
1289
+ // pre-verdict `Next:` never reaches the chat.
1290
+ ctx.ui.notify(buildApprovalChatLines({
1291
+ outcome: brief.outcome,
1292
+ details: brief.details,
1293
+ approval: `— auditor ${result.model} approved.`,
1294
+ record: manualArchiveRecord,
1295
+ }).join("\n"), "info");
1278
1296
  notifyExternal(ctx, `Goal complete (auditor approved): ${recap}`);
1279
1297
  return { content: [{ type: "text", text: `Goal approved by auditor ${result.model}.` }], details: {} };
1280
1298
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goal-list-loop-audit",
3
- "version": "0.38.19",
3
+ "version": "0.38.20",
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. A detached extension-less auditor process re-verifies every completion with raw evidence without holding the main pi turn; confirmed drafts, decision pauses and consent gates keep you in charge.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "author": "dracon",