pi-goal-list-loop-audit 0.38.46 → 0.38.47
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 +10 -0
- package/INSTALL.md +9 -9
- package/README.md +14 -14
- package/docs/INDEX.md +25 -27
- package/extensions/goal-continuation.ts +14 -9
- package/extensions/goal-loop.ts +2 -9
- package/extensions/loops/goal-list-queue.ts +4 -8
- package/extensions/prompt-layers.ts +145 -0
- package/package.json +1 -1
- package/prompts/goal-loop-continuation.md +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.38.47 — layered prompts: skeleton plus on-demand detail (2026-09-11)
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Prompt layers:** every `prompts/*.md` file is now a static skeleton plus on-demand detail blocks (`<!-- glla-layer: detail <id> -->`). The continuation template carries three conditional details — `auditor-disapproval` (live disapproval), `survey-pivot` (survey objective), `session-restart` (resync/recovery) — loaded only when their trigger is already known at render time; loop, draft, and plan prompts are skeleton-only (per-iteration essential or already file-granular on-demand). Static layers resolve before dynamic substitution, in file order every turn.
|
|
8
|
+
- **Measured saving:** clean-scenario full continuation render drops 24,347 to 21,801 chars (~10%, est. tokens 6,087 to 5,451); firing predicates re-add exactly their own bodies.
|
|
9
|
+
- **Loud failure:** a missing prompt file, unclosed/duplicate block, or unknown detail id throws naming the file — the `[template-not-found]`, inline mini-prompt, and `[DRAFTING] Clarify` silent fallbacks are gone.
|
|
10
|
+
- **Mid-turn contingencies stay put:** subagent death, provider errors, detached commits, stalls, and task workflow remain in the skeleton (their trigger cannot be known before the turn; no mid-turn fetch exists).
|
|
11
|
+
- **Pins:** `tests/prompt-layers.test.ts` (19) — full-assembly render-diff for all 7 files, exact-delta proofs, loud-missing, order determinism; `tests/context-growth-measurement.test.ts` and `tests/context-checkpoint.test.ts` re-baselined deliberately (deltas verified byte-exact against a pre-change worktree).
|
|
12
|
+
|
|
3
13
|
## 0.38.46 — rich Antigravity-style terminal summaries (2026-09-11)
|
|
4
14
|
|
|
5
15
|
### Changed
|
package/INSTALL.md
CHANGED
|
@@ -31,11 +31,11 @@ GLLA loads into new pi sessions. If pi is already open, reload that session:
|
|
|
31
31
|
|
|
32
32
|
## Updating
|
|
33
33
|
|
|
34
|
-
The status line always shows the running version (`glla: … · v0.38.
|
|
34
|
+
The status line always shows the running version (`glla: … · v0.38.46`).
|
|
35
35
|
When the npm registry is ahead, it also nudges:
|
|
36
36
|
|
|
37
37
|
```text
|
|
38
|
-
glla: … · v0.38.
|
|
38
|
+
glla: … · v0.38.45 · update v0.38.46 available
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
The nudge comes from a daily sidecar check (`.pi-glla/update-check.json`),
|
|
@@ -45,7 +45,7 @@ refreshed on command contact and never blocking a turn. To update:
|
|
|
45
45
|
pi install npm:pi-goal-list-loop-audit@latest
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
Then `/reload` every open pi session
|
|
48
|
+
Then `/reload` every open pi session: a session keeps running the
|
|
49
49
|
version it loaded with until reloaded. `/glla version` says whether the
|
|
50
50
|
current session is stale and repeats the update command.
|
|
51
51
|
|
|
@@ -58,7 +58,7 @@ and confirmation UX:
|
|
|
58
58
|
pi install npm:@juicesharp/rpiv-ask-user-question
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
For best automation and quality, add the **pinned parallel-orchestration companion** (`pi-subagents` 0.62.0)
|
|
61
|
+
For best automation and quality, add the **pinned parallel-orchestration companion** (`pi-subagents` 0.62.0): GLLA's power-max choice for `runs.all` fan-out, `runs.lanes` worker→review→fix chains, structured verification, worktree isolation, and durable recovery:
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
64
|
pi install npm:pi-subagents@0.62.0
|
|
@@ -260,9 +260,9 @@ supervisor, or let it own the session instead of GLLA.
|
|
|
260
260
|
|
|
261
261
|
## Further reading
|
|
262
262
|
|
|
263
|
-
- [`README.md`](README.md)
|
|
263
|
+
- [`README.md`](README.md): product overview, first-use guide, commands,
|
|
264
264
|
companion policy, autonomy model, recovery, and maintainer map;
|
|
265
|
-
- [`docs/DESIGN.md`](docs/DESIGN.md)
|
|
266
|
-
- [`docs/INDEX.md`](docs/INDEX.md)
|
|
267
|
-
- [`docs/RELEASING.md`](docs/RELEASING.md)
|
|
268
|
-
- [`CHANGELOG.md`](CHANGELOG.md)
|
|
265
|
+
- [`docs/DESIGN.md`](docs/DESIGN.md): architecture and design decisions;
|
|
266
|
+
- [`docs/INDEX.md`](docs/INDEX.md): shipped and repository-only documentation;
|
|
267
|
+
- [`docs/RELEASING.md`](docs/RELEASING.md): release process;
|
|
268
|
+
- [`CHANGELOG.md`](CHANGELOG.md): version history.
|
package/README.md
CHANGED
|
@@ -94,7 +94,7 @@ Done when:
|
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
The contract is the important part. Replace the example with the result you
|
|
97
|
-
actually want and checks that another person
|
|
97
|
+
actually want and checks that another person (or another agent) could inspect.
|
|
98
98
|
|
|
99
99
|
For an objective that needs shaping, start with bare `/goal` and answer the
|
|
100
100
|
interview. GLLA will research the ambiguity, ask focused questions, and show a
|
|
@@ -132,7 +132,7 @@ forcing every problem into a loop.
|
|
|
132
132
|
| `/list` | Several outcomes or a backlog of independently verifiable items | Each item is worked and audited separately; the queue advances safely |
|
|
133
133
|
| `/loop` | Ongoing improvement with no single final item | A metric, specification, audit cadence, bound, or `/loop stop` ends the process |
|
|
134
134
|
|
|
135
|
-
### `/goal
|
|
135
|
+
### `/goal`: one outcome
|
|
136
136
|
|
|
137
137
|
```text
|
|
138
138
|
/goal # interview + Confirm
|
|
@@ -152,7 +152,7 @@ A goal is the best default for work with a finish line. If the agent discovers
|
|
|
152
152
|
that the objective is too large, it can propose a bounded task plan instead of
|
|
153
153
|
quietly inventing an unbounded backlog.
|
|
154
154
|
|
|
155
|
-
### `/list
|
|
155
|
+
### `/list`: a durable work pool
|
|
156
156
|
|
|
157
157
|
```text
|
|
158
158
|
/list "fix the cache. Done when: tests pass"
|
|
@@ -179,11 +179,11 @@ bounded bootstrap turn containing `propose_task_list`. Confirm the redraft;
|
|
|
179
179
|
automatic repeats are fenced. Use `/list resume` or `/glla resume` for an
|
|
180
180
|
intentional retry of a waiting/restored queue, and `/list next` when you
|
|
181
181
|
intentionally want to skip or choose another queued item. `/list start` is
|
|
182
|
-
also explicit: it activates the queued head, or
|
|
182
|
+
also explicit: it activates the queued head, or, when the queue is empty, uses
|
|
183
183
|
one clear recent user request as a seed for the normal Confirm-gated list
|
|
184
184
|
drafting flow. Ambiguous context is never queued automatically.
|
|
185
185
|
|
|
186
|
-
### `/loop
|
|
186
|
+
### `/loop`: an improvement process
|
|
187
187
|
|
|
188
188
|
```text
|
|
189
189
|
/loop # interview + Confirm
|
|
@@ -274,13 +274,13 @@ GLLA is the supervisor. These companions add capabilities around it:
|
|
|
274
274
|
|
|
275
275
|
### Recommended for almost everyone
|
|
276
276
|
|
|
277
|
-
- **`@juicesharp/rpiv-ask-user-question
|
|
277
|
+
- **`@juicesharp/rpiv-ask-user-question`**: structured questions, multi-select,
|
|
278
278
|
previews, and Confirm dialogs for drafting and decisions. GLLA has a prose
|
|
279
279
|
fallback, but this is the intended UX.
|
|
280
280
|
|
|
281
|
-
### Recommended for power
|
|
281
|
+
### Recommended for power: parallel orchestration (`pi-subagents`)
|
|
282
282
|
|
|
283
|
-
- **`pi-subagents` 0.62.0 (pinned)
|
|
283
|
+
- **`pi-subagents` 0.62.0 (pinned): the power-max choice for GLLA.** Use it
|
|
284
284
|
when you want the best automation and quality: `runs.all` parallel fan-out,
|
|
285
285
|
`runs.lanes` worker→review→fix chains, `outputSchema` + `acceptance` structured
|
|
286
286
|
verification, `runs.host` gated shell, worktree isolation, model routing
|
|
@@ -295,7 +295,7 @@ GLLA is the supervisor. These companions add capabilities around it:
|
|
|
295
295
|
|
|
296
296
|
The main pi session remains the owner of the goal/list/loop; subagents are
|
|
297
297
|
workers and cannot silently replace the parent's objective. A short or mostly
|
|
298
|
-
sequential goal can still run cleanly without workers
|
|
298
|
+
sequential goal can still run cleanly without workers; install when
|
|
299
299
|
parallelism will pay for its coordination and model usage.
|
|
300
300
|
|
|
301
301
|
Display coexistence: pi-subagents renders its own inline run panels +
|
|
@@ -304,7 +304,7 @@ GLLA is the supervisor. These companions add capabilities around it:
|
|
|
304
304
|
swapping order, set pi-subagents `inlineToolDisplay: "summary"` (one
|
|
305
305
|
stable row per run) and pin `fleetViewPlacement`; GLLA's own richness is
|
|
306
306
|
the `subagentDisplayRichness` setting (`/glla` → Subagents): `rich`
|
|
307
|
-
(default
|
|
307
|
+
(default: worker rows + task linkage), `compact` (count line), `quiet`
|
|
308
308
|
(hung/aborting workers only). Upstream triple-render report:
|
|
309
309
|
nicobailon/pi-subagents#1931.
|
|
310
310
|
|
|
@@ -319,17 +319,17 @@ recommendation in the same session. Existing Tintin-era agent files are
|
|
|
319
319
|
cleaned only when GLLA's management marker proves that GLLA owns them; old
|
|
320
320
|
settings are not silently remapped to a different role. Do not stack
|
|
321
321
|
`@quintinshaw/pi-dynamic-workflows` as a competing orchestrator alongside
|
|
322
|
-
GLLA + `pi-subagents` in the same session
|
|
322
|
+
GLLA + `pi-subagents` in the same session: duplicate tools and competing
|
|
323
323
|
orchestration events create ambiguous ownership. Use its quality helpers
|
|
324
324
|
(`verify`/`judgePanel`/`loopUntilDry`) only as isolated complements if needed.
|
|
325
325
|
|
|
326
326
|
### Useful, but optional
|
|
327
327
|
|
|
328
|
-
- **`@pi-unipi/notify
|
|
328
|
+
- **`@pi-unipi/notify`**: Telegram, Gotify, or ntfy delivery when you need
|
|
329
329
|
alerts away from the desktop. GLLA's local notifications work without it;
|
|
330
330
|
when no command is configured, it auto-detects `notify-send`/`osascript`;
|
|
331
331
|
`notify=off` silences notifications.
|
|
332
|
-
- **`pi-chrome
|
|
332
|
+
- **`pi-chrome`**: logged-in browser research and interaction when a goal needs
|
|
333
333
|
a real web session. It is not required for repository-only work.
|
|
334
334
|
|
|
335
335
|
### What not to combine with GLLA
|
|
@@ -501,4 +501,4 @@ repository material, not first-use package content.
|
|
|
501
501
|
|
|
502
502
|
## License
|
|
503
503
|
|
|
504
|
-
GNU Affero General Public License v3.0-only
|
|
504
|
+
GNU Affero General Public License v3.0-only; see [LICENSE](LICENSE).
|
package/docs/INDEX.md
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
# docs/
|
|
1
|
+
# docs/ index
|
|
2
2
|
|
|
3
3
|
Ordered by reading path, not alphabetically.
|
|
4
4
|
|
|
5
5
|
## Active focus (recent work, durable artifacts)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
Policy contracts and recent changes live in the `audit/` directory of the
|
|
8
|
+
**repository checkout**; that directory is not shipped in the npm tarball
|
|
9
|
+
(see "Repository-only material" below). The shipped entry points are:
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- `../CHANGELOG.md` — user-facing changelog; an `Unreleased` section may lead
|
|
11
|
+
- `../CHANGELOG.md`: user-facing changelog; an `Unreleased` section may lead
|
|
14
12
|
the file, followed by the current released version. v0.35.5 adopted the six-label completion
|
|
15
13
|
recap; v0.35.6 added typed-boundary regression pins; v0.35.7 added
|
|
16
14
|
deterministic fast-fail pre-audits, zero-pause autonomous execution, and
|
|
@@ -18,7 +16,7 @@ For shipped docs, the relevant entry points are:
|
|
|
18
16
|
failback; v0.35.9 hardened cross-version npm tarball checks; v0.35.10
|
|
19
17
|
handles multi-entry npm dry-run reports; v0.35.11 accepts both npm report
|
|
20
18
|
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.
|
|
19
|
+
v0.35.14–v0.38.47 continue through the supervisor freeze (`/glla pause`),
|
|
22
20
|
load hold, auditor picker parity, Windows launch fix, zombie-watchdog
|
|
23
21
|
subagent carve-out, due-wait backstop, the `/glla agents` visibility panel,
|
|
24
22
|
durable state-root selection, blank-until-resume auditor context, frozen
|
|
@@ -28,43 +26,43 @@ For shipped docs, the relevant entry points are:
|
|
|
28
26
|
packed-artifact release verification, and the 2026-09-07 display/lifecycle/
|
|
29
27
|
settings audit pass (abort-latch send guards, ownership compare-and-swap,
|
|
30
28
|
auditor inherit/clear parity) and the v0.38.25 post-objective summary
|
|
31
|
-
(canonical approval render, persist + replay, audit-goal counts line), the v0.38.27 selective port of PRs #45/#46 (`/loop pause` soft-hold, widget subtask count), the v0.38.28 provenance-row closure fix, and the v0.38.29 compact active-card recovery/judgment projection
|
|
32
|
-
- `../README.md
|
|
29
|
+
(canonical approval render, persist + replay, audit-goal counts line), the v0.38.27 selective port of PRs #45/#46 (`/loop pause` soft-hold, widget subtask count), the v0.38.28 provenance-row closure fix, and the v0.38.29 compact active-card recovery/judgment projection; see CHANGELOG.md for the full trail.
|
|
30
|
+
- `../README.md`: what the plugin is, install, quickstart, and the
|
|
33
31
|
architectural guarantee (drafting + confirm + detached auditor).
|
|
34
|
-
- `../INSTALL.md
|
|
32
|
+
- `../INSTALL.md`: source install / local development setup; the recommended
|
|
35
33
|
companion plugins and the `auditor reads / writes are path-checked`
|
|
36
34
|
note.
|
|
37
35
|
|
|
38
36
|
## Entry points
|
|
39
|
-
- `../README.md
|
|
40
|
-
- `SETTINGS.md
|
|
41
|
-
- `../INSTALL.md
|
|
42
|
-
- `../CHANGELOG.md
|
|
37
|
+
- `../README.md`: what the plugin is, install, quickstart
|
|
38
|
+
- `SETTINGS.md`: canonical settings reference (files, precedence, every key)
|
|
39
|
+
- `../INSTALL.md`: source install / local development setup
|
|
40
|
+
- `../CHANGELOG.md`: user-facing changelog; the first versioned heading is
|
|
43
41
|
the current released package version (use `/glla version` to compare with
|
|
44
42
|
the registry); post-release work may appear in `Unreleased` above it.
|
|
45
43
|
|
|
46
44
|
## Architecture
|
|
47
|
-
- `DESIGN.md
|
|
48
|
-
- `DESIGN-long-running-supervision.md
|
|
49
|
-
- `GLLA-POSITIONING-AND-DECOMPOSITION-2026-08-08.md
|
|
45
|
+
- `DESIGN.md`: plugin design (types, state, extension lifecycle)
|
|
46
|
+
- `DESIGN-long-running-supervision.md`: v0.36.0 event/progress-driven supervision, aggressive recovery, terminal recaps, and future decision checklist
|
|
47
|
+
- `GLLA-POSITIONING-AND-DECOMPOSITION-2026-08-08.md`: ecosystem
|
|
50
48
|
positioning, competitor review, and the goal.ts decomposition plan
|
|
51
|
-
(the current strategic doc
|
|
49
|
+
(the current strategic doc; read this before touching
|
|
52
50
|
`extensions/loops/goal.ts`)
|
|
53
|
-
- `VISION-ASSIST.md
|
|
54
|
-
- `RELEASING.md
|
|
51
|
+
- `VISION-ASSIST.md`: vision-assist plugin notes
|
|
52
|
+
- `RELEASING.md`: how to publish to npm
|
|
55
53
|
|
|
56
54
|
## Supporting material
|
|
57
|
-
- `../prompts
|
|
58
|
-
- `../schemas
|
|
59
|
-
- `../examples
|
|
60
|
-
- `../CHANGELOG.md
|
|
61
|
-
- `/glla bug
|
|
55
|
+
- `../prompts/`: goal/loop drafting prompt templates
|
|
56
|
+
- `../schemas/`: goal state JSON schema
|
|
57
|
+
- `../examples/`: example objective files
|
|
58
|
+
- `../CHANGELOG.md`: user-facing changelog (unreleased at top)
|
|
59
|
+
- `/glla bug`: `extensions/goal-commands.ts:cmdGllaBug` captures failure context to `<stateDir>/bugs/<ts>-<id>.md` without touching `active.jsonl`/`goals/*.md` (see `tests/glla-bug-capture.test.ts`)
|
|
62
60
|
|
|
63
61
|
## Repository-only material
|
|
64
62
|
The audit history and competitor research live in `audit/` and `.research/`
|
|
65
63
|
for contributors, but are intentionally not included in the npm tarball.
|
|
66
64
|
|
|
67
65
|
## Research material
|
|
68
|
-
`.research
|
|
66
|
+
`.research/`: competitor plugin sources pulled from npm tarballs for study
|
|
69
67
|
(gitignored, local only). Re-pull with `cd .research && npm pack <pkg> &&
|
|
70
68
|
tar xzf <tgz>`; see the positioning doc's appendix for the package list.
|
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
type ObjectiveRepairTarget,
|
|
57
57
|
} from "./goal-loop-core.js";
|
|
58
58
|
import { auditorSurfaceSuppressed } from "./loops/goal-auditor-surface.js";
|
|
59
|
+
import { assemblePrompt } from "./prompt-layers.js";
|
|
59
60
|
import {
|
|
60
61
|
createContinuationDispatch,
|
|
61
62
|
transitionDispatch,
|
|
@@ -1235,7 +1236,7 @@ export function buildContinuationContent(goal: Goal, opts: { resync?: string; fi
|
|
|
1235
1236
|
const resync = opts.resync ?? "";
|
|
1236
1237
|
const marker = buildMarkerContent(goal.id);
|
|
1237
1238
|
const needsFull = (opts.firstSend ?? false) || needsFullContinuation(goal);
|
|
1238
|
-
if (needsFull) return { content: resync + continuationPrompt(goal), kind: resync ? "full+resync" : "full" };
|
|
1239
|
+
if (needsFull) return { content: resync + continuationPrompt(goal, { includeRestartDetail: Boolean(resync) || Boolean(goal.autoResumedAt) }), kind: resync ? "full+resync" : "full" };
|
|
1239
1240
|
if (resync) return { content: resync + marker, kind: "resync" };
|
|
1240
1241
|
return { content: marker, kind: "marker" };
|
|
1241
1242
|
}
|
|
@@ -1493,7 +1494,7 @@ export function buildPostCompactResync(briefExcerpt?: string): string {
|
|
|
1493
1494
|
return lines.join("\n") + "\n\n";
|
|
1494
1495
|
}
|
|
1495
1496
|
|
|
1496
|
-
export function continuationPrompt(goal: Goal): string {
|
|
1497
|
+
export function continuationPrompt(goal: Goal, opts: { includeRestartDetail?: boolean } = {}): string {
|
|
1497
1498
|
// Read the .md file as the template, then substitute {{tokens}}.
|
|
1498
1499
|
// For v0.1.0 we inline-substitute so we don't need fs at runtime.
|
|
1499
1500
|
const next = findNextPendingTask(goal.taskList?.tasks ?? []);
|
|
@@ -1503,13 +1504,17 @@ export function continuationPrompt(goal: Goal): string {
|
|
|
1503
1504
|
const taskSummary = goal.taskList?.tasks.length
|
|
1504
1505
|
? buildTaskSummary(goal.taskList.tasks)
|
|
1505
1506
|
: "(no task list)";
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1507
|
+
// Layered assembly: skeleton always; details only when their trigger is
|
|
1508
|
+
// already known at render time. Static layers resolve before dynamic
|
|
1509
|
+
// ${...} substitution, in file order every turn. Mid-turn contingencies
|
|
1510
|
+
// (subagent death, provider errors, detached commits, stalls, task
|
|
1511
|
+
// workflow) stay in the skeleton: their trigger cannot be known before
|
|
1512
|
+
// the turn, and there is no mid-turn detail fetch.
|
|
1513
|
+
const detailIds = new Set<string>();
|
|
1514
|
+
if (liveDisapproval(goal.auditHistory ?? [])) detailIds.add("auditor-disapproval");
|
|
1515
|
+
if (isFullAuditObjective(goal.objective)) detailIds.add("survey-pivot");
|
|
1516
|
+
if (opts.includeRestartDetail || goal.autoResumedAt) detailIds.add("session-restart");
|
|
1517
|
+
const tmpl = assemblePrompt("goal-loop-continuation.md", detailIds);
|
|
1513
1518
|
// v0.25.0 (contract items 22/28): conditional directives — aggressiveMode
|
|
1514
1519
|
// TODOs from the audit cap, and the full-audit fan-out directive when the
|
|
1515
1520
|
// objective reads as a survey pivot. The canonical judgment policy and
|
package/extensions/goal-loop.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
supervisorPaused,
|
|
27
27
|
} from "./goal-loop-core.js";
|
|
28
28
|
import { BACKOFF_IDLE_RETRY_MS, MEASURE_TIMEOUT_MS } from "./goal-loop-backoff.js";
|
|
29
|
+
import { loadPromptWhole } from "./prompt-layers.js";
|
|
29
30
|
import {
|
|
30
31
|
REPETITION,
|
|
31
32
|
continueVariant,
|
|
@@ -285,15 +286,7 @@ function loopPrompt(loop: LoopState, regressionNote: string, strategyNote: strin
|
|
|
285
286
|
// v0.23.0: metricless loops get their own prompt — no metric section,
|
|
286
287
|
// anti-doorknob rules instead of anti-gaming rules.
|
|
287
288
|
const metricless = !loop.measureCmd;
|
|
288
|
-
const
|
|
289
|
-
let tmpl: string;
|
|
290
|
-
try {
|
|
291
|
-
tmpl = fs.readFileSync(tmplPath, "utf-8");
|
|
292
|
-
} catch {
|
|
293
|
-
tmpl = metricless
|
|
294
|
-
? `[LOOP ITERATION ${loop.iteration + 1}] Target: ${loop.target}. Metricless spec loop — make ONE real, inspectable change advancing the target. No cosmetic churn. ${variantNote} ${interventionNote}`
|
|
295
|
-
: `[LOOP ITERATION ${loop.iteration + 1}] Target: ${loop.target}. Measure: ${loop.measureCmd} (${loop.direction}). Make ONE small change to improve the metric. ${interventionNote}`;
|
|
296
|
-
}
|
|
289
|
+
const tmpl = loadPromptWhole(metricless ? "goal-loop-forever-metricless.md" : "goal-loop-forever.md");
|
|
297
290
|
return tmpl
|
|
298
291
|
.replace(/\$\{ITERATION\}/g, String(loop.iteration + 1))
|
|
299
292
|
.replace(/\$\{TARGET\}/g, loop.target)
|
|
@@ -26,6 +26,7 @@ import { Type } from "typebox";
|
|
|
26
26
|
// (positioning doc invariant #2). Property reads on the imported binding are
|
|
27
27
|
// fine; wholesale replacement goes through replaceState().
|
|
28
28
|
import { state, replaceState, persistStateLine } from "../goal-state.js";
|
|
29
|
+
import { loadPromptWhole } from "../prompt-layers.js";
|
|
29
30
|
|
|
30
31
|
import {
|
|
31
32
|
type Goal,
|
|
@@ -863,11 +864,9 @@ async function startDrafting(ctx: ExtensionContext, target: "goal" | "list" | "l
|
|
|
863
864
|
: target === "loop"
|
|
864
865
|
? `${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].`
|
|
865
866
|
: `${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>.`);
|
|
866
|
-
|
|
867
|
-
let tmpl
|
|
868
|
-
|
|
869
|
-
tmpl = fs.readFileSync(tmplPath, "utf-8");
|
|
870
|
-
if (target === "list") {
|
|
867
|
+
// Layered-prompt contract: a missing draft file fails loudly — never render lean.
|
|
868
|
+
let tmpl = loadPromptWhole(file);
|
|
869
|
+
if (target === "list") {
|
|
871
870
|
tmpl = tmpl.replace(
|
|
872
871
|
"[GOAL DRAFTING]",
|
|
873
872
|
"[LIST DRAFTING — the confirmed item goes into the /list LIST, it does not activate immediately. " +
|
|
@@ -880,9 +879,6 @@ async function startDrafting(ctx: ExtensionContext, target: "goal" | "list" | "l
|
|
|
880
879
|
"is still a SHORT task — never an aggregate wrapper ('land all N findings' with a '≥N commits' contract is the " +
|
|
881
880
|
"canonical anti-pattern: the auto-committer squashes, the count fails, the auditor disapproves finished work).]",
|
|
882
881
|
);
|
|
883
|
-
}
|
|
884
|
-
} catch {
|
|
885
|
-
tmpl = `[DRAFTING] Clarify the user's ${target}, then call ${tool}.`;
|
|
886
882
|
}
|
|
887
883
|
// v0.14.0: the LLM grills (its strength — v0.13.0's canned questionnaire
|
|
888
884
|
// accepted non-answers), the plugin enforces the floor: propose_goal_draft
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// prompt-layers.ts — static skeleton + on-demand detail assembly for prompts/
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Every prompts/*.md file is one canonical, editor-renderable document.
|
|
5
|
+
// Detail sections are wrapped in zero-width structural markers:
|
|
6
|
+
//
|
|
7
|
+
// <!-- glla-layer: detail <id> -->
|
|
8
|
+
// ...section body, untouched...
|
|
9
|
+
// <!-- glla-layer: end -->
|
|
10
|
+
//
|
|
11
|
+
// Everything outside a detail block is the skeleton: always loaded.
|
|
12
|
+
// A detail block loads only when its phase predicate holds. Assembly order
|
|
13
|
+
// is file order, every turn — static layers resolve before dynamic
|
|
14
|
+
// ${...} substitution slots, so renders are deterministic.
|
|
15
|
+
//
|
|
16
|
+
// Loudness contract: any structural problem (missing file, unclosed block,
|
|
17
|
+
// duplicate id, end-without-start, unknown requested id) THROWS naming the
|
|
18
|
+
// file. A render never proceeds lean on a "[template-not-found]" fallback.
|
|
19
|
+
// Marker lines are structural only and are stripped from assembled output,
|
|
20
|
+
// so full assembly (every detail active) is byte-identical to the source
|
|
21
|
+
// file minus its marker lines.
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
import * as fs from "node:fs";
|
|
25
|
+
import * as path from "node:path";
|
|
26
|
+
|
|
27
|
+
const LAYER_START_RE = /^\s*<!--\s*glla-layer:\s*detail\s+([A-Za-z0-9_-]+)\s*-->\s*$/;
|
|
28
|
+
const LAYER_END_RE = /^\s*<!--\s*glla-layer:\s*end\s*-->\s*$/;
|
|
29
|
+
|
|
30
|
+
export interface PromptSegment {
|
|
31
|
+
kind: "skeleton" | "detail";
|
|
32
|
+
/** Present only for detail segments. */
|
|
33
|
+
id?: string;
|
|
34
|
+
/** Exact source lines for this segment (marker lines excluded). */
|
|
35
|
+
lines: string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function promptFilePath(name: string): string {
|
|
39
|
+
return path.resolve(__dirname, "..", "prompts", name);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Parse one prompt source into ordered segments. Throws naming the file
|
|
43
|
+
* on any structural defect. Pure (no fs) so tests can feed fixtures. */
|
|
44
|
+
export function parsePromptLayers(source: string, name: string): PromptSegment[] {
|
|
45
|
+
const segments: PromptSegment[] = [];
|
|
46
|
+
let current: PromptSegment = { kind: "skeleton", lines: [] };
|
|
47
|
+
let openId: string | null = null;
|
|
48
|
+
const seen = new Set<string>();
|
|
49
|
+
const fail = (why: string): never => {
|
|
50
|
+
throw new Error(`[glla] prompts/${name}: ${why}`);
|
|
51
|
+
};
|
|
52
|
+
for (const line of source.split("\n")) {
|
|
53
|
+
const start = line.match(LAYER_START_RE);
|
|
54
|
+
const end = LAYER_END_RE.test(line);
|
|
55
|
+
if (start) {
|
|
56
|
+
if (openId !== null) fail(`detail "${openId}" unclosed before detail "${start[1]}" starts`);
|
|
57
|
+
const id = start[1] as string;
|
|
58
|
+
if (seen.has(id)) fail(`duplicate detail id "${id}"`);
|
|
59
|
+
seen.add(id);
|
|
60
|
+
segments.push(current);
|
|
61
|
+
current = { kind: "detail", id, lines: [] };
|
|
62
|
+
openId = id;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (end) {
|
|
66
|
+
if (openId === null) fail("glla-layer end marker without an open detail block");
|
|
67
|
+
segments.push(current);
|
|
68
|
+
current = { kind: "skeleton", lines: [] };
|
|
69
|
+
openId = null;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
current.lines.push(line);
|
|
73
|
+
}
|
|
74
|
+
if (openId !== null) fail(`detail "${openId}" unclosed at end of file`);
|
|
75
|
+
segments.push(current);
|
|
76
|
+
return segments;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Read + parse a prompt file. Throws naming the path when unreadable. */
|
|
80
|
+
export function loadPromptSegments(name: string): PromptSegment[] {
|
|
81
|
+
const file = promptFilePath(name);
|
|
82
|
+
let source: string;
|
|
83
|
+
try {
|
|
84
|
+
source = fs.readFileSync(file, "utf-8");
|
|
85
|
+
} catch (err) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
`[glla] prompt file missing: prompts/${name} (${file}): ${(err as Error)?.message ?? err}. ` +
|
|
88
|
+
"Refusing to render lean — restore the file, do not proceed without it.",
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return parsePromptLayers(source, name);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Raw whole-file read. Throws (loud) when unreadable. Use for
|
|
95
|
+
* skeleton-only phases and for render-diff baselines. */
|
|
96
|
+
export function loadPromptWhole(name: string): string {
|
|
97
|
+
const file = promptFilePath(name);
|
|
98
|
+
try {
|
|
99
|
+
return fs.readFileSync(file, "utf-8");
|
|
100
|
+
} catch (err) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`[glla] prompt file missing: prompts/${name} (${file}): ${(err as Error)?.message ?? err}. ` +
|
|
103
|
+
"Refusing to render lean — restore the file, do not proceed without it.",
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Detail ids declared by a prompt file, in file order. */
|
|
109
|
+
export function promptDetailIds(name: string): string[] {
|
|
110
|
+
const ids: string[] = [];
|
|
111
|
+
for (const seg of loadPromptSegments(name)) {
|
|
112
|
+
if (seg.kind === "detail" && seg.id) ids.push(seg.id);
|
|
113
|
+
}
|
|
114
|
+
return ids;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Assemble skeleton + the requested details in file order. Unknown ids
|
|
118
|
+
* throw. Empty `active` renders the bare skeleton. */
|
|
119
|
+
export function assemblePrompt(name: string, active: Iterable<string>): string {
|
|
120
|
+
const segments = loadPromptSegments(name);
|
|
121
|
+
const want = new Set(active);
|
|
122
|
+
const known = new Set<string>();
|
|
123
|
+
for (const seg of segments) {
|
|
124
|
+
if (seg.kind === "detail" && seg.id) known.add(seg.id);
|
|
125
|
+
}
|
|
126
|
+
for (const id of want) {
|
|
127
|
+
if (!known.has(id)) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
`[glla] prompts/${name}: unknown detail id "${id}" (known: ${[...known].join(", ") || "(none)"}). ` +
|
|
130
|
+
"Refusing to render lean — fix the request, do not proceed without it.",
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const out: string[] = [];
|
|
135
|
+
for (const seg of segments) {
|
|
136
|
+
if (seg.kind === "skeleton" || (seg.id && want.has(seg.id))) out.push(...seg.lines);
|
|
137
|
+
}
|
|
138
|
+
return out.join("\n");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Full assembly: every declared detail active. Render-diff gate: this must
|
|
142
|
+
* equal the source file minus its marker lines. */
|
|
143
|
+
export function assemblePromptFull(name: string): string {
|
|
144
|
+
return assemblePrompt(name, promptDetailIds(name));
|
|
145
|
+
}
|
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.47",
|
|
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",
|
|
@@ -77,6 +77,7 @@ When the agent calls any of these, the orchestrator tracks the call and persists
|
|
|
77
77
|
- **Bound every long command.** Wrap test suites, builds, and dev servers in `timeout <seconds>` (e.g. `timeout 120 bun test src/lib`). An unbounded command that hangs burns an hour; a bounded one burns two minutes and tells you it hung. If a command produces no output for many minutes, treat it as hung: kill it, diagnose why, rerun bounded.
|
|
78
78
|
- **Chunk output near context-full & microcompaction.** When the conversation is heavy (long-running audit, deep debug, big rollout), prefer smaller commits, smaller tool outputs, and focused reasoning — one or two punchy paragraphs, one well-scoped tool call at a time. Don't try to fit a thousand lines of work into one reply. Spool massive stdout/diffs to disk logs if needed. glla's auto-continue fires on `stop_reason="length"` and will reschedule you; chunking is cheaper than recovering from the cap. Save large file writes for their own turns.
|
|
79
79
|
|
|
80
|
+
<!-- glla-layer: detail auditor-disapproval -->
|
|
80
81
|
## WHEN THE AUDITOR DISAPPROVES
|
|
81
82
|
|
|
82
83
|
If the orchestrator tells you the auditor disapproved, **investigate before asking the user**:
|
|
@@ -90,7 +91,9 @@ If the orchestrator tells you the auditor disapproved, **investigate before aski
|
|
|
90
91
|
"The auditor's last 3 reports all complain about saves-3 not shipping. The current objective IS saves-3, but the work shipped is menu-3 + kingdom-2 (different items). I shipped those because [reason]. The auditor is disapproving because the original objective isn't literally shipped. Three options: A. /goal tweak the objective to menu-3+kingdom-2, then /goal resume — B. Re-scope saves-3 and ship it — C. Pivot to a different item entirely."
|
|
91
92
|
|
|
92
93
|
Do NOT ask the user to choose between generic options like "/goal resume / Move on silently / Different item". Those options tell the user nothing. Always include YOUR ASSESSMENT with quoted objections and shipped evidence.
|
|
94
|
+
<!-- glla-layer: end -->
|
|
93
95
|
|
|
96
|
+
<!-- glla-layer: detail survey-pivot -->
|
|
94
97
|
## PIVOT DETECTION
|
|
95
98
|
|
|
96
99
|
When the user says "do a full audit", "survey the project", "find all problems", "mark a tasklist", or similar — the goal is a SURVEY, not a single fix. You must:
|
|
@@ -100,6 +103,7 @@ When the user says "do a full audit", "survey the project", "find all problems",
|
|
|
100
103
|
3. Use subagents to PARALLEL-survey different subsystems (game logic, UI, audio, tests, docs) — one `scout` agent per subsystem, spawned in a single message.
|
|
101
104
|
4. Don't ship a single bug fix and then ask if the user wants to continue — the user already said "do a full audit".
|
|
102
105
|
5. After the task list is confirmed, work through tasks systematically with `complete_task` / `update_task_status`.
|
|
106
|
+
<!-- glla-layer: end -->
|
|
103
107
|
|
|
104
108
|
## WHEN SUBAGENTS DIE: RESUME, DON'T RESPAWN
|
|
105
109
|
|
|
@@ -108,9 +112,11 @@ A failed subagent's SESSION survives its death (verified in pi-subagents source:
|
|
|
108
112
|
1. **Resume it immediately**: `Agent(resume: "<id>", prompt: "Wrap up now: report what you found within ~150 lines.")`. A resumed agent already HAS its research; it only needs to report. Do not infer a reset time from the error text.
|
|
109
113
|
2. **Resume failed with "not found"?** The manager forgot it (a /reload wipes the in-process registry — old IDs are unresumable). Only NOW respawn, and never the same wide brief: SPLIT into 2 narrower agents or ABSORB the remainder inline.
|
|
110
114
|
|
|
115
|
+
<!-- glla-layer: detail session-restart -->
|
|
111
116
|
## AFTER A SESSION RESTART: YOUR SUBAGENTS ARE DEAD
|
|
112
117
|
|
|
113
118
|
Subagents run IN-PROCESS — a /reload or restart kills every running one instantly and silently (no failure event, no ✗), and their IDs become unresumable. On a restored session, check your transcript for in-flight agents and relaunch them (tight briefs) or absorb their scope — do NOT sit waiting for results that can never arrive. This is why long fan-out passes belong under a glla goal/list item: the goal plane survives restarts and re-drives the fan-out through the continuation; an ad-hoc fan-out dies with the tab.
|
|
119
|
+
<!-- glla-layer: end -->
|
|
114
120
|
|
|
115
121
|
## WHEN SUBAGENTS HIT PROVIDER/RUNTIME ERRORS
|
|
116
122
|
|