pi-goal-list-loop-audit 0.15.1 → 0.17.0
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 +17 -14
- package/docs/DESIGN.md +1 -1
- package/extensions/goal-loop-auditor.ts +1 -1
- package/extensions/goal-loop-core.ts +14 -4
- package/extensions/goal-loop-display.ts +6 -6
- package/extensions/loops/goal.ts +23 -23
- package/package.json +1 -1
- package/prompts/goal-loop-continuation.md +1 -1
- package/scripts/smoke.sh +6 -6
package/README.md
CHANGED
|
@@ -33,13 +33,14 @@ Four top-level commands, that's all:
|
|
|
33
33
|
/goal # drafting: agent grills, you Confirm
|
|
34
34
|
/goal "audit the repo" # no contract clause → agent grills you first (propose is gated on it)
|
|
35
35
|
/goal "Step 1. Step 2. Done when: tests pass." # has contract → starts now
|
|
36
|
+
/goal start "fix the flaky login test" # explicit skip-draft: starts now, no interview (auditor infers the contract)
|
|
36
37
|
/goal status # show state
|
|
37
38
|
/goal pause # pause
|
|
38
39
|
/goal resume # resume
|
|
39
40
|
/goal cancel # abort
|
|
40
41
|
/goal tweak "<new objective>" # edit in place (Confirm dialog)
|
|
41
42
|
/goal archive # archived goals, newest first
|
|
42
|
-
/
|
|
43
|
+
/glla # open the settings UI (or /glla key=value)
|
|
43
44
|
/list add # draft a contract (or a whole batch via items[])
|
|
44
45
|
/list add "<objective>" # queue one directly
|
|
45
46
|
/list add plan.md # file detected → bulk import, one Confirm
|
|
@@ -69,7 +70,9 @@ your objectives can start with any verb.)
|
|
|
69
70
|
|
|
70
71
|
Drafting rules: **no-args drafts, args-without-a-`Done when:`-clause get
|
|
71
72
|
grilled by the agent (proposing is mechanically blocked until you have
|
|
72
|
-
replied at least once
|
|
73
|
+
replied at least once — typed chat or an answered `ask_user_question` dialog
|
|
74
|
+
both count), args-with-a-clause start instantly, `/goal start` skips the
|
|
75
|
+
interview by explicit command, a file path is
|
|
73
76
|
bulk direct.** A
|
|
74
77
|
sisyphus-style plan file (checklists, bullets, numbered, plain lines) imports
|
|
75
78
|
as-is — headings become nothing, items become goals. And the drafter itself
|
|
@@ -148,7 +151,7 @@ Each loop is a different policy class on the same status machine.
|
|
|
148
151
|
|
|
149
152
|
## Live TUI (always know it's on)
|
|
150
153
|
|
|
151
|
-
A persistent `
|
|
154
|
+
A persistent `glla:` status segment + an above-editor widget show the current
|
|
152
155
|
goal/loop at all times: objective, status, elapsed, tokens, next task or loop
|
|
153
156
|
metric, pause reason, and live auditor progress during audits. If something is
|
|
154
157
|
running, you can see it — no command needed.
|
|
@@ -163,19 +166,19 @@ No external watchdog plugin needed.
|
|
|
163
166
|
## Config (one global place, rarely opened)
|
|
164
167
|
|
|
165
168
|
```
|
|
166
|
-
/
|
|
167
|
-
/
|
|
168
|
-
/
|
|
169
|
-
/
|
|
170
|
-
/
|
|
171
|
-
/
|
|
172
|
-
/
|
|
169
|
+
/glla # open the settings UI
|
|
170
|
+
/glla model=provider/id # auditor model override → GLOBAL
|
|
171
|
+
/glla thinking=high # auditor thinking → GLOBAL
|
|
172
|
+
/glla notify='cmd "$1"' # push on complete/pause/stop → GLOBAL
|
|
173
|
+
/glla tokenlimit=10000000 # per-goal token budget (default: off) → GLOBAL
|
|
174
|
+
/glla tokenlimit=0 # explicitly no cap (the default)
|
|
175
|
+
/glla project tokenlimit=500 # rare per-project override
|
|
173
176
|
```
|
|
174
177
|
|
|
175
178
|
Resolution per key: **project > global > defaults**. The auditor defaults to
|
|
176
179
|
your pi session model. When the session provider is extension-registered the
|
|
177
180
|
auditor can't auth it — you're told once (info level) with the fix:
|
|
178
|
-
`/
|
|
181
|
+
`/glla model=provider/id`, set once, rarely touched again. The plugin never
|
|
179
182
|
picks a model itself. Thinking follows the session too (floor `high`).
|
|
180
183
|
|
|
181
184
|
## Token guard
|
|
@@ -183,7 +186,7 @@ picks a model itself. Thinking follows the session too (floor `high`).
|
|
|
183
186
|
Every goal tracks real token usage; crossing the budget pauses the goal.
|
|
184
187
|
Default 10,000,000 per goal — a runaway threshold, not a big-goal threshold
|
|
185
188
|
(real research/feature goals legitimately burn 2-4M). Tune with
|
|
186
|
-
`/
|
|
189
|
+
`/glla tokenlimit=<n>`. Loop 3 doesn't need this cap — it has its own brakes
|
|
187
190
|
(max iterations + plateau).
|
|
188
191
|
|
|
189
192
|
## Compatibility (what goes well, what conflicts)
|
|
@@ -209,8 +212,8 @@ not requirements).
|
|
|
209
212
|
|
|
210
213
|
**Two footnotes**: (1) extension-registered providers work in the main session
|
|
211
214
|
but not the auditor's extension-less session — if audits fail auth, set the
|
|
212
|
-
override once with `/
|
|
213
|
-
`/
|
|
215
|
+
override once with `/glla model=`. (2) `pi-notify-agent` notifies on every turn;
|
|
216
|
+
`/glla notify=` fires only on goal complete/pause/loop stop.
|
|
214
217
|
|
|
215
218
|
## Files
|
|
216
219
|
|
package/docs/DESIGN.md
CHANGED
|
@@ -179,7 +179,7 @@ active → aborted (user /pi-gla-cancel)
|
|
|
179
179
|
|
|
180
180
|
### Decision 9: JSONL state (deterministic compaction)
|
|
181
181
|
|
|
182
|
-
Goal state lives in `.pi-
|
|
182
|
+
Goal state lives in `.pi-glla/active.jsonl`. Each line is a state transition. On compaction, the summary is rebuilt deterministically from the JSONL (autoresearch pattern).
|
|
183
183
|
|
|
184
184
|
This protects against model-generated summaries losing fidelity.
|
|
185
185
|
|
|
@@ -297,7 +297,7 @@ export async function runGoalCompletionAuditor(args: {
|
|
|
297
297
|
output,
|
|
298
298
|
model: modelLabel(model),
|
|
299
299
|
thinkingLevel,
|
|
300
|
-
error: `Auditor produced no output${streamError ? `: ${streamError}` : " — the auditor session likely failed (check the model's auth/quota, or set a working one with /
|
|
300
|
+
error: `Auditor produced no output${streamError ? `: ${streamError}` : " — the auditor session likely failed (check the model's auth/quota, or set a working one with /glla model=provider/id)"}`,
|
|
301
301
|
};
|
|
302
302
|
}
|
|
303
303
|
|
|
@@ -158,10 +158,10 @@ export interface Goal {
|
|
|
158
158
|
export type GoalRoute =
|
|
159
159
|
| { kind: "draft" }
|
|
160
160
|
| { kind: "set"; text: string }
|
|
161
|
-
| { kind: "sub"; name: "status" | "pause" | "resume" | "cancel" | "tweak" | "archive"; rest: string };
|
|
161
|
+
| { kind: "sub"; name: "status" | "pause" | "resume" | "cancel" | "tweak" | "archive" | "start"; rest: string };
|
|
162
162
|
|
|
163
163
|
const GOAL_EXACT_SUBS = new Set(["status", "pause", "resume", "cancel"]);
|
|
164
|
-
const GOAL_ARG_SUBS = new Set(["tweak", "archive"]);
|
|
164
|
+
const GOAL_ARG_SUBS = new Set(["tweak", "archive", "start"]);
|
|
165
165
|
|
|
166
166
|
export function routeGoalArgs(raw: string): GoalRoute {
|
|
167
167
|
const trimmed = raw.trim();
|
|
@@ -173,7 +173,7 @@ export function routeGoalArgs(raw: string): GoalRoute {
|
|
|
173
173
|
return { kind: "sub", name: first as "status" | "pause" | "resume" | "cancel", rest: "" };
|
|
174
174
|
}
|
|
175
175
|
if (GOAL_ARG_SUBS.has(first)) {
|
|
176
|
-
return { kind: "sub", name: first as "tweak" | "archive", rest };
|
|
176
|
+
return { kind: "sub", name: first as "tweak" | "archive" | "start", rest };
|
|
177
177
|
}
|
|
178
178
|
return { kind: "set", text: trimmed };
|
|
179
179
|
}
|
|
@@ -334,7 +334,17 @@ export const DEFAULT_STATE: State = {
|
|
|
334
334
|
// =================================================================
|
|
335
335
|
|
|
336
336
|
export function piGlaDir(cwd: string): string {
|
|
337
|
-
|
|
337
|
+
const dir = path.join(cwd, ".pi-glla");
|
|
338
|
+
// v0.17.0: one-time migration of the pre-rename state dir (.pi-gla →
|
|
339
|
+
// .pi-glla). Active goals, ledgers, and project settings move with the
|
|
340
|
+
// name — no relics, no lost state.
|
|
341
|
+
const legacy = path.join(cwd, ".pi-gla");
|
|
342
|
+
try {
|
|
343
|
+
if (!fs.existsSync(dir) && fs.existsSync(legacy)) fs.renameSync(legacy, dir);
|
|
344
|
+
} catch {
|
|
345
|
+
// read-only fs or partial state — fall through and use the new dir
|
|
346
|
+
}
|
|
347
|
+
return dir;
|
|
338
348
|
}
|
|
339
349
|
|
|
340
350
|
export function goalMdPath(cwd: string, id: string): string {
|
|
@@ -46,28 +46,28 @@ export interface AuditDisplayProgress {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
* One-line status for ctx.ui.setStatus("pi-
|
|
49
|
+
* One-line status for ctx.ui.setStatus("pi-glla", …).
|
|
50
50
|
* Returns undefined when nothing is being supervised (clears the segment).
|
|
51
51
|
*/
|
|
52
52
|
export function buildStatusText(state: State, audit?: AuditDisplayProgress | null, now = Date.now()): string | undefined {
|
|
53
53
|
if (state.loop?.active) {
|
|
54
54
|
const l = state.loop;
|
|
55
55
|
const arrow = l.direction === "min" ? "↓" : "↑";
|
|
56
|
-
return `
|
|
56
|
+
return `glla: loop ${arrow} iter ${l.iteration}/${l.maxIterations} · best ${l.bestValue ?? "n/a"} · stall ${l.stallCount}/${l.plateauWindow}`;
|
|
57
57
|
}
|
|
58
58
|
const g = state.goal;
|
|
59
59
|
if (!g) return undefined;
|
|
60
60
|
if (g.status === "auditing") {
|
|
61
61
|
const tool = audit?.currentTool ? ` · ${audit.currentTool}` : "";
|
|
62
|
-
return `
|
|
62
|
+
return `glla: auditing…${tool}`;
|
|
63
63
|
}
|
|
64
64
|
if (g.status === "paused") {
|
|
65
|
-
return `
|
|
65
|
+
return `glla: paused ⏸ ${truncate(g.pauseReason ?? "", 40)}`;
|
|
66
66
|
}
|
|
67
67
|
if (g.status === "active") {
|
|
68
68
|
const queue = state.list?.length ? ` · list ${state.list.length}` : "";
|
|
69
69
|
const tasks = g.taskList ? ` ${countDone(g)}/${countTotal(g)} tasks ·` : "";
|
|
70
|
-
return `
|
|
70
|
+
return `glla: goal ●${tasks} ${fmtElapsed(now - Date.parse(g.createdAt))}${queue}`;
|
|
71
71
|
}
|
|
72
72
|
return undefined; // complete/aborted → clear
|
|
73
73
|
}
|
|
@@ -99,7 +99,7 @@ function countTotal(g: Goal): number {
|
|
|
99
99
|
// ---- above-editor widget (multi-line panel) ----
|
|
100
100
|
|
|
101
101
|
/**
|
|
102
|
-
* Widget lines for ctx.ui.setWidget("pi-
|
|
102
|
+
* Widget lines for ctx.ui.setWidget("pi-glla", lines).
|
|
103
103
|
* Returns undefined when nothing is worth showing.
|
|
104
104
|
*/
|
|
105
105
|
export function buildWidgetLines(state: State, audit?: AuditDisplayProgress | null, now = Date.now()): string[] | undefined {
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* /goal "<objective>" | /goal (draft) | /goal status|pause|resume|cancel|tweak <text>|archive
|
|
12
12
|
* /list add|show|next|remove|clear
|
|
13
13
|
* /loop (draft) | /loop start|status|stop
|
|
14
|
-
* /
|
|
14
|
+
* /glla (settings UI) | /glla key=value | /glla project key=value
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import * as fs from "node:fs";
|
|
@@ -142,8 +142,8 @@ let uiTicker: NodeJS.Timeout | null = null;
|
|
|
142
142
|
function refreshUI(ctx: ExtensionContext): void {
|
|
143
143
|
if (!ctx.hasUI) return;
|
|
144
144
|
try {
|
|
145
|
-
ctx.ui.setStatus("pi-
|
|
146
|
-
ctx.ui.setWidget("pi-
|
|
145
|
+
ctx.ui.setStatus("pi-glla", buildStatusText(state, latestAuditProgress));
|
|
146
|
+
ctx.ui.setWidget("pi-glla", buildWidgetLines(state, latestAuditProgress));
|
|
147
147
|
} catch {
|
|
148
148
|
// stale ctx — next event refreshes
|
|
149
149
|
}
|
|
@@ -434,7 +434,7 @@ async function startDrafting(ctx: ExtensionContext, target: "goal" | "list" | "l
|
|
|
434
434
|
const [file, label, tool] = prompts[target]!;
|
|
435
435
|
ctx.ui.notify(
|
|
436
436
|
seed
|
|
437
|
-
? `${label}: the objective has no "Done when:" clause — the agent will grill you about it first (nothing activates until you confirm)
|
|
437
|
+
? `${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>.`
|
|
438
438
|
: `${label} started. The agent will grill until the contract is concrete, then ${tool} opens a Confirm dialog. No work begins before confirmation.`,
|
|
439
439
|
"info",
|
|
440
440
|
);
|
|
@@ -481,6 +481,16 @@ async function cmdGoal(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
481
481
|
if (route.name === "cancel") return cmdCancel(ctx);
|
|
482
482
|
if (route.name === "tweak") return cmdTweak(route.rest, ctx);
|
|
483
483
|
if (route.name === "archive") return cmdGoals(ctx);
|
|
484
|
+
// v0.16.0: /goal start <objective> — explicit skip-draft. Activates
|
|
485
|
+
// immediately, no interview, no "Done when:" heuristic. Symmetric
|
|
486
|
+
// with /loop start. The auditor infers the contract from the objective.
|
|
487
|
+
if (route.name === "start") {
|
|
488
|
+
if (!route.rest) {
|
|
489
|
+
ctx.ui.notify("Usage: /goal start <objective> — activates immediately, skipping the drafting interview. (Without start, an objective needs a 'Done when:' clause or it gets drafted first.)", "warning");
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
return cmdSet(route.rest, ctx, true);
|
|
493
|
+
}
|
|
484
494
|
}
|
|
485
495
|
return cmdSet(route.kind === "set" ? route.text : "", ctx);
|
|
486
496
|
}
|
|
@@ -489,7 +499,7 @@ async function cmdGoal(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
489
499
|
// /goal: bypass drafting, start now (the only entry in v0.1.0)
|
|
490
500
|
// =================================================================
|
|
491
501
|
|
|
492
|
-
async function cmdSet(args: string, ctx: ExtensionContext): Promise<void> {
|
|
502
|
+
async function cmdSet(args: string, ctx: ExtensionContext, skipDraft = false): Promise<void> {
|
|
493
503
|
let raw = args.trim();
|
|
494
504
|
// Users naturally quote the objective ("/goal \"do X\""); strip one layer of
|
|
495
505
|
// surrounding matching quotes so they don't leak into the goal text.
|
|
@@ -507,7 +517,8 @@ async function cmdSet(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
507
517
|
// v0.11.0: a contract-less objective gets drafted, not activated raw —
|
|
508
518
|
// the pi-goal-x lesson: arg + Enter is worse than a 5-minute draft.
|
|
509
519
|
// Include an explicit "Done when: …" clause to activate instantly.
|
|
510
|
-
|
|
520
|
+
// v0.16.0: /goal start bypasses this by explicit user command.
|
|
521
|
+
if (!skipDraft && goalArgsNeedDrafting(raw)) {
|
|
511
522
|
await startDrafting(ctx, "goal", raw);
|
|
512
523
|
return;
|
|
513
524
|
}
|
|
@@ -534,7 +545,7 @@ async function cmdStatus(ctx: ExtensionContext): Promise<void> {
|
|
|
534
545
|
`Objective: ${g.objective}`,
|
|
535
546
|
`Auto-continue: ${g.autoContinue ? "on" : "off"}`,
|
|
536
547
|
`Iteration: ${iterationCounter}`,
|
|
537
|
-
`Tokens: ${(g.usage?.tokensUsed ?? 0).toLocaleString()}${(g.usage?.tokensLimit ?? 0) > 0 ? ` / ${(g.usage!.tokensLimit).toLocaleString()}` : " (no cap — /
|
|
548
|
+
`Tokens: ${(g.usage?.tokensUsed ?? 0).toLocaleString()}${(g.usage?.tokensLimit ?? 0) > 0 ? ` / ${(g.usage!.tokensLimit).toLocaleString()}` : " (no cap — /glla tokenlimit=<n> to set)"}`,
|
|
538
549
|
];
|
|
539
550
|
if (g.auditHistory && g.auditHistory.length > 0) {
|
|
540
551
|
lines.push(`Audits: ${g.auditHistory.length} (${g.auditHistory.filter((v) => v.approved).length} approved)`);
|
|
@@ -837,7 +848,7 @@ function notifyExternal(ctx: ExtensionContext, message: string): void {
|
|
|
837
848
|
}
|
|
838
849
|
|
|
839
850
|
// =================================================================
|
|
840
|
-
// Loop 3: /loop — metric-driven
|
|
851
|
+
// Loop 3: /loop — metric-driven process loop (never completes)
|
|
841
852
|
//
|
|
842
853
|
// The anti-doorknob law: the loop only believes a number. The orchestrator
|
|
843
854
|
// runs the user's measure command (via pi.exec) after every agent turn;
|
|
@@ -1412,7 +1423,7 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1412
1423
|
parameters: Type.Object({
|
|
1413
1424
|
objective: Type.String({ description: "The clarified, concrete objective (single item) or a summary when items[] is used" }),
|
|
1414
1425
|
verificationContract: Type.Optional(Type.String({ description: "Checkable done-criteria (commands, file states, test outcomes)" })),
|
|
1415
|
-
items: Type.Optional(Type.Array(Type.String(), { description: "LIST drafting only: many objectives at once (e.g. 'queue these 50 things'). Each becomes a
|
|
1426
|
+
items: Type.Optional(Type.Array(Type.String(), { description: "LIST drafting only: many objectives at once (e.g. 'queue these 50 things'). Each becomes a list item; per-item 'Done when:' clauses are honored." })),
|
|
1416
1427
|
}),
|
|
1417
1428
|
async execute(_id, params, _signal, _onUpdate, execCtx) {
|
|
1418
1429
|
const p = params as { objective: string; verificationContract?: string; items?: string[] };
|
|
@@ -2079,7 +2090,7 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
2079
2090
|
// We detect duplicates at session start and warn loudly once.
|
|
2080
2091
|
// =================================================================
|
|
2081
2092
|
|
|
2082
|
-
const OUR_COMMANDS = ["goal", "
|
|
2093
|
+
const OUR_COMMANDS = ["goal", "glla", "list", "loop"];
|
|
2083
2094
|
let collisionWarned = false;
|
|
2084
2095
|
|
|
2085
2096
|
// Providers verified to exist in a bare (extension-less) session. The auditor
|
|
@@ -2149,7 +2160,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
2149
2160
|
// /loop — the metric loop (draft|start|status|stop)
|
|
2150
2161
|
// /gla — the settings UI (+ scriptable key=value)
|
|
2151
2162
|
pi.registerCommand("goal", {
|
|
2152
|
-
description: "Set/draft a goal, or /goal status|pause|resume|cancel|tweak <text>|archive
|
|
2163
|
+
description: "Set/draft a goal, or /goal status|pause|resume|cancel|tweak <text>|archive|start <objective>. Objectives without a 'Done when:' clause are grilled into a contract first; include the clause or use /goal start to skip the interview and activate instantly.",
|
|
2153
2164
|
handler: (args: string, ctx: ExtensionContext) => { rememberCtx(ctx); return cmdGoal(args, ctx); },
|
|
2154
2165
|
});
|
|
2155
2166
|
const settingsHandler = (args: string, ctx: ExtensionContext) => { rememberCtx(ctx); return cmdSettings(args, ctx); };
|
|
@@ -2157,23 +2168,12 @@ export default function (pi: ExtensionAPI): void {
|
|
|
2157
2168
|
description: "Open the settings UI for goals, loops, lists, and the auditor. Scriptable form: /glla key=value · /glla project key=value",
|
|
2158
2169
|
handler: settingsHandler,
|
|
2159
2170
|
});
|
|
2160
|
-
// /gla kept as an alias (renamed package, v0.15.0) — muscle memory is real.
|
|
2161
|
-
pi.registerCommand("gla", {
|
|
2162
|
-
description: "Alias for /glla (package renamed to pi-goal-list-loop-audit in v0.15.0).",
|
|
2163
|
-
handler: settingsHandler,
|
|
2164
|
-
});
|
|
2165
2171
|
pi.registerCommand("list", {
|
|
2166
2172
|
description: "Loop 2: the list of audited goals — order is the default, not the law. /list add <obj or file or paste> | /list show | /list next [n] | /list remove <n> | /list clear",
|
|
2167
2173
|
handler: (args: string, ctx: ExtensionContext) => { rememberCtx(ctx); return cmdList(args, ctx); },
|
|
2168
2174
|
});
|
|
2169
|
-
// Alias for one release (renamed 0.10.0 back to /list — order isn't
|
|
2170
|
-
// mandatory with subagents; it's a shopping list, not a FIFO).
|
|
2171
|
-
pi.registerCommand("queue", {
|
|
2172
|
-
description: "Alias for /list (renamed in v0.10.0). Same subcommands.",
|
|
2173
|
-
handler: (args: string, ctx: ExtensionContext) => { rememberCtx(ctx); return cmdList(args, ctx); },
|
|
2174
|
-
});
|
|
2175
2175
|
pi.registerCommand("loop", {
|
|
2176
|
-
description: "Loop 3: metric-driven
|
|
2176
|
+
description: "Loop 3: metric-driven process — it never completes. /loop start \"<target>\" measure=\"<cmd>\" direction=min|max [window=5] [max=50] [time=<hours>] [tokens=<budget>] [branch=1] | /loop status | /loop stop. 'Improve until X' is a /goal, not a loop.",
|
|
2177
2177
|
handler: (args: string, ctx: ExtensionContext) => { rememberCtx(ctx); return cmdLoop(args, ctx); },
|
|
2178
2178
|
});
|
|
2179
2179
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
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",
|
|
@@ -43,7 +43,7 @@ You have `read`, `write`, `edit`, `bash`, `grep`, `find`, `ls`, and the goal too
|
|
|
43
43
|
|
|
44
44
|
If the objective decomposes into milestones and no task list exists yet, call `propose_task_list` early — the user confirms it, then you track progress with `complete_task` / `update_task_status` as you go (not in a batch at the end). Limits: 20 tasks, 5 subtasks per task.
|
|
45
45
|
|
|
46
|
-
When the agent calls any of these, the orchestrator tracks the call and persists state to `.pi-
|
|
46
|
+
When the agent calls any of these, the orchestrator tracks the call and persists state to `.pi-glla/active.jsonl`.
|
|
47
47
|
|
|
48
48
|
## TASK WORKFLOW
|
|
49
49
|
|
package/scripts/smoke.sh
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# pi-goal-list-loop-audit — live integration smoke
|
|
3
3
|
#
|
|
4
4
|
# Drives a real pi session in tmux against a scratch dir and asserts on the
|
|
5
|
-
# .pi-
|
|
5
|
+
# .pi-glla ledger. This is the M2 "integration harness": it exercises the full
|
|
6
6
|
# loop (goal → agent work → complete_goal → isolated auditor → archive) with
|
|
7
7
|
# real models, which unit tests cannot do.
|
|
8
8
|
#
|
|
@@ -43,7 +43,7 @@ wait_for() { # wait_for <pattern> <timeout-s>
|
|
|
43
43
|
return 1
|
|
44
44
|
}
|
|
45
45
|
ledger_has() { # ledger_has <jq-ish python expr substring>
|
|
46
|
-
python3 - "$1" "$WORK/.pi-
|
|
46
|
+
python3 - "$1" "$WORK/.pi-glla/active.jsonl" <<'EOF'
|
|
47
47
|
import json, sys
|
|
48
48
|
needle, path = sys.argv[1], sys.argv[2]
|
|
49
49
|
try:
|
|
@@ -81,7 +81,7 @@ case "$SCENARIO" in
|
|
|
81
81
|
sleep 2
|
|
82
82
|
if [ -f "$WORK/smoke.txt" ]; then pass "smoke.txt created"; else fail "smoke.txt missing"; fi
|
|
83
83
|
if ledger_has '"approved":true'; then pass "ledger records approval"; else fail "ledger missing approval"; fi
|
|
84
|
-
if ls "$WORK/.pi-
|
|
84
|
+
if ls "$WORK/.pi-glla/archive/"*.md >/dev/null 2>&1; then pass "goal archived"; else fail "archive empty"; fi
|
|
85
85
|
if ledger_has '"regressionShieldPassed":true'; then pass "regression_shield recorded"; else fail "shield outcome missing"; fi
|
|
86
86
|
;;
|
|
87
87
|
|
|
@@ -93,11 +93,11 @@ case "$SCENARIO" in
|
|
|
93
93
|
if wait_for "approved by auditor" 120; then pass "item 1 approved"; else fail "item 1 not approved"; fi
|
|
94
94
|
# wait for second archive file
|
|
95
95
|
for i in $(seq 1 120); do
|
|
96
|
-
n=$(ls "$WORK/.pi-
|
|
96
|
+
n=$(ls "$WORK/.pi-glla/archive/"*.md 2>/dev/null | wc -l)
|
|
97
97
|
[ "$n" -ge 2 ] && break
|
|
98
98
|
sleep 1
|
|
99
99
|
done
|
|
100
|
-
n=$(ls "$WORK/.pi-
|
|
100
|
+
n=$(ls "$WORK/.pi-glla/archive/"*.md 2>/dev/null | wc -l)
|
|
101
101
|
if [ "$n" -ge 2 ]; then pass "both items archived ($n)"; else fail "only $n archived"; fi
|
|
102
102
|
if [ -f "$WORK/a.txt" ] && [ -f "$WORK/b.txt" ]; then pass "both files created"; else fail "files missing"; fi
|
|
103
103
|
if ledger_has '"list":[]'; then pass "list drained"; else fail "list not empty"; fi
|
|
@@ -119,7 +119,7 @@ case "$SCENARIO" in
|
|
|
119
119
|
echo 5 > "$WORK/num.txt"
|
|
120
120
|
NOTIFY_LOG="$WORK/notify.log"
|
|
121
121
|
# project scope — never write test config into the user's GLOBAL settings
|
|
122
|
-
send "/
|
|
122
|
+
send "/glla project notify='echo \$1 >> $NOTIFY_LOG'"
|
|
123
123
|
sleep 4
|
|
124
124
|
send '/loop start "Reduce the number in num.txt toward zero, never below 0" measure="cat num.txt" direction=min window=3 max=12'
|
|
125
125
|
say "waiting for plateau stop (up to 300s)"
|