pi-goal-list-loop-audit 0.38.35 → 0.38.37

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,30 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.38.37 — bullets + deliberate non-do (2026-09-09)
4
+
5
+ ### Fixed
6
+
7
+ - **Auditor disapproval of v0.38.36 answered:** the posted summary now carries one `•` bullet per informing detail on both posted surfaces (chat + transcript); new optional `complete_goal` `leftOut` parameter renders as the closing `• Left out:` bullet on all three approval sites (fresh, detached-retry, Esc-anyway). Absent stays absent — never invented, filler drops. Recap/archive format unchanged.
8
+ - **Stale pins updated deliberately:** approval-chat `deepEqual` expectations now bullet-prefixed.
9
+
10
+ ### Verification
11
+
12
+ - New pins: bullet count/evidence + present/absent/filler non-do (render unit), end-to-end `leftOut` claim → posted bullet (MockPi).
13
+ - Full `release:check`: 2040 pass / 2 skip / 0 fail across 204 files (`/var/tmp/glla-bullets-release-check2.log`, tarball `pi-goal-list-loop-audit-0.38.37.tgz`); `tsc --noEmit` clean.
14
+ - Evidence: `audit/HUMAN-VOICE-SUMMARY-2026-09-08.md`.
15
+
16
+ ## 0.38.36 — human-voice terminal summaries (2026-09-08)
17
+
18
+ ### Fixed
19
+
20
+ - **Summaries read as machine receipts (field 20260908_223522/223523):** `clipSummaryValue` now cuts at clause boundaries and strips stranded punctuation (`…append/replace Apply…`, never `…playlist auto-add,…`); the counts line is verdict-only (`— audit: auditor approved (1 verdict).`, raw run stats stay in the archive); the AUDIT PENDING notice states facts instead of agent imperatives. Agent-written summaries get Codex-shape guidance in the continuation prompt (outcome-first, verifiable bullets, clause cuts). Researched from real Codex transcripts + agy binary strings; user chose Codex-style chat shape.
21
+ - **Stale pins updated deliberately:** `— run:` telemetry assertions, `/detached auditor queued/` wordings, continuation-payload byte fixtures (+344/payload, growth exactly linear).
22
+
23
+ ### Verification
24
+
25
+ - Full `release:check`: 2038 pass / 2 skip / 0 fail across 204 files; `tsc --noEmit` clean.
26
+ - Evidence: `audit/HUMAN-VOICE-SUMMARY-2026-09-08.md`.
27
+
3
28
  ## 0.38.35 — closed card + smart summary (2026-09-08)
4
29
 
5
30
  ### 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.35 continue through the supervisor freeze (`/glla pause`),
