pi-goal-list-loop-audit 0.38.13 → 0.38.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.38.14 — human end-of-objective briefing (2026-09-04)
4
+
5
+ ### Changed
6
+ Every `✓ done` chat notify is now a human briefing: outcome first in its own words, only informing labels after it (`none` / `not recorded` / empty filler dropped; `none — <content>` keeps the content). Word-boundary cuts, auditor/no-audit trailer, archive keeps the full six-label record. Runbook in `audit/HUMAN-BRIEF-2026-09-04.md`.
7
+
3
8
  ## 0.38.13 — good completion summary (2026-09-04)
4
9
 
5
10
  ### Changed
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.13 continue through the supervisor freeze (`/glla pause`),
21
+ v0.35.14–v0.38.14 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
@@ -116,6 +116,49 @@ export function compactCompletionSummary(text: string | undefined, maxValueLengt
116
116
  return parts.join(" · ");
117
117
  }
118
118
 
119
+ /** A recap value informs the human only when it is not an empty / none /
120
+ * not-recorded placeholder. `None — <real content>` (a common agent habit)
121
+ * keeps just the content. Returns null for filler. */
122
+ export function briefValueContent(value: string): string | null {
123
+ const clean = value.replace(/\s+/g, " ").trim();
124
+ if (!clean) return null;
125
+ // System placeholder vocabulary: the `— explanation` is still a placeholder.
126
+ if (/^not recorded\b/i.test(clean)) return null;
127
+ // Agent habit: `none — <real content>` hides information behind filler.
128
+ const prefixed = /^none\s*[—–:\-]\s*(.+)$/i.exec(clean);
129
+ const body = (prefixed?.[1] ?? clean).trim();
130
+ if (/^(none|n\/a|nil|nothing)(\s+for\s+this\s+\w+)?\.?$/i.test(body)) return null;
131
+ return body;
132
+ }
133
+
134
+ export interface HumanCompletionBrief {
135
+ outcome: string;
136
+ details: string[];
137
+ }
138
+
139
+ /** The human briefing: outcome first in its own words, then only the
140
+ * labels that carry real content (filler like `Unresolved: none` or
141
+ * `Changed: not recorded` is dropped, never shown). The durable archive
142
+ * keeps the full six-label record; this is the end-of-objective voice
143
+ * that informs the user at a glance. */
144
+ export function humanCompletionBrief(
145
+ text: string | undefined,
146
+ outcomeBudget = 140,
147
+ valueBudget = 120,
148
+ ): HumanCompletionBrief {
149
+ const lines = completionSummaryLines(text, Math.max(outcomeBudget, valueBudget));
150
+ const rawOutcome = (lines[0] ?? "").replace(/^Outcome:\s*/, "");
151
+ const outcome = clipSummaryValue(briefValueContent(rawOutcome) ?? "done", outcomeBudget);
152
+ const details: string[] = [];
153
+ for (const line of lines.slice(1)) {
154
+ const separator = line.indexOf(":");
155
+ if (separator < 0) continue;
156
+ const content = briefValueContent(line.slice(separator + 1));
157
+ if (content) details.push(`${line.slice(0, separator)}: ${clipSummaryValue(content, valueBudget)}`);
158
+ }
159
+ return { outcome, details };
160
+ }
161
+
119
162
  /** Multi-line projection: one `Label: value` line per label with generous
120
163
  * word-bounded values. This is the user-facing `✓ done` block — six short
121
164
  * facts that stay scannable in chat. The single-line projection remains
@@ -236,6 +279,15 @@ export function compactTerminalCompletionSummary(
236
279
  return compactCompletionSummary(resolveCompletionSummary(facts, candidate).summary, maxValueLength);
237
280
  }
238
281
 
282
+ /** Brief twin of compactTerminalCompletionSummary for the `✓ done` chat
283
+ * notifies: same resolved facts, outcome + informing labels only. */
284
+ export function terminalHumanBrief(
285
+ facts: CompletionSummaryFacts,
286
+ candidate = facts.goal.completionSummary,
287
+ ): HumanCompletionBrief {
288
+ return humanCompletionBrief(resolveCompletionSummary(facts, candidate).summary);
289
+ }
290
+
239
291
  /** Multi-line twin of compactTerminalCompletionSummary for the `✓ done`
240
292
  * chat notifies: same resolved facts, one `Label: value` line each. */
