pi-goal-list-loop-audit 0.35.13 → 0.35.32

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.
Files changed (41) hide show
  1. package/CHANGELOG.md +366 -0
  2. package/INSTALL.md +3 -3
  3. package/LIST-PHILOSOPHY.md +90 -0
  4. package/PLAN.md +316 -0
  5. package/README.md +108 -13
  6. package/docs/DESIGN-subagent-visibility.md +76 -0
  7. package/docs/DESIGN.md +59 -8
  8. package/docs/GLLA-POSITIONING-AND-DECOMPOSITION-2026-08-08.md +6 -1
  9. package/docs/INDEX.md +4 -0
  10. package/docs/VISION-ASSIST.md +2 -2
  11. package/extensions/auditor-extensions.ts +289 -0
  12. package/extensions/goal-agents-panel.ts +185 -0
  13. package/extensions/goal-commands.ts +120 -6
  14. package/extensions/goal-continuation.ts +45 -2
  15. package/extensions/goal-heartbeat.ts +176 -5
  16. package/extensions/goal-loop-auditor-process.ts +26 -0
  17. package/extensions/goal-loop-auditor.ts +3 -5
  18. package/extensions/goal-loop-backoff.ts +79 -0
  19. package/extensions/goal-loop-core.ts +97 -4
  20. package/extensions/goal-loop-display.ts +107 -8
  21. package/extensions/goal-loop-forever.ts +39 -1
  22. package/extensions/goal-loop-shield.ts +114 -25
  23. package/extensions/goal-loop.ts +66 -1
  24. package/extensions/goal-recovery.ts +8 -2
  25. package/extensions/goal-settings.ts +23 -0
  26. package/extensions/goal-state.ts +11 -0
  27. package/extensions/loops/goal-activation.ts +226 -25
  28. package/extensions/loops/goal-auditor-hooks.ts +39 -3
  29. package/extensions/loops/goal-list-queue.ts +17 -1
  30. package/extensions/loops/goal-orchestrator.ts +28 -1
  31. package/extensions/loops/goal-settings-ui.ts +96 -5
  32. package/extensions/loops/goal-tools.ts +82 -28
  33. package/extensions/loops/goal-ui.ts +105 -1
  34. package/extensions/loops/goal.ts +7 -7
  35. package/extensions/multi-model-picker.ts +45 -7
  36. package/extensions/settings-menu.ts +10 -0
  37. package/package.json +8 -3
  38. package/schemas/goal.schema.json +2 -0
  39. package/scripts/goal-auditor-launch.mjs +19 -1
  40. package/scripts/goal-auditor-worker.mjs +15 -0
  41. package/scripts/verify-auditor-extensions-offline.mjs +59 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,371 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.35.32 — hermetic settings round-trip test (2026-08-22)