21
+ v0.35.14–v0.38.37 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
@@ -78,18 +78,33 @@ export function isUsefulCompletionSummary(text: string | undefined): boolean {
78
78
  * Missing labels are retained as `not recorded` so a compact notification
79
79
  * cannot accidentally imply evidence that the durable recap did not contain.
80
80
  */
81
- /** Cut a summary value at a word boundary, never mid-word (the
82
- * Screenshot_20260903_204003/204005 complaint: `0 o…`, `qu…`, `belo…`).
83
- * Falls back to a hard cut only when the head holds no space past the
84
- * halfway mark — a long token such as a commit hash must not eviscerate
85
- * the whole value. Short values pass through untouched (no `…`). */
81
+ /** Cut a summary value at a clause boundary, never mid-word (field
82
+ * complaints 2026-09-03 `0 o…` and 2026-09-08 `playlist auto-add,…`:
83
+ * a word-boundary cut that strands dangling punctuation still reads as
84
+ * clipping, not summarizing). Prefer the last clause boundary
85
+ * (`, ; : · — – ( [`) past a floor so the cut reads intentional; fall
86
+ * back to the word break, then to a hard cut only when the head holds
87
+ * no space past the halfway mark — a long token such as a commit hash
88
+ * must not eviscerate the whole value. Trailing punctuation is stripped
89
+ * before the ellipsis. Short values pass through untouched (no `…`). */
86
90
  export function clipSummaryValue(value: string, limit: number): string {
87
91
  const clean = value.replace(/\s+/g, " ").trim();
88
92
  const capped = Number.isFinite(limit) ? Math.max(8, Math.floor(limit)) : 72;
89
93
  if (clean.length <= capped) return clean;
90
94
  const head = clean.slice(0, capped - 1);
95
+ const floor = Math.max(16, Math.floor((capped - 1) * 0.4));
96
+ let boundary = -1;
97
+ for (let i = 0; i < head.length; i++) {
98
+ if (/[,;:·—–(\[]/.test(head[i]!)) boundary = i;
99
+ }
100
+ if (boundary >= floor) {
101
+ const cut = head.slice(0, boundary).replace(/[,;:·—–(\[\s]+$/u, "");
102
+ if (cut.length >= Math.min(floor, 16)) return `${cut}…`;
103
+ }
91
104
  const space = head.lastIndexOf(" ");
92
- const kept = (space > capped / 2 ? head.slice(0, space) : head).trimEnd();
105
+ const kept = (space > capped / 2 ? head.slice(0, space) : head)
106
+ .trimEnd()
107
+ .replace(/[,;:·—–(\[]$/u, "");
93
108
  return `${kept}…`;
94
109
  }
95
110
 
@@ -185,22 +200,21 @@ export function buildApprovalChatLines(notice: {
185
200
  }): string[] {
186
201
  return [
187
202
  `✓ done — ${notice.outcome}`,
188
- ...withoutStaleNext(notice.details),
203
+ // v0.38.37 (audit 2026-09-08): one verifiable-result bullet per
204
+ // informing detail — the Codex closing shape.
205
+ ...withoutStaleNext(notice.details).map((detail) => `• ${detail}`),
189
206
  notice.approval,
190
207
  ...(notice.counts ? [notice.counts] : []),
191
208
  notice.record,
192
209
  ];
193
210
  }
194
211
 
195
- /** v0.38.25: the audit-goal counts line. Compact execution + audit proof
196
- * built ONLY from durable goal state — turns/file-writes/bash from
197
- * telemetry, verdict counts from auditHistory. Absent facts are named as
198
- * absent, never invented (the recorded-facts honesty rule). */
212
+ /** v0.38.25: the audit-goal counts line — verdict proof ONLY, built from
213
+ * durable goal state. Raw run stats (turns/file-writes/bash calls) read as
214
+ * a machine receipt in user chat (field 2026-09-08 223522/223523); the
215
+ * full execution evidence stays in the six-label archive record. Absent
216
+ * facts are named as absent, never invented (recorded-facts honesty). */
199
217
  export function buildAuditCountsLine(goal: Goal, auditNote?: string): string {
200
- const telemetry = goal.telemetry;
201
- const run = telemetry
202
- ? `${telemetry.turns} turns · ${telemetry.fileWrites} file writes · ${telemetry.bashCalls} bash calls`
203
- : "no execution telemetry was recorded";
204
218
  const history = goal.auditHistory ?? [];
205
219
  const latest = history.length > 0 ? history[history.length - 1] : undefined;
206
220
  const audit = auditNote ?? (latest === undefined
@@ -209,7 +223,7 @@ export function buildAuditCountsLine(goal: Goal, auditNote?: string): string {
209
223
  const verdict = latest.approved ? "approved" : latest.impossible ? "impossible" : latest.disapproved ? "disapproved" : "no verdict";
210
224
  return `auditor ${verdict} (${history.length} verdict${history.length === 1 ? "" : "s"})`;
211
225
  })());
212
- return `— run: ${run} · ${audit}.`;
226
+ return `— audit: ${audit}.`;
213
227
  }
214
228
 
215
229
  export interface TerminalApprovalRenderInput {
@@ -228,6 +242,8 @@ export interface TerminalApprovalRenderInput {
228
242
  countsLine?: string;
229
243
  /** Override for the audit half of the counts line. */
230
244
  auditNote?: string;
245
+ /** v0.38.37: what the agent deliberately left out (complete_goal leftOut). */
246
+ leftOut?: string;
231
247
  }
232
248
 
233
249
  export interface TerminalApprovalRender {
@@ -259,7 +275,15 @@ export function buildTerminalApprovalRender(input: TerminalApprovalRenderInput):
259
275
  };
260
276
  const candidate = input.completionSummary ?? input.goal.completionSummary;
261
277
  const recap = compactTerminalCompletionSummary(facts, candidate);
262
- const brief = terminalHumanBrief(facts, candidate);
278
+ const baseBrief = terminalHumanBrief(facts, candidate);
279
+ // v0.38.37 (audit 2026-09-08): the deliberate non-do comes from the
280
+ // agent's complete_goal leftOut claim — never invented. Filler ("none")
281
+ // drops via the same briefValueContent filter; absent stays absent.
282
+ const leftOut = input.leftOut?.trim();
283
+ const leftOutContent = leftOut ? briefValueContent(leftOut) : null;
284
+ const brief = leftOutContent
285
+ ? { ...baseBrief, details: [...baseBrief.details, `Left out: ${clipSummaryValue(leftOutContent, 120)}`] }
286
+ : baseBrief;
263
287
  const countsLine = input.countsLine ?? buildAuditCountsLine(input.goal, input.auditNote);
264
288
  return {
265
289
  chatLines: [
@@ -273,7 +297,7 @@ export function buildTerminalApprovalRender(input: TerminalApprovalRenderInput):
273
297
  ...(input.extras ?? []),
274
298
  ],
275
299
  recap,
276
- transcriptLines: [...withoutStaleNext(brief.details), input.approval],
300
+ transcriptLines: [...withoutStaleNext(brief.details).map((detail) => `• ${detail}`), input.approval],
277
301
  countsLine,
278
302
  outcome: brief.outcome,
279
303
  approval: input.approval,
@@ -280,6 +280,12 @@ export interface PendingCompletion {
280
280
  recoveryEpisodeKey?: string;
281
281
  /** Durable per-episode notice fence; display projections must consult it. */
282
282
  recoveryNoticeKeys?: string[];
283
+ /**
284
+ * v0.38.37: what the agent deliberately left out of this turn's work
285
+ * (complete_goal leftOut). Renders as the closing non-do bullet in the
286
+ * terminal user summary; absent means nothing was deliberately left out.
287
+ */
288
+ leftOut?: string;
283
289
  /**
284
290
  * Durable one-shot recovery fence. A parked claim may receive one
285
291
  * automatic retry after a validated healthy lifecycle/recovery event;
@@ -1439,6 +1439,8 @@ async function retryStoredCompletionAudit(origin: CompletionAuditOrigin = "provi
1439
1439
  completionSummary: state.goal.completionSummary,
1440
1440
  approval: `— auditor ${result.model} approved${approvalVia}.`,
1441
1441
  record: approvalRecord,
1442
+ // v0.38.37: the deliberate non-do the agent claimed, if any.
1443
+ ...(claim.leftOut ? { leftOut: claim.leftOut } : {}),
1442
1444
  extras: inspectionSessionPath
1443
1445
  ? [`Auditor session kept for review: pi --session ${inspectionSessionPath} (or pi --fork ${inspectionSessionPath}).`]
1444
1446
  : [],
@@ -519,6 +519,13 @@ function registerAgentTools(pi: any): void {
519
519
  "and cross-checks the verification claim against real artifacts (see audit/COMPLETION-SUMMARY-POLICY-2026-08-19.md).",
520
520
  })),
521
521
  newObjective: Type.Optional(Type.String({ description: "v0.25.0 (contract item 15): when the work has legitimately shifted, pass the new objective here — it atomically replaces the goal objective AND the audit proceeds against the NEW objective in this same call. Do not use to dodge a legitimate disapproval; the auditor sees the change." })),
522
+ leftOut: Type.Optional(Type.String({
523
+ maxLength: 500,
524
+ description:
525
+ "v0.38.37: what this turn deliberately left out (scope cut, deferred item + why, in plain words). " +
526
+ "Renders as the closing `• Left out:` bullet in the user-facing terminal summary. " +
527
+ "Omit when nothing was deliberately left out — absent stays absent, never invented.",
528
+ })),
522
529
  }),
523
530
  async execute(_id, params, signal, _onUpdate, execCtx) {
524
531
  const foreign0 = foreignToolGuard(execCtx);
@@ -542,7 +549,7 @@ function registerAgentTools(pi: any): void {
542
549
  }
543
550
  return { content: [{ type: "text", text: `No active goal — it is ${state.goal.status}.` }], details: {} };
544
551
  }
545
- const p = params as { completionSummary?: string; verificationSummary?: string; newObjective?: string };
552
+ const p = params as { completionSummary?: string; verificationSummary?: string; newObjective?: string; leftOut?: string };
546
553
  if (state.goal.repairTarget) {
547
554
  return {
548
555
  content: [{ type: "text", text: `This repair card cannot be completed yet. Redraft the original target as a confirmed task list with propose_task_list (include objective: ${state.goal.repairTarget.objective.slice(0, 180)}), then continue the real work.` }],
@@ -742,6 +749,9 @@ function registerAgentTools(pi: any): void {
742
749
  const completionClaim = beginCompletionAudit(ctx, {
743
750
  completionSummary: finalSummary,
744
751
  verificationSummary: p.verificationSummary,
752
+ // v0.38.37: the deliberate non-do rides the pending claim into
753
+ // the terminal render — the only source the summary may cite.
754
+ ...(p.leftOut?.trim() ? { leftOut: p.leftOut.trim().slice(0, 500) } : {}),
745
755
  at: nowIso(),
746
756
  }, "complete-goal");
747
757
  if (!completionClaim) {
@@ -1194,6 +1204,9 @@ function registerAgentTools(pi: any): void {
1194
1204
  // Escape hatch: the user aborted the audit (Esc). Offer the explicit
1195
1205
  // choice — complete WITHOUT audit, or keep working. (pi-goal-x parity.)
1196
1206
  if (result.error === "Auditor aborted.") {
1207
+ // v0.38.37: capture the agent-claimed non-do before the claim is
1208
+ // cleared below — the Esc-complete path renders the same voice.
1209
+ const escLeftOut = state.goal.pendingCompletion?.leftOut;
1197
1210
  updateGoal({ status: "active", auditHistory: history, pendingCompletion: undefined, pauseReason: "audit aborted by user (Esc)" }, ctx);
1198
1211
  const abortConfirmCtx = freshCtxForGeneration(auditGeneration);
1199
1212
  if (!abortConfirmCtx) return staleToolResult();
@@ -1232,6 +1245,9 @@ function registerAgentTools(pi: any): void {
1232
1245
  approval: `— completed without audit (your choice).`,
1233
1246
  record: `— record: ${escArchivePath}`,
1234
1247
  auditNote: "completed without audit (your choice)",
1248
+ // v0.38.37: the claim was cleared pre-confirm; the non-do was
1249
+ // captured from it above.
1250
+ ...(escLeftOut ? { leftOut: escLeftOut } : {}),
1235
1251
  });
1236
1252
  if (!archiveCurrentGoal(ctx, "complete", terminalReason)) {
1237
1253
  return {
@@ -1286,6 +1302,8 @@ function registerAgentTools(pi: any): void {
1286
1302
  completionSummary: state.goal.completionSummary,
1287
1303
  approval: `— auditor ${result.model} approved.`,
1288
1304
  record: manualArchiveRecord,
1305
+ // v0.38.37: the deliberate non-do rides the durable claim.
1306
+ ...(durableCompletionClaim.leftOut ? { leftOut: durableCompletionClaim.leftOut } : {}),
1289
1307
  extras: inspectionSessionPath
1290
1308
  ? [`Auditor session kept for review: pi --session ${inspectionSessionPath} (or pi --fork ${inspectionSessionPath}).`]
1291
1309
  : [],
@@ -1876,7 +1894,7 @@ function registerAgentTools(pi: any): void {
1876
1894
  if (notifyFailure) current.ui.notify(`Completion auditor worker failed to settle (infrastructure, not a verdict). The stored claim is safe; ${activeGoalSurfaceCommand("resume")} retries it.`, "warning");
1877
1895
  });
1878
1896
  return {
1879
- content: [{ type: "text", text: `AUDIT PENDING — nonterminal. Completion claim persisted; detached auditor queued (model: ${via ?? "setting"}). The goal is not approved or complete. Do not claim completion or give a final success summary. Do not wait or poll: GLLA will post the concrete final summary after verified approval and durable archive; rejection resumes work.` }],
1897
+ content: [{ type: "text", text: `AUDIT PENDING — nonterminal. The completion claim is stored and a detached auditor is settling it (model: ${via ?? "setting"}). The goal is not approved or complete yet. The final summary posts here after verified approval and durable archive; rejection resumes work automatically. Ending this turn now is correct — no waiting or polling needed.` }],
1880
1898
  details: { status: "audit-pending", terminal: false },
1881
1899
  // End this tool batch, not the goal; the detached worker owns settlement.
1882
1900
  terminate: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goal-list-loop-audit",
3
- "version": "0.38.35",
3
+ "version": "0.38.37",
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",
@@ -196,3 +196,5 @@ The orchestrator's backstop is the stall watchdog: three consecutive turns with
196
196
  ### Completion communication
197
197
 
198
198
  `complete_goal` submits a nonterminal claim, not a completion verdict. While the detached audit is pending, do not say the goal is done/complete/approved or present a final success summary. Do not wait or poll. If a response is needed, give one brief pending-status sentence. GLLA posts the concrete outcome-first summary into chat after verified approval and durable archive, without needing another user prompt. Do not duplicate that summary. A rejected claim remains unfinished and repair work continues.
199
+
200
+ The agent-written completionSummary is user-facing evidence, not a step log: open with the outcome in plain words, then one verifiable result per label with its proof inline (test counts, file paths, commit refs) — never raw run stats. Cut values at clause boundaries, never mid-word; pass what was deliberately left out as the complete_goal leftOut parameter — it renders as the closing bullet, and is omitted when absent.