pi-goal-list-loop-audit 0.23.4 → 0.23.6
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/README.md
CHANGED
|
@@ -58,10 +58,11 @@ matches `/list show`.
|
|
|
58
58
|
/list remove <n> # drop item n from the queue
|
|
59
59
|
/list clear # empty the queue
|
|
60
60
|
/loop # draft the loop (agent grills; measure is test-run before you confirm)
|
|
61
|
+
/loop start "keep polishing the UI" # infinite metricless loop (v0.23.6): no plateau, no cap — ends at time=/tokens= or /loop stop
|
|
61
62
|
/loop start "reduce TODOs" measure="grep -c TODO src.txt | head -1" direction=min
|
|
62
63
|
/loop start "shrink the bundle" measure="..." direction=min time=4 tokens=500000 # arbitrary bounds
|
|
63
64
|
/loop start "reduce TODOs" measure="..." direction=min branch=1 # scratch-branch mode
|
|
64
|
-
/loop start "keep improving SPEC.md" measure=none max=20 # metricless
|
|
65
|
+
/loop start "keep improving SPEC.md" measure=none max=20 # metricless with an explicit cap (v0.23.0)
|
|
65
66
|
/loop status # iteration, best, stall, recent values
|
|
66
67
|
/loop stop # halt with summary
|
|
67
68
|
```
|
|
@@ -593,3 +593,37 @@ export function shouldAutoResumeOnSessionStart(reason: string | undefined, autoR
|
|
|
593
593
|
if (autoResume === true) return true;
|
|
594
594
|
return reason === "resume" || reason === "reload" || reason === "fork";
|
|
595
595
|
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* v0.23.5: normalize a drafter-supplied verification contract for the
|
|
599
|
+
* Confirm dialog AND for storage. Three cleanups, all mechanical:
|
|
600
|
+
* 1. Drop bare introducer lines ("Done when:", "Done when ALL of the
|
|
601
|
+
* following are true:") — the dialog adds its own "Done when" header;
|
|
602
|
+
* a model-supplied one renders doubled (field-observed) and pollutes
|
|
603
|
+
* the shield's item list.
|
|
604
|
+
* 2. Strip a glued "Done when: " prefix on a content line.
|
|
605
|
+
* 3. Renumber bullet/numbered lines sequentially ("1.", "2.", ...) so the
|
|
606
|
+
* dialog reads as a checklist and reject-feedback can cite item
|
|
607
|
+
* numbers. Non-bullet prose lines pass through untouched.
|
|
608
|
+
*/
|
|
609
|
+
export function normalizeDraftContract(raw: string): string {
|
|
610
|
+
const lines = raw
|
|
611
|
+
.trim()
|
|
612
|
+
.split("\n")
|
|
613
|
+
.map((l) => l.trim())
|
|
614
|
+
.filter((l) => !/^(?:done when|verified when|verify|verification)\b[^:]*:\s*$/i.test(l))
|
|
615
|
+
.map((l) => l.replace(/^(?:done when|verified when)\s*:\s+/i, ""))
|
|
616
|
+
.filter((l) => l.length > 0);
|
|
617
|
+
let n = 0;
|
|
618
|
+
return lines
|
|
619
|
+
.map((l) => {
|
|
620
|
+
const m = l.match(/^(?:[-*•]\s+|\d+[.)]\s+)(.+)$/);
|
|
621
|
+
return m ? `${++n}. ${m[1]}` : l;
|
|
622
|
+
})
|
|
623
|
+
.join("\n");
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/** Count the numbered checklist items in a normalized contract. */
|
|
627
|
+
export function draftContractItemCount(normalized: string): number {
|
|
628
|
+
return normalized.split("\n").filter((l) => /^\d+\.\s/.test(l)).length;
|
|
629
|
+
}
|
|
@@ -237,12 +237,17 @@ export function parseLoopStartArgs(raw: string): {
|
|
|
237
237
|
const measureRaw = (kv.get("measure") ?? "").trim();
|
|
238
238
|
// v0.23.0: measure=none → metricless "spec loop" (Sisyphus mode). No
|
|
239
239
|
// metric, no direction, no plateau — bounds and /loop stop only.
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
// v0.23.6: a bare `/loop start "<target>"` IS the infinite command —
|
|
241
|
+
// no measure= means metricless too. The Confirm dialog names "NO
|
|
242
|
+
// plateau · NO iteration cap · /loop stop" before anything runs, so
|
|
243
|
+
// the choice is never silent (the v0.23.0 rule). Metric loops keep
|
|
244
|
+
// the 50-iteration default cap; metricless loops default to UNBOUNDED
|
|
245
|
+
// (max=0) unless max= is given explicitly.
|
|
246
|
+
const metricless = !measureRaw || measureRaw.toLowerCase() === "none";
|
|
242
247
|
const dirRaw = (kv.get("direction") ?? "").toLowerCase();
|
|
243
|
-
if (metricless && dirRaw) throw new Error("direction= is meaningless
|
|
244
|
-
if (!metricless && dirRaw !== "min" && dirRaw !== "max") throw new Error("missing direction=min|max");
|
|
245
|
-
if (!target) throw new Error("missing target (what to improve), e.g. /loop start \"
|
|
248
|
+
if (metricless && dirRaw) throw new Error("direction= is meaningless without a metric — add measure=\"<cmd>\" or drop direction=");
|
|
249
|
+
if (!metricless && dirRaw !== "min" && dirRaw !== "max") throw new Error("missing direction=min|max (a metric loop needs to know which way is better; a bare /loop start \"<target>\" with no measure= is the infinite metricless form)");
|
|
250
|
+
if (!target) throw new Error("missing target (what to improve), e.g. /loop start \"keep polishing the UI\" — bare start is metricless + unbounded; add measure=\"<cmd>\" direction=min|max for a metric loop");
|
|
246
251
|
|
|
247
252
|
const window = Number.parseInt(kv.get("window") ?? "", 10);
|
|
248
253
|
const max = Number.parseInt(kv.get("max") ?? "", 10);
|
|
@@ -263,8 +268,11 @@ export function parseLoopStartArgs(raw: string): {
|
|
|
263
268
|
measureCmd: metricless ? "" : measureRaw,
|
|
264
269
|
direction: metricless ? undefined : dirRaw as LoopDirection,
|
|
265
270
|
plateauWindow: Number.isFinite(window) && window > 0 ? window : LOOP_DEFAULTS.plateauWindow,
|
|
266
|
-
// v0.23.0: max=0 = truly unbounded (no iteration cap)
|
|
267
|
-
|
|
271
|
+
// v0.23.0: max=0 = truly unbounded (no iteration cap).
|
|
272
|
+
// v0.23.6: metricless with no explicit max= defaults to UNBOUNDED —
|
|
273
|
+
// an infinite loop is the point of the bare form. Metric loops keep
|
|
274
|
+
// the 50-cap default.
|
|
275
|
+
maxIterations: kv.has("max") ? (Number.isFinite(max) && max >= 0 ? max : LOOP_DEFAULTS.maxIterations) : metricless ? 0 : LOOP_DEFAULTS.maxIterations,
|
|
268
276
|
branch: branchRaw === "1" || branchRaw === "true" || branchRaw === "yes",
|
|
269
277
|
force: forceRaw === "1" || forceRaw === "true" || forceRaw === "yes",
|
|
270
278
|
timeLimitHours: Number.isFinite(timeRaw) && timeRaw > 0 ? timeRaw : undefined,
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -53,6 +53,8 @@ import {
|
|
|
53
53
|
newGoalId,
|
|
54
54
|
nowIso,
|
|
55
55
|
piGlaDir,
|
|
56
|
+
normalizeDraftContract,
|
|
57
|
+
draftContractItemCount,
|
|
56
58
|
readState,
|
|
57
59
|
renderGoalMarkdown,
|
|
58
60
|
shouldAutoResumeOnSessionStart,
|
|
@@ -465,7 +467,7 @@ async function startDrafting(ctx: ExtensionContext, target: "goal" | "list" | "l
|
|
|
465
467
|
target === "list"
|
|
466
468
|
? `${label}: the objective has no "Done when:" clause — the agent will grill you about it first (nothing activates until you confirm). Add directly instead: include a "Done when:" clause.`
|
|
467
469
|
: target === "loop"
|
|
468
|
-
? `${label}: a loop target needs a metric and a direction — the agent will help you design them first (nothing activates until you confirm). Skip the interview entirely: /loop start "<target>" measure="<cmd>" direction=min|max [window=5] [max=50] [time=h] [tokens=n] [branch=1].`
|
|
470
|
+
? `${label}: a loop target needs a metric and a direction — the agent will help you design them first (nothing activates until you confirm). Skip the interview entirely: /loop start "<target>" (bare = infinite metricless) or /loop start "<target>" measure="<cmd>" direction=min|max [window=5] [max=50] [time=h] [tokens=n] [branch=1].`
|
|
469
471
|
: `${label}: the objective has no "Done when:" clause — the agent will grill you about it first (nothing activates until you confirm). Skip the interview entirely: /goal start <objective>.`;
|
|
470
472
|
ctx.ui.notify(
|
|
471
473
|
seed
|
|
@@ -1256,7 +1258,7 @@ async function cmdLoop(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
1256
1258
|
if (sub === "status") {
|
|
1257
1259
|
const loop = state.loop;
|
|
1258
1260
|
if (!loop) {
|
|
1259
|
-
ctx.ui.notify("No loop. /loop to draft one,
|
|
1261
|
+
ctx.ui.notify("No loop. /loop to draft one, /loop start \"<target>\" for an infinite metricless loop, or add measure=\"<cmd>\" direction=min|max for a metric loop [window=5] [max=50] [time=<hours>] [tokens=<budget>]", "info");
|
|
1260
1262
|
return;
|
|
1261
1263
|
}
|
|
1262
1264
|
const lines = [
|
|
@@ -1630,8 +1632,10 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1630
1632
|
}
|
|
1631
1633
|
return { content: [{ type: "text", text: `${n} items confirmed and queued (${listQueue().length} waiting).` }], details: {} };
|
|
1632
1634
|
}
|
|
1633
|
-
const
|
|
1634
|
-
|
|
1635
|
+
const normContract = p.verificationContract?.trim() ? normalizeDraftContract(p.verificationContract) : "";
|
|
1636
|
+
const checkCount = normContract ? draftContractItemCount(normContract) : 0;
|
|
1637
|
+
const contractBlock = normContract
|
|
1638
|
+
? `\n\nDone when${checkCount > 0 ? ` — ${checkCount} check${checkCount === 1 ? "" : "s"}` : ""}:\n${normContract}`
|
|
1635
1639
|
: "\n\n(No verification contract — the auditor will infer done-criteria from the objective. Consider adding one.)";
|
|
1636
1640
|
// v0.22.6: a list draft that will activate immediately must SAY so in
|
|
1637
1641
|
// the Confirm dialog — "I started a list and ended up with a running
|
|
@@ -1657,7 +1661,7 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1657
1661
|
}
|
|
1658
1662
|
const confirmedTarget = draftingTarget;
|
|
1659
1663
|
draftingTarget = null;
|
|
1660
|
-
const full = p.objective.trim() + (
|
|
1664
|
+
const full = p.objective.trim() + (normContract ? `\nDone when:\n${normContract}` : "");
|
|
1661
1665
|
// List drafting: the confirmed contract goes into the QUEUE, not active.
|
|
1662
1666
|
if (confirmedTarget === "list") {
|
|
1663
1667
|
const extracted = extractVerificationContract(full);
|
|
@@ -2429,7 +2433,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
2429
2433
|
handler: (args: string, ctx: ExtensionContext) => { rememberCtx(ctx); return cmdList(args, ctx); },
|
|
2430
2434
|
});
|
|
2431
2435
|
pi.registerCommand("loop", {
|
|
2432
|
-
description: "Loop 3: metric-driven process — it never completes. /loop <target> drafts the metric with you · /loop start \"<target>\" measure=\"<cmd>\" direction=min|max [window=5] [max=50] [
|
|
2436
|
+
description: "Loop 3: metric-driven process — it never completes. /loop <target> drafts the metric with you · /loop start \"<target>\" = infinite metricless loop (no plateau, no cap; ends at time=/tokens= or /loop stop) · add measure=\"<cmd>\" direction=min|max [window=5] [max=50] [branch=1] for a metric loop · /loop status · /loop stop. 'Improve until X' is a /goal, not a loop.",
|
|
2433
2437
|
getArgumentCompletions: completions([
|
|
2434
2438
|
["start", "skip drafting: /loop start \"<target>\" measure=\"<cmd>\" direction=min|max [window=5] [max=50]"],
|
|
2435
2439
|
["status", "show metric, iteration, best/last values, stall count"],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.6",
|
|
4
4
|
"description": "Goal. Loop. Audit. Done. — a pi-coding-agent extension that supervises long-running work, with isolated auditor on each completion. Beat bamboozling by design: the auditor runs in a fresh session with no extensions, no skills, no editor — only the read tools needed to verify your goal.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "dracon",
|
|
@@ -19,6 +19,13 @@ request into a **confirmed goal contract**. Do NOT start substantive work yet.
|
|
|
19
19
|
- **verification contract** — how an independent auditor can tell it is
|
|
20
20
|
done, as checkable items (commands, file states, test outcomes).
|
|
21
21
|
Strongly recommended; without it the auditor infers from the objective.
|
|
22
|
+
Write 3–8 mechanical checks, one per line, each verifiable with ONE
|
|
23
|
+
command or file check. The auditor must quote raw evidence for EVERY
|
|
24
|
+
item — a 17-item contract means a slow, expensive audit and more
|
|
25
|
+
regression-shield friction. Verify the artifact's integrity (the doc
|
|
26
|
+
exists, the table has N rows, the gates pass), not every sub-part.
|
|
27
|
+
Do NOT prefix the contract with "Done when:" — the Confirm dialog
|
|
28
|
+
adds that header itself.
|
|
22
29
|
- **boundaries** — what is explicitly out of scope (fold into the
|
|
23
30
|
objective text).
|
|
24
31
|
4. Keep grilling until the objective and success criteria are concrete
|