4
+
5
+ ### Fix
6
+ tests/auditor-extensions.test.ts depended on the developer machine having
7
+ ~/.pi/agent extensions to discover: on a bare CI home the discovered list
8
+ was empty, the handler fell back to the input prompt, and the TUI-picker
9
+ branch under test never ran (publish workflow failure). The test now seeds
10
+ a project-scope `.pi/extensions/hermetic-ext.js` so discovery is non-empty
11
+ on every machine; verified green under both an empty HOME and a populated one.
12
+
13
+ ## 0.35.31 — user-seed trust for /goal start; loop plateau no longer false-stops on a never-moved baseline (2026-08-22)
14
+
15
+ ### Fix 1: explicit /goal start paused by the suspicious-objective heuristic
16
+ Field: Screenshot_20260822_193744 — `/goal start "…because we are logged in"`
17
+ was parked as "Suspicious objective detected (dangling-fragment)" with a
18
+ repair task queued instead of dispatching. The fragment heuristics exist
19
+ for AGENT-authored report garbage; an explicit `/goal start` whose
20
+ objective is verbatim a user seed now dispatches and ledgers
21
+ `faulty_objective_user_seed_trusted` (/goal tweak remains available).
22
+ ### Fix 2: loop plateau vs a degenerate zero baseline
23
+ Field: doomtap loop stopped "plateau — best: 0" while iterations visibly
24
+ fixed real findings: a min-direction metric reading 0 before work starts
25
+ pins best at 0, so every later productive reading scores flat and burns
26
+ plateau slots. Flat readings now count toward plateau only once the metric
27
+ has demonstrably moved (an improvement on record, or best ≠ first measured
28
+ reading of the run); a metric that NEVER moves gets its own loud bounded
29
+ stop ("metric never moved …") after 2× window, not a fake plateau.
30
+
31
+ ## 0.35.30 — durable last-outcome retention: the final verdict stays visible (2026-08-22)
32
+
33
+ ### Gap
34
+ Field report with screenshots (email-api-compare, 2026-08-22): "goal gets
35
+ closed before final audit, so auditor never approves." Forensics showed the
36
+ lifecycle was CORRECT — every archived goal had an approving verdict — but
37
+ closeArchivedSlot nulled the widget slot the moment the goal archived, so
38
+ after the agent's turn ended the surface went completely blank and the only
39
+ trace of the approval was one transient toast. Returning later, "Auditor
40
+ verdict pending" was the last visible text: indistinguishable from closed-
41
+ without-audit.
42
+ ### Ship
43
+ - State.lastOutcome {at, ok, title, recap}: written by closeArchivedSlot on
44
+ every terminal slot close (approved AND aborted), overwritten per outcome.
45
+ - Widget: while no goal/list/loop occupies the slot, one dim retention line
46
+ renders for 24h — "✓ done · auditor … approved · <recap>" or "▪ ended ·
47
+ <reason>" — then goes silent. A live goal always outranks it.
48
+ - /glla wipe clears the record (clean slate means clean).
49
+ - Tests: tests/last-outcome-retention.test.ts (5) — render shapes, expiry +
50
+ garbage-timestamp safety, live-goal precedence, source pins for both
51
+ write and wipe-clear sites.
52
+
53
+ ## 0.35.29 — /glla agents: tracked-subagent panel, transcript tail, widget segment (2026-08-22, GitHub issue #15)
54
+
55
+ ### Gap
56
+ During long fan-outs the only child visibility was the widget's 3-slot
57
+ recent-action ring. A child that "almost completed its final report, went
58
+ back to check some more, then crashed" was invisible: no live status, no
59
+ counters, and no post-mortem trail anyone could find (issue #15).
60
+ Scope agreed with the user: panel + transcript tail + widget line; a live
61
+ activity stream was explicitly rejected as too noisy.
62
+ ### Ship
63
+ - getSubagentAgentsSnapshot() (goal-heartbeat.ts): read-only view of the
64
+ tracked-subagent probes with hung classification mirroring the watchdog
65
+ scan WITHOUT its counter mutation; record-frozen vs event-only evidence
66
+ named; degrades gracefully when the pi-subagents manager registry is
67
+ absent (as on currently installed versions).
68
+ - /glla agents: ranked table (hung > running > ended), per-child
69
+ tools/output/silent clocks, liveness hint on hung rows, 20-row cap with
70
+ an explicit trim notice. Read-only, stale-safe.
71
+ - /glla agents --tail <id> [--lines N]: locates the child's session file
72
+ in the cwd-munged session store by needle + newest mtime and prints the
73
+ last N entries tolerantly ([role] text, raw fallback). LOUD when nothing
74
+ matches — searched dir, transcript count, needles. Never resumes or
75
+ attaches to a child session.
76
+ - Widget segment: "● N agents · <busiest> silent Xm ⚠" appended to every
77
+ card shape via buildWidgetLines; hidden at zero tracked children.
78
+ - New pure module extensions/goal-agents-panel.ts; snapshot reaches
79
+ goal-commands via CommandDeps injection (no heartbeat import cycle).
80
+
81
+ ## 0.35.28 — due-wait backstop: lapsed wait pauses actually resume; "you were recovered" notice (2026-08-22, GitHub issue #16)
82
+
83
+ ### Root cause (field: goal paused 30min past its scheduled auto-resume while the agent narrated "the system should have auto-resumed by now")
84
+ Auto-resume for pauseKind "wait" relied SOLELY on in-memory timers. An
85
+ exhaustive map of every wait-pause site found: agent-authored waits
86
+ (pause_goal kind="wait") armed NO timer at all while their own copy
87
+ promised automatic continuation; error-brake cooldown waits were not
88
+ re-armed on session_start; single-slot provider-retry timers could be
89
+ silently clobbered by a later schedule; and no code path anywhere compared
90
+ wall time against pauseResumeAt outside display rendering.
91
+ ### Fix
92
+ The heartbeat owns the durable invariant now: every tick, a wait whose
93
+ pauseResumeAt lapsed >90s is re-fired — main-model recovery waits route to
94
+ a provider probe, everything else clears the park and dispatches one fresh
95
+ continuation. supervisorPaused() still freezes it under /glla pause and
96
+ the load hold, one attempt per (goalId:resumeAt) key prevents storms (the
97
+ route re-parks with a fresh resumeAt on failure), and every fire is
98
+ ledgered wait_pause_overdue_resume. A stale hold persisted by a previous
99
+ process is released when a consenting reload arrives. Issue part 2:
100
+ resumed goals carry an autoResumed stamp rendered as a RECOVERY NOTICE in
101
+ the continuation prompt — "welcome back, YOU were recovered" — so agents
102
+ stop waiting for an external recovery signal that already happened.
103
+
104
+ ## 0.35.27 — Windows auditor launch: quote only when needed, gate always first (2026-08-22, PR #17)
105
+
106
+ ### Field report (PR #17, reproduced on Windows 11 + pnpm global shim)
107
+ The detached auditor died ~0.5s after launch and retried forever: quoting
108
+ EVERY argument wraps a bare executable name in quotes, which changes how
109
+ cmd.exe resolves it and how npm/pnpm .CMD shims compute their own
110
+ directory -> MODULE_NOT_FOUND -> "pi exited without an agent_settled RPC
111
+ event" in a 60s retry loop of flashing terminal windows.
112
+ ### Fix
113
+ buildAuditorPiSpawnSpec now runs the WINDOWS_UNSAFE_ARG rejection on EVERY
114
+ argument BEFORE the quoting decision, then quotes only when tokenization
115
+ requires it (whitespace / cmd metacharacters / empty). Clean bare tokens
116
+ reach cmd.exe untouched (shims resolve; full RPC sessions work); the
117
+ upstream PR's variant was not mergeable as-is because its needs-quoting
118
+ regex also gated the unsafe-arg check, letting %/CR/LF through bare.
119
+ Regression tests pin all three classes through the spec builder.
120
+
121
+ ## 0.35.26 — zombie watchdog recognizes pi-subagents tool names (2026-08-22, GitHub issue #13)
122
+
123
+ ### Gap
124
+ The v0.35.4 subagent-wait carve-out matched only the legacy built-in names
125
+ (Agent / get_subagent_result / steer_subagent). The pi-subagents extension
126
+ registers its foreground dispatch tool as "subagent" and a blocking wait as
127
+ "subagent_wait", so a parent legitimately BUSY on a healthy foreground child
128
+ tripped the bounded abort: field report shows a child writing Postgres
129
+ records productively for 30 minutes while the parent was stream-silent on
130
+ `subagent` — zombie_run_suspected at 20m, loop_stopped + zombie_run_aborted
131
+ at 30m, productive work killed mid-write.
132
+ ### Fix
133
+ One shared SUBAGENT_WAIT_TOOL_NAMES set + isSubagentWaitCall predicate in
134
+ goal-heartbeat.ts, consumed by BOTH sites (zombie stand-down and wedge-alert
135
+ hint) so the lists cannot drift apart again. New names: "subagent",
136
+ "subagent_wait". Behavioral tests drive the real heartbeat tick with a real
137
+ tool_call event: stand-down while in flight, clean abort once it settles,
138
+ no blanket amnesty.
139
+
140
+ ## 0.35.25 — /loop resume honors the zero-stream abort park (2026-08-22, GitHub issue #14)
141
+
142
+ ### Gap
143
+ abortZombieRun parks a loop with stopReason "stopped: automatic zero-stream
144
+ abort — ... (iteration N preserved; /loop resume to retry)" and its message
145
+ promises /loop resume — but the RESUMABLE_STOP predicate in the resume
146
+ handler never matched that prefix. The explicit resume answered "No held
147
+ loop to resume"; iteration count, best value, and preserved history were
148
+ unreachable without re-drafting from scratch (field report: a metricless
149
+ 24h loop parked at iteration 210 with 200 history entries).
150
+ ### Fix
151
+ RESUMABLE_STOP gains the "stopped: automatic zero-stream abort" prefix.
152
+ The explicit resume now re-arms the loop exactly as promised: fresh stall
153
+ window, re-armed counters, load hold released, one new dispatch — with
154
+ iteration/best/history intact. Control test pins that non-resumable stops
155
+ (e.g. bounds) stay stopped.
156
+
157
+ ## 0.35.24 — auditor model picker at full selector parity: forbidden-models filtering (2026-08-22, note.md Next #1)
158
+
159
+ ### Gap
160
+ The /glla -> Auditor model row already hosted the /model-style fuzzy
161
+ picker and persisted to the exact key resolveAuditorModel reads — but
162
+ unlike every main-agent flow it did NOT apply forbidden-models policy:
163
+ blocked models appeared in the list and the typed escape hatch accepted
164
+ them, yielding pins the resolver silently skips at audit time.
165
+ ### Fix
166
+ promptModelRef gains an excludeRefs opt threading into buildModelPickItems
167
+ (list-level filter) AND validating typed entries against isForbiddenModel
168
+ (a policy match is refused with a warning naming the ref — never saved).
169
+ Both auditor slots use it: Auditor model and Auditor fallback agent.
170
+ A pin saved by the picker is one the resolver honors; runtime skips
171
+ (auditor_model_fallback reason:"forbidden") remain as belt-and-suspenders.
172
+
173
+ ## 0.35.23 — load without autostart: cold sessions hold automation for an explicit decision (2026-08-22, note.md Next #2)
174
+
175
+ ### Root cause
176
+ shouldAutoResumeOnSessionStart already demanded explicit `autoResume ===
177
+ true` (v0.28.21 tri-state, undefined default = HOLD) — but its only
178
+ consumer fed it the AGGRESSIVE-MODE COERCED value (unset -> true because
179
+ aggressiveMode defaults on), so stock installs auto-resumed everything on
180
+ every session load despite the documented default. Three further paths
181
+ bypassed the consent entirely.
182
+ ### Fixes
183
+ - Load consent now reads the RAW global autoResume setting; aggressive
184
+ mode keeps owning its caps only. Default (unset/false) = restore and
185
+ DISPLAY state, hold automation.
186
+ - New durable loadHoldAt state engages through the SAME freeze gates as
187
+ /glla pause (continuation dispatch, loop ticks, heartbeat refires,
188
+ recovery timers); released by any explicit work command (/goal resume,
189
+ /list resume, /list next, /loop resume|start, new goal creation),
190
+ each release ledgered load_hold_released. Heartbeat host-loss
191
+ supervision stays armed under the hold — a held plane is never an
192
+ unprobed idle plane.
193
+ - Closed consent bypasses: different-pid crash successors no longer
194
+ auto-resume held loops or replay journals as automation (same-process
195
+ /reload successors keep continuity); parked completion-audit claims no
196
+ longer auto-retry on a bare cold start (the main-model-recovery one-
197
+ shot retry keeps its pinned consent).
198
+
199
+ ## 0.35.22 — a queued item blocked by a live loop is loud and self-heals at loop end (2026-08-22)
200
+
201
+ ### suspicious-unstartable-repair-card fix (note.md Next #3)
202
+ Field (screenshots 20260821_114109/114210/134442/134645): /goal start of a
203
+ lowercase-fragment objective paused the goal and queued a repair task; the
204
+ card said "/list next starts the preserved repair/replan task" — but with
205
+ the Chrome-Bridge loop owning the surface, activateNextListItem's
206
+ one-active-thing guard refused activation LEDGER-ONLY: unstartable AND
207
+ invisibly blocked. Two fixes:
208
+ - the refusal now notifies with the queued objective and the way out
209
+ ("/loop stop … then /list next"), and the ledger names what stayed queued;
210
+ - when a loop ends by ANY route (/loop stop, /loop finish, plateau/bounds
211
+ stop), resumeQueuedListAfterLoopEnd retries list activation when no goal
212
+ owns the surface — the blocked entry starts instead of staying dead.
213
+ Also: tests/list-invisible-restart.test.ts no longer depends on co-resident
214
+ module state (unique owner session + explicit reset), fixing the cross-file
215
+ ordering failure surfaced by audit round eight.
216
+
217
+ ## 0.35.21 — list queue stays visible across lifecycle boundaries (2026-08-22)
218
+
219
+ ### list-invisible-until-restart fix (note.md Next #4)
220
+ Field: a stopped/interrupted /list exec left the queue surface blank —
221
+ active item only, no "N waiting · up next" line — until a session
222
+ restart. Root cause: the sidebar renders state.list from MEMORY while the
223
+ durable queue is the UNION of the state ledger and the per-item
224
+ .queue.json sidecars (v0.34.60 disk-first writes); a plugin re-init /
225
+ stale-handle window reset RAM to defaults and only some later path
226
+ re-ran the disk merge. session_start's restore now converges memory to
227
+ that union immediately (hydrateListQueueFromDisk after readState), so
228
+ the next lifecycle boundary heals the surface without a restart; the
229
+ hydration notifies with a truthful count ("restored N queued list
230
+ item(s)"). Regression tests: sidecar-only item is hydrated AND rendered;
231
+ convergence is idempotent (no duplicate for items in both stores).
232
+
233
+ ## 0.35.20 — one bounded automatic retry for transient mechanical-check deaths (2026-08-21)
234
+
235
+ ### Gate resilience
236
+ Field (sixth audit round): the pre-audit gate died MID-RUN under machine
237
+ load ~30 — output ends inside a passing file, no runner summary, exit 1 —
238
+ while the identical tree passed green twice in isolation. Resource
239
+ contention, not a red suite. Mechanical check commands now get exactly ONE
240
+ bounded automatic retry on failure: a deterministic red command stays red
241
+ on both attempts (final output names the retry and preserves the second
242
+ attempt's diagnostics); a first-attempt transient death followed by a
243
+ passing retry passes with an honest `recoveredRetryNote` in the result.
244
+ Mirrors the v0.35.17 zero-stream auto-retry philosophy at the gate level.
245
+
246
+ ## 0.35.19 — load-resilient budgets for the aggressive-recovery test (2026-08-21)
247
+
248
+ ### Flake hardening
249
+ At machine load ~50 (16 cores), the aggressive no-verdict recovery test's
250
+ wall-clock wait budgets (2x 25s inside a 60s per-test ceiling) expired
251
+ before two real subprocess-based auditor retry cycles completed — while
252
+ the canonical full-suite run stayed green in the same conditions. Raised:
253
+ per-test 60s→120s, retry waits 25s→45s, state-transition waits 8s→20s.
254
+ Budgets only; semantics untouched (same precedent as v0.35.15's 30s→60s).
255
+
256
+ ## 0.35.18 — mechanical checks resolve raw runners to their canonical scripts (2026-08-21)
257
+
258
+ ### Spurious fast-fail fix (fourth audit round)
259
+ A verification contract that names a RAW RUNNER in prose ("passes under
260
+ `bun test`") made the deterministic pre-audit execute `bun test` bare,
261
+ ignoring the project's own required configuration encoded in package.json
262
+ scripts (--parallel=1 --max-concurrency=1 --timeout; this suite shares
263
+ module state process-wide by design and serializes deliberately). The bare
264
+ invocation failed 6 tests + 5 nested-test errors while the canonical gate
265
+ was green twice — a spurious fast-fail of finished work. Mechanical check
266
+ commands that are exactly a raw runner invocation (bun test / vitest /
267
+ jest, no extra args) now resolve to the package script that wraps them;
268
+ narrower runs and non-runner programs pass through untouched. Pure resolver
269
+ (`resolveCanonicalRunnerCommand`) lives in goal-loop-backoff.ts with unit
270
+ tests; bunfig cannot express the required flags (verified empirically:
271
+ `[test] timeout` is not honored on bun 1.3.14).
272
+
273
+ ## 0.35.17 — zero-stream abort gains ONE bounded automatic retry; tag backfill (2026-08-21)
274
+
275
+ ### Post-accept hang self-heal (note.md Next §1)
276
+ Turns dispatched by accepting a Confirm dialog hung with zero provider
277
+ stream activity often enough that users repeatedly returned to parked
278
+ "action needed" sessions (field screenshot 20260821_152311). The watchdog's
279
+ bounded abort was correct; what was missing is self-heal. The FIRST silence
280
+ of a zero-stream streak now arms exactly ONE automatic retry ~90s after the
281
+ park — the parked goal/list item/loop auto-resumes through the durable
282
+ continuation machinery and one fresh dispatch goes out. A SECOND consecutive
283
+ silence refuses further retries (`zombie_auto_retry_refused_streak`) and
284
+ parks permanently for manual resume; real stream activity between aborts
285
+ resets the streak so an independent later hang earns its own single retry.
286
+ `/glla pause` freezes the retry like every other automatic side-effect;
287
+ the timer only clears a pause carrying exactly the watchdog's own reason
288
+ (a newer manual/recovery pause supersedes it); the heartbeat's one-shot
289
+ abort latch is released on retry dispatch so a fully-silent retry can still
290
+ be re-aborted. Pure streak decision lives in goal-loop-backoff.ts
291
+ (`zombieRetryDecision`) with unit tests; behavioral coverage drives the
292
+ full hang→abort/park→auto-resume→re-dispatch arc plus the double-hang and
293
+ pause-during-waystation paths (tests/post-accept-hang-retry.test.ts).
294
+
295
+ ### Version tags backfilled
296
+ All 41 released versions missing their `v<version>` git tag (v0.34.20 …
297
+ v0.35.16) were tagged at the historical commit whose package.json carried
298
+ that exact version and pushed to all remotes. Additive-only — no history
299
+ rewrite.
300
+
301
+ ### README currency pass
302
+ Documents the v0.35.15 per-phase glyphs/activity meter/silent-stretch
303
+ footer, `/glla pause`, and the v0.35.17 zero-stream auto-retry.
304
+
305
+ ## 0.35.16 — mechanical pre-audit gate no longer kills legitimate long checks (2026-08-21)
306
+
307
+ ### Deterministic pre-audit timeout fix
308
+ `runMechanicalPreAuditChecks` executed every contract command under a
309
+ hard 60-second `execFileSync` ceiling — but this repo's own contract
310
+ command (`npm run release:check`) legitimately needs ~3 minutes. Every
311
+ deterministic pre-audit therefore fast-failed with a truncated
312
+ head-of-output report showing only startup logs (two field rounds:
313
+ 2026-08-21 14:17 and 16:01), burning two auditor cycles on a gate that
314
+ could never pass inside its own bound. The default bound is now 10 minutes
315
+ — still a hang guard, no longer an honest-slow-work guard. Failed output
316
+ keeps the TAIL (where failures live) instead of the head, truncation is
317
+ labeled, and a timeout kill is bannered as such instead of masquerading as
318
+ an exit-code-1 test failure.
319
+
320
+ ## 0.35.15 — glla status-surface UX: visual footer, /glla pause, proactive quiet notify (2026-08-21)
321
+
322
+ ### Visual status footer
323
+ The auditing footer now leads each auditor phase with a distinct glyph
324
+ (queued ⋯ · running ▶ · quiet ◌ · blocked ⛔ · awaiting-verdict ✓) and a
325
+ compact draining activity meter (▰▱) that empties as worker silence grows
326
+ toward the quiet threshold. A glance answers "is the audit alive?" without
327
+ reading the sentence.
328
+
329
+ ### /glla pause | resume — broad supervisor freeze
330
+ `/glla pause` freezes ALL automatic machinery — heartbeat re-arms, stale
331
+ probes, zombie cleanup, main-model recovery probes, automatic completion-
332
+ audit recovery, continuation dispatch, loop ticks, and the proactive quiet
333
+ notification — while leaving the active goal/list item/loop and any
334
+ detached worker untouched. The flag persists via `supervisorPausedAt`, so a
335
+ session restart cannot silently re-arm machinery the user explicitly
336
+ stopped. `/glla resume` clears it first, then resumes whatever else is
337
+ resumable, and never follows with a misleading "Nothing to resume". Manual
338
+ user commands always still work.
339
+
340
+ ### Proactive auditor quiet reporting
341
+ Entering the quiet phase (~3 min of zero worker activity) now fires exactly
342
+ ONE warning notify instead of only recoloring the status chip — the field
343
+ complaint was an 8-minute silent stretch the user only discovered after the
344
+ fact. Once activity resumes, the footer shows "silent Xm then resumed" for
345
+ 10 minutes so a missed silence stays visible.
346
+
347
+ ### Persistence fix (latent bug)
348
+ `persistStateLine` never serialized `lastCompactionAt` despite v0.34.97's
349
+ comment claiming it did — the ⏳ compacting… chip silently lost its reload
350
+ survival. Both epoch fields now ride the state line with explicit nulls so
351
+ ledger merges clear them correctly.
352
+
353
+ ## 0.35.14 — full extension audit hardening (2026-08-21)
354
+
355
+ ### Verification and lifecycle integrity
356
+ Mechanical contract checks now run through a shell-free literal-argument
357
+ boundary, auditor verdicts require one final terminal marker, and regression
358
+ shield references must appear inside `<evidence>`. Invalid persisted IDs are
359
+ rejected at state hydration and filesystem boundaries. Child extension
360
+ factories no longer claim the host API or start timers before an admitted
361
+ `session_start`; completion approval cannot report success when terminal
362
+ archiving fails, and branch-mode loop resumes refuse the wrong branch.
363
+
364
+ ### Release contract
365
+ Published documentation includes the linked planning files, the workflow
366
+ runs the release contract on pushes and pull requests, and release tooling
367
+ uses pinned Node/npm versions.
368
+
3
369
  ## 0.35.13 — stale-API recovery loop fix (2026-08-20)
4
370
 
5
371
  ### Stale-handle recovery correctness
package/INSTALL.md CHANGED
@@ -52,7 +52,7 @@ Then the other two modes:
52
52
 
53
53
  ## Install from source (developers)
54
54
 
55
- Prerequisites: Node 22+ and bun (the test runner — `bun test`), pi-coding-agent, TypeScript 5.9+ (for `tsc --noEmit`).
55
+ Prerequisites: Node 22.19.0+ and bun (the test runner — `bun test`), pi-coding-agent, TypeScript 5.9+ (for `tsc --noEmit`).
56
56
 
57
57
  ```bash
58
58
  git clone https://github.com/DraconDev/pi-goal-list-loop-audit.git # or use the local dir
@@ -111,8 +111,8 @@ a file or committed — narration alone is the narrate-but-don't-ship loop)
111
111
  Why it changed: the v0.24.0 single-signal detector (same tool + same
112
112
  result hash 3×) killed two real user loops that were shipping work with
113
113
  stable verification output — stable verification is the GOAL state of a
114
- metricless loop, not the stuck state. Design doc:
115
- `audit/STUCK-DETECTION-REWORK-2026-07-24.md`. `/loop start toolsamerepeat=0`
114
+ metricless loop, not the stuck state. See `docs/DESIGN.md` for the shipped
115
+ design overview. `/loop start toolsamerepeat=0`
116
116
  disables the legacy check entirely; `/loop finish [reason]` ends a loop
117
117
  cleanly with stopReason `completed: <reason>` (distinct from
118
118
  stuck/plateau/stopped-by-user).
@@ -0,0 +1,90 @@
1
+ # List philosophy — the three-mode hierarchy
2
+
3
+ pi-goal-list-loop-audit ships three loops. They are NOT redundant — each
4
+ has a **distinct source of long-running-ness**:
5
+
6
+ | Mode | Item size | Long-running by | Typical lifetime |
7
+ |---|---|---|---|
8
+ | `/goal` | ONE big multi-hour task | **Scope** | Hours |
9
+ | `/list` | N items × short (minutes each) | **Queue depth** | Hours → days → weeks |
10
+ | `/loop` | 1 metric × infinite polish | **Bounds** | Until plateau/stop/finish |
11
+
12
+ ## `/goal` — one big long task
13
+
14
+ `/goal` is the multi-hour mode. Its long-running property is **scope**:
15
+ one task that spans multiple agent runs, requires deep research, or would
16
+ take hours end-to-end. It ends only when the isolated auditor approves
17
+ the verification contract. If your work fits in a single agent run — a
18
+ focused change, one audit, a small refactor — it belongs in `/list`.
19
+
20
+ ## `/list` — hundreds of short items
21
+
22
+ `/list` items are **short tasks, not multi-hour objectives**. Each item
23
+ should fit comfortably in a single agent run: minutes of work, a single
24
+ focused change. The list's long-running property is **queue depth** — the
25
+ queue can hold hundreds of items, activated one at a time, pushed over
26
+ days or weeks. An item longer than ~30 minutes probably wants breaking
27
+ up; much longer and it wants `/goal`.
28
+
29
+ `/list depth` shows the long-running state: queue depth, oldest item age,
30
+ and average item duration from your archived list items.
31
+
32
+ ## `/loop` — metric-driven infinite polish
33
+
34
+ `/loop` improves ONE metric forever. Its long-running property is
35
+ **bounds**: it ends on plateau, on bounds (max iterations / time /
36
+ tokens), on `/loop stop`, or on `/loop finish` (graceful stop after the
37
+ current iteration).
38
+
39
+ ## The wrapper-goal anti-pattern (why this doc exists)
40
+
41
+ Real incidents, 2026-07-24, two projects on the same day:
42
+
43
+ > "Close every weak point in `docs/per-screen-weak-points.md` (76 items,
44
+ > one commit each)" and "land all 40 findings as a tasklist, ordered by
45
+ > ROI" — each folded into **ONE** list item with an **aggregate**
46
+ > verification contract ("≥ 76 commits with `CLOSED:`", "≥ 32 fix(Wn)
47
+ > commits").
48
+
49
+ The work got done; the auto-committer squashed intermediate commits; the
50
+ literal commit count failed; the isolated auditor **correctly
51
+ disapproved** finished work. The failure was at step 1 — task
52
+ designation — not at the audit.
53
+
54
+ The fix: **N independent short items → N `/list` items**, each closing
55
+ exactly ONE finding with its own per-item contract ("close IMP-AUD3-68:
56
+ `Map.svelte:1528` missing `role`"). Per-item contracts are impossible to
57
+ squash. Any aggregate re-audit becomes the FINAL `/goal`, not the first.
58
+
59
+ Since v0.25.3 the drafting flow detects this shape ("N items" + "each" +
60
+ "one commit") and steers you to `items[]` — see the cross-recommend
61
+ block in `prompts/goal-loop-draft.md`.
62
+
63
+ Since v0.26.0 the **Reviewer** is the post-completion glue layer across
64
+ all three modes' terminal states: it converts completion findings into
65
+ `/list` items (bug/refactor, no Confirm), proposes architectural work as
66
+ `/goal` (Confirm), fires a regression-scan audit on clean completions,
67
+ and notifies + idles otherwise. See `INSTALL.md` "Reviewer".
68
+
69
+ ## Audit Cadence: Why Every `/list` Task is Audited
70
+
71
+ Auditing occurs at the completion boundary of **every single `/list` task** (via the detached isolated auditor) before the next item in the queue can activate.
72
+
73
+ While auditing every item requires rigorous verification, it prevents **queue drift**:
74
+ - In a 50-item list, an unverified error in item #2 would otherwise silently corrupt the codebase, causing items #3 through #50 to fail or build on broken invariants.
75
+ - Per-task auditing ensures each item represents a rock-solid, verified invariant before the next task begins.
76
+
77
+ ## Single-Trunk Execution Law & Parallelization
78
+
79
+ ### Why "Main-Only" Outperforms Branch Swarms in Autonomous Loops
80
+ Speculative feature branching across autonomous subagents creates **stale context bubbles** and **merge collision debt**:
81
+ 1. Agent A on branch-1 and Agent B on branch-2 both read from snapshot $T_0$.
82
+ 2. Once Agent A lands a commit, Agent B is working on an obsolete codebase without knowing it.
83
+ 3. Merging parallel LLM branches frequently causes semantic regressions and broken invariants.
84
+
85
+ ### The Single-Trunk Operating Rule:
86
+ * **Serial Queue on `main`**: All queue items drain sequentially on the single primary working tree. Item $N+1$ always executes with 100% truthful, up-to-date context left by item $N$.
87
+ * **Transactional Green-or-Revert**: Every task either lands green (verified by tests and the detached auditor) and commits, or cleanly rolls back on `main` before the next backlog item is touched.
88
+ * **Safe Subagent Parallelism**: Subagents are used for **read-only research fan-out** (e.g. concurrent `Explore` queries across subsystems in a single turn) or standalone verification, rather than speculative mutating branches.
89
+
90
+ See `INSTALL.md` for the command surface.