241
293
  export function terminalCompletionSummaryLines(
@@ -240,7 +240,7 @@ import {
240
240
  pushCapped as pushRepetitionCapped,
241
241
  } from "../goal-loop-repetition.js";
242
242
  import { buildStatusText, buildWidgetLines, type AuditDisplayProgress } from "../goal-loop-display.js";
243
- import { compactCompletionSummary, compactTerminalCompletionSummary, isGenericCompletionSummary, missingCompletionSummaryLabels, terminalCompletionSummaryLines } from "../completion-summary.js";
243
+ import { compactCompletionSummary, compactTerminalCompletionSummary, isGenericCompletionSummary, missingCompletionSummaryLabels, terminalHumanBrief } from "../completion-summary.js";
244
244
  import {
245
245
  defaultAgentDir,
246
246
  resolveEffectiveSubagentModel,
@@ -1416,7 +1416,7 @@ async function retryStoredCompletionAudit(origin: CompletionAuditOrigin = "provi
1416
1416
  archivePath: path.relative(liveCtx.cwd, archivedGoalPath(liveCtx.cwd, state.goal.id)) || archivedGoalPath(liveCtx.cwd, state.goal.id),
1417
1417
  }, state.goal.completionSummary);
1418
1418
  // Computed pre-archive: archiveCurrentGoal clears state.goal.
1419
- const recapLines = terminalCompletionSummaryLines({
1419
+ const brief = terminalHumanBrief({
1420
1420
  goal: state.goal,
1421
1421
  status: "complete",
1422
1422
  stopReason: terminalReason,
@@ -1438,10 +1438,10 @@ async function retryStoredCompletionAudit(origin: CompletionAuditOrigin = "provi
1438
1438
  appendLedger(liveCtx.cwd, "goal_archive_failed_after_approval", { goalId, attemptId: claim.attemptId, origin });
1439
1439
  return;
1440
1440
  }
1441
- // v0.38.13: the chat notify carries the six-label block (one fact per
1442
- // line, word-bounded) instead of the single-line mash — the external
1443
- // notify keeps the compact single line (pager/sound safe).
1444
- liveCtx.ui.notify(`✓ done — auditor ${result.model} approved${approvalVia}.\n${recapLines.join("\n")}`, "info");
1441
+ // v0.38.14: the chat notify is the human briefing — outcome first,
1442
+ // filler labels dropped — while the external notify keeps the compact
1443
+ // single line (pager/sound safe).
1444
+ liveCtx.ui.notify(`✓ done — ${brief.outcome}\n${[...brief.details, `— auditor ${result.model} approved${approvalVia}.`].join("\n")}`, "info");
1445
1445
  notifyExternal(liveCtx, `Goal complete (auditor approved, ${origin}): ${recap}`);
1446
1446
  return;
1447
1447
  }
@@ -250,7 +250,7 @@ import {
250
250
  pushCapped as pushRepetitionCapped,
251
251
  } from "../goal-loop-repetition.js";
252
252
  import { buildStatusText, buildWidgetLines, type AuditDisplayProgress } from "../goal-loop-display.js";
253
- import { compactCompletionSummary, compactTerminalCompletionSummary, resolveCompletionSummary, terminalCompletionSummaryLines } from "../completion-summary.js";
253
+ import { compactCompletionSummary, compactTerminalCompletionSummary, resolveCompletionSummary, terminalHumanBrief } from "../completion-summary.js";
254
254
  import {
255
255
  defaultAgentDir,
256
256
  resolveEffectiveSubagentModel,
@@ -1216,15 +1216,16 @@ function registerAgentTools(pi: any): void {
1216
1216
  details: {},
1217
1217
  };
1218
1218
  }
1219
- const recapLines = terminalCompletionSummaryLines({
1219
+ const brief = terminalHumanBrief({
1220
1220
  goal: terminalGoal,
1221
1221
  status: "complete",
1222
1222
  stopReason: terminalReason,
1223
1223
  archivePath: path.relative(ctx.cwd, archivedGoalPath(ctx.cwd, terminalGoal.id)) || archivedGoalPath(ctx.cwd, terminalGoal.id),
1224
1224
  });
1225
- ctx.ui.notify(`✓ done without audit (user choice):\n${recapLines.join("\n")}`, "info");
1225
+ const briefBlock = [...brief.details, `— completed without audit (your choice).`].join("\n");
1226
+ ctx.ui.notify(`✓ done — ${brief.outcome}\n${briefBlock}`, "info");
1226
1227
  notifyExternal(ctx, `Goal complete without audit (user choice): ${recap}`);
1227
- return { content: [{ type: "text", text: `Goal marked complete without audit (user choice).\n\n${recapLines.join("\n")}` }], details: {} };
1228
+ return { content: [{ type: "text", text: `Goal marked complete without audit (user choice).\n\n${briefBlock}` }], details: {} };
1228
1229
  }
1229
1230
  scheduleContinuation(ctx, true);
1230
1231
  return {
@@ -1250,7 +1251,7 @@ function registerAgentTools(pi: any): void {
1250
1251
  archivePath: path.relative(ctx.cwd, archivedGoalPath(ctx.cwd, state.goal.id)) || archivedGoalPath(ctx.cwd, state.goal.id),
1251
1252
  }, state.goal.completionSummary);
1252
1253
  // Computed pre-archive: archiveCurrentGoal clears state.goal.
1253
- const recapLines = terminalCompletionSummaryLines({
1254
+ const brief = terminalHumanBrief({
1254
1255
  goal: state.goal,
1255
1256
  status: "complete",
1256
1257
  stopReason: terminalReason,
@@ -1271,7 +1272,7 @@ function registerAgentTools(pi: any): void {
1271
1272
  appendLedger(ctx.cwd, "goal_archive_failed_after_approval", { goalId: state.goal?.id, origin: "manual-verify", model: result.model });
1272
1273
  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: {} };
1273
1274
  }
1274
- ctx.ui.notify(`✓ done — auditor ${result.model} approved.\n${recapLines.join("\n")}`, "info");
1275
+ ctx.ui.notify(`✓ done — ${brief.outcome}\n${[...brief.details, `— auditor ${result.model} approved.`].join("\n")}`, "info");
1275
1276
  notifyExternal(ctx, `Goal complete (auditor approved): ${recap}`);
1276
1277
  return { content: [{ type: "text", text: `Goal approved by auditor ${result.model}.` }], details: {} };
1277
1278
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goal-list-loop-audit",
3
- "version": "0.38.13",
3
+ "version": "0.38.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. 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",