pi-goal-list-loop-audit 0.38.37 → 0.38.39
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,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.38.39 — Codex-like terminal summary (2026-09-09)
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Terminal `[goal-event]` summary was not Codex-like (field 20260909_013733):** `•` detail bullets followed by dangling `—` trailer lines, every bullet trailing into `…`, a raw `/var/tmp/…` log path in chat, and no next action. The render is now one voice: uniform `•` bullets (approval/counts/record ride as bullets, record stays last), machine paths (`/tmp/…`, `/var/tmp/…`, `*.tgz`, receipt-only paren groups) stripped from chat bullets (archive keeps the full text), `+` joins the clause-boundary cut set, and `withoutStaleNext` is selective — audit-self-referential Next lines still drop but the first concrete next action survives as the closing bullet ahead of the non-do.
|
|
8
|
+
- **Claim-side guidance:** continuation prompt budgets each label value to ~90 chars, bans machine paths from the recap, and asks for one concrete `Next:` or none.
|
|
9
|
+
|
|
10
|
+
### Verification
|
|
11
|
+
|
|
12
|
+
- New pins: uniform-voice, machine-path strip, `+`-boundary cut, next-action ordering, record-last; full `release:check` 0 failures; `tsc --noEmit` clean.
|
|
13
|
+
- Evidence: `audit/CODEX-SUMMARY-2026-09-09.md`. Archive format and delivery/replay mechanics unchanged.
|
|
14
|
+
|
|
15
|
+
## 0.38.38 — elapsed-time duplication cut + PR #47 closed (2026-09-09)
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **Elapsed time in two places (field 20260909_002132):** the `setStatus` one-liner no longer carries `total …` on the idle/busy/working branches — other sessions' tab strips showed `glla: [WORKING] total …` under a card already reading it. The card head keeps the timer unconditionally; state + task/queue counts stay on the status line. (`Chrome Bridge`/`(indefinite)` framing is pi core, not GLLA — only our timer text was cut.)
|
|
20
|
+
- **PR #47 triage:** KV-cache checkpoint complaint verified real on main but the strip shape is unsafe as-is (post-compaction blindness, no compensating pointer) — replied per-piece, gave the contributor a merge-based re-sync command, closed without merging per the #45/#46 precedent (live `active.jsonl`, 39-behind divergence, bundled features). Per-task audits stay held pending cost review.
|
|
21
|
+
|
|
22
|
+
### Verification
|
|
23
|
+
|
|
24
|
+
- New pins: status-without-timer + card-keeps-timer; full `release:check` 0 failures; `tsc --noEmit` clean.
|
|
25
|
+
- Evidence: `audit/ELAPSED-DUPLICATION-PR47-2026-09-09.md`. No open PRs.
|
|
26
|
+
|
|
3
27
|
## 0.38.37 — bullets + deliberate non-do (2026-09-09)
|
|
4
28
|
|
|
5
29
|
### 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.
|
|
21
|
+
v0.35.14–v0.38.39 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
|
|
@@ -86,7 +86,7 @@ export function isUsefulCompletionSummary(text: string | undefined): boolean {
|
|
|
86
86
|
* back to the word break, then to a hard cut only when the head holds
|
|
87
87
|
* no space past the halfway mark — a long token such as a commit hash
|
|
88
88
|
* must not eviscerate the whole value. Trailing punctuation is stripped
|
|
89
|
-
*
|
|
89
|
+
* `npm version…+ latest` cut inside a `+`-joined list). */
|
|
90
90
|
export function clipSummaryValue(value: string, limit: number): string {
|
|
91
91
|
const clean = value.replace(/\s+/g, " ").trim();
|
|
92
92
|
const capped = Number.isFinite(limit) ? Math.max(8, Math.floor(limit)) : 72;
|
|
@@ -95,16 +95,16 @@ export function clipSummaryValue(value: string, limit: number): string {
|
|
|
95
95
|
const floor = Math.max(16, Math.floor((capped - 1) * 0.4));
|
|
96
96
|
let boundary = -1;
|
|
97
97
|
for (let i = 0; i < head.length; i++) {
|
|
98
|
-
if (/[,;:·—–(\[]/.test(head[i]!)) boundary = i;
|
|
98
|
+
if (/[,;:·—–(+[\[]/.test(head[i]!)) boundary = i;
|
|
99
99
|
}
|
|
100
100
|
if (boundary >= floor) {
|
|
101
|
-
const cut = head.slice(0, boundary).replace(/[,;:·—–(\[\s]+$/u, "");
|
|
101
|
+
const cut = head.slice(0, boundary).replace(/[,;:·—–(+[\[\s]+$/u, "");
|
|
102
102
|
if (cut.length >= Math.min(floor, 16)) return `${cut}…`;
|
|
103
103
|
}
|
|
104
104
|
const space = head.lastIndexOf(" ");
|
|
105
105
|
const kept = (space > capped / 2 ? head.slice(0, space) : head)
|
|
106
106
|
.trimEnd()
|
|
107
|
-
.replace(/[,;:·—–(
|
|
107
|
+
.replace(/[,;:·—–(+\[]$/u, "");
|
|
108
108
|
return `${kept}…`;
|
|
109
109
|
}
|
|
110
110
|
|
|
@@ -152,6 +152,34 @@ export interface HumanCompletionBrief {
|
|
|
152
152
|
details: string[];
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
/** v0.38.39 (field 20260909_013733): absolute machine paths read as a
|
|
156
|
+
* machine receipt in user chat (`Tests: … (/var/tmp/glla-….log…`). The
|
|
157
|
+
* chat brief strips `/tmp/…`, `/var/tmp/…`, and `*.tgz` tokens so bullets
|
|
158
|
+
* carry human-readable proof (counts, versions, repo-relative paths);
|
|
159
|
+
* the durable archive keeps the full text. Falls back to the original
|
|
160
|
+
* when stripping would empty the value. */
|
|
161
|
+
export function chatSafeDetailValue(value: string): string {
|
|
162
|
+
const withoutGroups = value.replace(/\([^()]*\)/g, (group) => {
|
|
163
|
+
// A parenthesized group that carries nothing but stripped tokens and
|
|
164
|
+
// receipt words is machine packaging — drop the whole group so no
|
|
165
|
+
// `( tarball )` husk survives. Groups with real words stay verbatim.
|
|
166
|
+
const inner = group
|
|
167
|
+
.slice(1, -1)
|
|
168
|
+
.replace(/(?:\/var)?\/tmp\/\S+/g, "")
|
|
169
|
+
.replace(/\S+\.tgz\b/g, "")
|
|
170
|
+
.replace(/\btarballs?\b|\blogs?\b/gi, "");
|
|
171
|
+
return /[a-z]/i.test(inner) ? group : "";
|
|
172
|
+
});
|
|
173
|
+
const stripped = withoutGroups
|
|
174
|
+
.replace(/(?:\/var)?\/tmp\/\S+/g, "")
|
|
175
|
+
.replace(/\btarballs?\s+\S+\.tgz\b/gi, "")
|
|
176
|
+
.replace(/\S+\.tgz\b/g, "")
|
|
177
|
+
.replace(/\(\s*\)/g, "")
|
|
178
|
+
.replace(/\s{2,}/g, " ")
|
|
179
|
+
.trim();
|
|
180
|
+
return stripped || value;
|
|
181
|
+
}
|
|
182
|
+
|
|
155
183
|
/** The human briefing: outcome first in its own words, then only the
|
|
156
184
|
* labels that carry real content (filler like `Unresolved: none` or
|
|
157
185
|
* `Changed: not recorded` is dropped, never shown). The durable archive
|
|
@@ -170,7 +198,9 @@ export function humanCompletionBrief(
|
|
|
170
198
|
const separator = line.indexOf(":");
|
|
171
199
|
if (separator < 0) continue;
|
|
172
200
|
const content = briefValueContent(line.slice(separator + 1));
|
|
173
|
-
|
|
201
|
+
// v0.38.39: machine paths are archive evidence, not chat evidence —
|
|
202
|
+
// strip before clipping so the budget applies to the human text.
|
|
203
|
+
if (content) details.push(`${line.slice(0, separator)}: ${clipSummaryValue(chatSafeDetailValue(content), valueBudget)}`);
|
|
174
204
|
}
|
|
175
205
|
return { outcome, details };
|
|
176
206
|
}
|
|
@@ -178,10 +208,27 @@ export function humanCompletionBrief(
|
|
|
178
208
|
/** v0.38.20: the agent's `Next:` recap line goes stale the moment the
|
|
179
209
|
* verdict lands — reprinting it next to the approval trailer reads as
|
|
180
210
|
* complete-before-verify (field 2026-09-04: `Next: detached auditor
|
|
181
|
-
* verdict decides.` printed above `— auditor … approved.`).
|
|
182
|
-
*
|
|
211
|
+
* verdict decides.` printed above `— auditor … approved.`).
|
|
212
|
+
* v0.38.39 (field 20260909_013733): the Codex close wants at most ONE
|
|
213
|
+
* concrete next action, so the filter is now selective instead of total:
|
|
214
|
+
* self-referential audit/process lines (`verdict decides`, `awaiting
|
|
215
|
+
* approval`, …) still drop, but the first concrete action survives as
|
|
216
|
+
* the closing bullet. The full six-label record stays in the archive. */
|
|
217
|
+
const STALE_NEXT_PATTERN = /auditor|verdict|approv|audit|settl|review/i;
|
|
183
218
|
export function withoutStaleNext(details: string[] | undefined): string[] {
|
|
184
|
-
|
|
219
|
+
const kept: string[] = [];
|
|
220
|
+
let actionKept = false;
|
|
221
|
+
for (const detail of details ?? []) {
|
|
222
|
+
if (!/^\s*Next\s*:/i.test(detail)) {
|
|
223
|
+
kept.push(detail);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (STALE_NEXT_PATTERN.test(detail)) continue;
|
|
227
|
+
if (actionKept) continue;
|
|
228
|
+
actionKept = true;
|
|
229
|
+
kept.push(detail);
|
|
230
|
+
}
|
|
231
|
+
return kept;
|
|
185
232
|
}
|
|
186
233
|
|
|
187
234
|
/** v0.38.20: the approval chat notify — outcome first, all bounded
|
|
@@ -202,13 +249,24 @@ export function buildApprovalChatLines(notice: {
|
|
|
202
249
|
`✓ done — ${notice.outcome}`,
|
|
203
250
|
// v0.38.37 (audit 2026-09-08): one verifiable-result bullet per
|
|
204
251
|
// informing detail — the Codex closing shape.
|
|
252
|
+
// v0.38.39: the approval/counts/record trailer rides as bullets too
|
|
253
|
+
// (field 20260909_013733 — the `—` tail read as a second voice); the
|
|
254
|
+
// record pointer stays last.
|
|
205
255
|
...withoutStaleNext(notice.details).map((detail) => `• ${detail}`),
|
|
206
|
-
notice.approval,
|
|
207
|
-
...(notice.counts ? [notice.counts] : []),
|
|
208
|
-
notice.record,
|
|
256
|
+
trailerBullet(notice.approval),
|
|
257
|
+
...(notice.counts ? [trailerBullet(notice.counts)] : []),
|
|
258
|
+
trailerBullet(notice.record),
|
|
209
259
|
];
|
|
210
260
|
}
|
|
211
261
|
|
|
262
|
+
/** v0.38.39 (field 20260909_013733): the trailer rides as bullets too —
|
|
263
|
+
* the Codex close is uniform `•` lines (`• Changed:` … `• record:`),
|
|
264
|
+
* never a `•` block followed by dangling `—` lines. The `— ` sigil is
|
|
265
|
+
* stripped at render; the input strings keep it for non-chat surfaces. */
|
|
266
|
+
function trailerBullet(line: string): string {
|
|
267
|
+
return `• ${line.replace(/^—\s*/, "")}`;
|
|
268
|
+
}
|
|
269
|
+
|
|
212
270
|
/** v0.38.25: the audit-goal counts line — verdict proof ONLY, built from
|
|
213
271
|
* durable goal state. Raw run stats (turns/file-writes/bash calls) read as
|
|
214
272
|
* a machine receipt in user chat (field 2026-09-08 223522/223523); the
|
|
@@ -297,7 +355,7 @@ export function buildTerminalApprovalRender(input: TerminalApprovalRenderInput):
|
|
|
297
355
|
...(input.extras ?? []),
|
|
298
356
|
],
|
|
299
357
|
recap,
|
|
300
|
-
transcriptLines: [...withoutStaleNext(brief.details).map((detail) => `• ${detail}`), input.approval],
|
|
358
|
+
transcriptLines: [...withoutStaleNext(brief.details).map((detail) => `• ${detail}`), trailerBullet(input.approval)],
|
|
301
359
|
countsLine,
|
|
302
360
|
outcome: brief.outcome,
|
|
303
361
|
approval: input.approval,
|
|
@@ -1323,8 +1323,11 @@ function buildStatusTextBase(state: State, audit?: AuditDisplayProgress | null,
|
|
|
1323
1323
|
// state + counts only. Freshness tails (`last host activity`,
|
|
1324
1324
|
// `last stream`) duplicated the card-head `stream {age}` readout —
|
|
1325
1325
|
// one surface owns liveness so the two can never disagree.
|
|
1326
|
+
// v0.38.38 (field 20260909_002132): the elapsed `total …` duplicated
|
|
1327
|
+
// the card head too (other sessions' tab strips showed
|
|
1328
|
+
// `glla: [WORKING] total …` under a card already reading it) — the
|
|
1329
|
+
// card owns the timer now; the status line keeps state + counts.
|
|
1326
1330
|
const idleDetails = [
|
|
1327
|
-
goalTotalText(g, now),
|
|
1328
1331
|
(state.list?.length ?? 0) > 0 ? `${state.list!.length} queued` : "",
|
|
1329
1332
|
].filter(Boolean);
|
|
1330
1333
|
return `glla: ${activityStateBadge("IDLE", theme, "warning")}${idleDetails.length > 0 ? ` ${idleDetails.join(" · ")}` : ""}${heldSuffix}`;
|
|
@@ -1335,7 +1338,6 @@ function buildStatusTextBase(state: State, audit?: AuditDisplayProgress | null,
|
|
|
1335
1338
|
// like progress. Queued work is neither idle nor currently executing.
|
|
1336
1339
|
if (activity === "busy") {
|
|
1337
1340
|
const busyDetails = [
|
|
1338
|
-
goalTotalText(g, now),
|
|
1339
1341
|
g.taskList ? `${countDone(g)}/${countTotal(g)} tasks` : "",
|
|
1340
1342
|
(state.list?.length ?? 0) > 0 ? `${state.list!.length} queued` : "",
|
|
1341
1343
|
].filter(Boolean);
|
|
@@ -1370,11 +1372,11 @@ function buildStatusTextBase(state: State, audit?: AuditDisplayProgress | null,
|
|
|
1370
1372
|
const recovery = state.mainModelRecovery;
|
|
1371
1373
|
const blockedByRecovery = queued && recovery && recovery.retryAt;
|
|
1372
1374
|
const recoverySuffix = blockedByRecovery ? ` · parked on provider recovery` : "";
|
|
1373
|
-
// Keep the screenshot-proven order: state,
|
|
1374
|
-
//
|
|
1375
|
-
//
|
|
1375
|
+
// Keep the screenshot-proven order: state, then queue/task context.
|
|
1376
|
+
// It scans like a compact instrument readout and remains useful when
|
|
1377
|
+
// the above-editor card is hidden or scrolled away. Elapsed `total …`
|
|
1378
|
+
// lives on the card head only (v0.38.38, field 20260909_002132).
|
|
1376
1379
|
const details = [
|
|
1377
|
-
goalTotalText(g, now),
|
|
1378
1380
|
g.taskList ? `${countDone(g)}/${countTotal(g)} tasks` : "",
|
|
1379
1381
|
// v0.34.124: the QUEUED "why" — an accepted dispatch that pi has not
|
|
1380
1382
|
// started. (The last-activity age that used to ride here moved to the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.38.
|
|
3
|
+
"version": "0.38.39",
|
|
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",
|
|
@@ -197,4 +197,4 @@ The orchestrator's backstop is the stall watchdog: three consecutive turns with
|
|
|
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
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.
|
|
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. Keep each label value to one line of ~90 chars so every chat bullet fits without a trailing …; cite human-readable proof (counts, versions, repo-relative paths, PR numbers) and never machine paths (/var/tmp logs, tarballs — those live in the archive only). Cut values at clause boundaries, never mid-word; write Next: as one concrete follow-up action or none (audit-self-referential lines like "verdict decides" are stripped at render); pass what was deliberately left out as the complete_goal leftOut parameter — it renders as the closing bullet, and is omitted when absent.
|