specpi 0.10.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +150 -0
  2. package/LICENSE +21 -0
  3. package/NPM_RELEASE.md +110 -0
  4. package/README.md +155 -0
  5. package/SECURITY.md +85 -0
  6. package/SECURITY_MODEL.md +107 -0
  7. package/THIRD_PARTY.md +61 -0
  8. package/browser-runtime/package-lock.json +86 -0
  9. package/browser-runtime/package.json +15 -0
  10. package/extensions/browser/core.mjs +306 -0
  11. package/extensions/browser/index.ts +723 -0
  12. package/extensions/browser/smoke.mjs +47 -0
  13. package/extensions/command-guard/bash.mjs +1426 -0
  14. package/extensions/command-guard/cmd.mjs +369 -0
  15. package/extensions/command-guard/core.mjs +506 -0
  16. package/extensions/command-guard/index.ts +634 -0
  17. package/extensions/command-guard/managed-files.mjs +22 -0
  18. package/extensions/command-guard/paths.mjs +398 -0
  19. package/extensions/command-guard/powershell-parser.ps1 +47 -0
  20. package/extensions/command-guard/powershell.mjs +655 -0
  21. package/extensions/command-guard/redact.mjs +65 -0
  22. package/extensions/command-guard/rules.mjs +2557 -0
  23. package/extensions/command-guard/smoke.mjs +422 -0
  24. package/extensions/files/core.mjs +422 -0
  25. package/extensions/files/index.ts +678 -0
  26. package/extensions/spec/core.mjs +47 -0
  27. package/extensions/spec.ts +457 -0
  28. package/extensions/tool-wishlist/capabilities.json +114 -0
  29. package/extensions/tool-wishlist/core.mjs +1525 -0
  30. package/extensions/tool-wishlist/index.ts +804 -0
  31. package/extensions/tool-wishlist/registry.mjs +99 -0
  32. package/extensions/tool-wishlist/validators.mjs +345 -0
  33. package/extensions/ui-refresh/index.ts +54 -0
  34. package/extensions/workflow-controls/challenge.mjs +196 -0
  35. package/extensions/workflow-controls/experiments.mjs +628 -0
  36. package/extensions/workflow-controls/index.ts +1144 -0
  37. package/extensions/workflow-controls/scope.mjs +272 -0
  38. package/extensions/workflow-controls/smoke.mjs +201 -0
  39. package/package.json +98 -0
  40. package/scripts/check-package.mjs +483 -0
  41. package/scripts/check-pi-package.mjs +223 -0
  42. package/scripts/check-release-order.mjs +97 -0
  43. package/scripts/lib.mjs +182 -0
  44. package/scripts/lock.mjs +122 -0
  45. package/scripts/specpi.mjs +2037 -0
  46. package/scripts/verify-artifact.mjs +21 -0
  47. package/shell/pi-profiles.sh +14 -0
  48. package/site/logo.svg +9 -0
  49. package/site/self-improvement-loop-v2.svg +108 -0
  50. package/skills/donsetch/SKILL.md +76 -0
  51. package/skills/specpi-improve/SKILL.md +54 -0
  52. package/specpi +4 -0
  53. package/specpi.cmd +4 -0
  54. package/templates/AGENTS.md +23 -0
  55. package/templates/settings.json +10 -0
  56. package/themes/specpi-spec.json +96 -0
  57. package/themes/tea-house.json +89 -0
@@ -0,0 +1,47 @@
1
+ const PHASES = new Map([
2
+ ["ready", { index: "00", label: "READY" }],
3
+ ["thinking", { index: "01", label: "INTAKE" }],
4
+ ["reasoning", { index: "02", label: "ANALYZE" }],
5
+ ["synthesizing", { index: "04", label: "SYNTHESIZE" }],
6
+ ]);
7
+
8
+ function safeDetail(value) {
9
+ return String(value ?? "")
10
+ .replace(/[^a-zA-Z0-9_.:-]+/g, "-")
11
+ .replace(/^-+|-+$/g, "")
12
+ .slice(0, 32)
13
+ .toUpperCase();
14
+ }
15
+
16
+ export function describeSpecPhase(phase) {
17
+ const fixed = PHASES.get(phase);
18
+ if (fixed) {
19
+ return { ...fixed, detail: "" };
20
+ }
21
+
22
+ if (phase.startsWith("using ")) {
23
+ return { index: "03", label: "TOOL", detail: safeDetail(phase.slice(6)) || "UNKNOWN" };
24
+ }
25
+
26
+ if (phase.endsWith(" failed")) {
27
+ return { index: "!!", label: "FAULT", detail: safeDetail(phase.slice(0, -7)) || "UNKNOWN" };
28
+ }
29
+
30
+ return { index: "02", label: "ANALYZE", detail: safeDetail(phase) };
31
+ }
32
+
33
+ export function transformSpecMarkdown(markdown, context, enabled) {
34
+ if (!enabled) {
35
+ return markdown;
36
+ }
37
+
38
+ if (context.messageType === "assistant-thinking") {
39
+ return "> **01 / REASONING** · working trace sealed in Spec mode";
40
+ }
41
+
42
+ if (context.messageType === "assistant" && context.isStreaming) {
43
+ return "> **04 / SYNTHESIS** · response held until complete";
44
+ }
45
+
46
+ return markdown;
47
+ }
@@ -0,0 +1,457 @@
1
+ /**
2
+ * Spec - focused execution mode for Pi.
3
+ *
4
+ * Spec mode is deliberately visible and behavioral:
5
+ * - a persistent specification panel and reduced footer replace normal chrome;
6
+ * - live reasoning and answer streams become stable placeholders until completion;
7
+ * - tool output stays collapsed and routine narration is suppressed;
8
+ * - each model turn receives quiet, evidence-led execution guidance;
9
+ * - disabling the mode restores the normal interface and transcript rendering.
10
+ *
11
+ * Toggle with /spec or Ctrl+Alt+Z.
12
+ */
13
+
14
+ import fs from "node:fs";
15
+ import os from "node:os";
16
+ import path from "node:path";
17
+ import { createHash } from "node:crypto";
18
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
19
+ import { Key, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
20
+ import { describeSpecPhase, transformSpecMarkdown } from "./spec/core.mjs";
21
+
22
+ const specPiAgentDir = process.env.PI_CODING_AGENT_DIR || path.join(os.homedir(), ".pi", "agent");
23
+ const specPiStateDir = path.join(specPiAgentDir, "specpi");
24
+ const specPiManagedBin = path.join(specPiStateDir, "bin");
25
+
26
+ function hasValidatedSpecPiTools(): boolean {
27
+ try {
28
+ const manifest = JSON.parse(fs.readFileSync(path.join(specPiStateDir, "manifest.json"), "utf8"));
29
+ const records = manifest.managedOptionalTools || [];
30
+ if (records.length === 0) {
31
+ return false;
32
+ }
33
+
34
+ const expectedNames = new Set<string>();
35
+ for (const record of records) {
36
+ if (path.dirname(record.target) !== specPiManagedBin) {
37
+ return false;
38
+ }
39
+
40
+ const stat = fs.lstatSync(record.target);
41
+ if (!stat.isFile() || stat.isSymbolicLink()) {
42
+ return false;
43
+ }
44
+
45
+ const hash = createHash("sha256").update(fs.readFileSync(record.target)).digest("hex");
46
+ if (hash !== record.installedHash) {
47
+ return false;
48
+ }
49
+
50
+ expectedNames.add(path.basename(record.target));
51
+ }
52
+
53
+ const actualNames = fs.readdirSync(specPiManagedBin).filter((name) => !name.startsWith("."));
54
+
55
+ return actualNames.length === expectedNames.size && actualNames.every((name) => expectedNames.has(name));
56
+ } catch {
57
+ return false;
58
+ }
59
+ }
60
+
61
+ process.env.PATH = (process.env.PATH || "")
62
+ .split(path.delimiter)
63
+ .filter((entry) => path.resolve(entry || ".").toLowerCase() !== path.resolve(specPiManagedBin).toLowerCase())
64
+ .join(path.delimiter);
65
+ if (hasValidatedSpecPiTools()) {
66
+ process.env.PATH = `${specPiManagedBin}${path.delimiter}${process.env.PATH || ""}`;
67
+ }
68
+
69
+ type SpecPhase = "ready" | "thinking" | "reasoning" | "synthesizing" | `using ${string}` | `${string} failed`;
70
+
71
+ const SPEC_STATE_ENTRY = "spec-mode";
72
+ const SPEC_SYSTEM_GUIDANCE = `
73
+
74
+ [SPEC MODE — SPEC EXECUTION]
75
+ Operate as a quiet technical instrument on one objective.
76
+ - Inspect relevant state before changing it.
77
+ - Prefer the smallest coherent change; avoid unrelated additions and cleanup.
78
+ - Use tools without narrating routine progress. Speak mid-task only for a material decision, blocker, or safety boundary.
79
+ - Do not repeat tool output. Convert evidence into conclusions.
80
+ - Ask when ambiguity would materially change the result.
81
+ - Validate with direct evidence before declaring completion.
82
+ - Keep the final response specification-minimal: outcome, evidence, and residual risk only when useful.`;
83
+
84
+ function renderEdge(
85
+ left: string,
86
+ right: string,
87
+ label: string,
88
+ width: number,
89
+ color: (text: string) => string,
90
+ ): string {
91
+ if (width <= 0) {
92
+ return "";
93
+ }
94
+
95
+ if (width === 1) {
96
+ return color("─");
97
+ }
98
+
99
+ const innerWidth = width - visibleWidth(left) - visibleWidth(right);
100
+ const fittedLabel = truncateToWidth(label, Math.max(0, innerWidth), "");
101
+ const fill = "─".repeat(Math.max(0, innerWidth - visibleWidth(fittedLabel)));
102
+
103
+ return truncateToWidth(color(`${left}${fittedLabel}${fill}${right}`), width, "");
104
+ }
105
+
106
+ function splitLine(left: string, right: string, width: number): string {
107
+ const gap = Math.max(1, width - visibleWidth(left) - visibleWidth(right));
108
+
109
+ return truncateToWidth(`${left}${" ".repeat(gap)}${right}`, width, "");
110
+ }
111
+
112
+ function countLabel(value: number, unit: string): string {
113
+ return `${unit}${String(value).padStart(2, "0")}`;
114
+ }
115
+
116
+ export default function (pi: ExtensionAPI) {
117
+ let enabled = false;
118
+ let phase: SpecPhase = "ready";
119
+ let turnCount = 0;
120
+ let toolCount = 0;
121
+ let workflowScope: { active: boolean; pending: number; indeterminate: boolean } = {
122
+ active: false,
123
+ pending: 0,
124
+ indeterminate: false,
125
+ };
126
+ let toolsWereExpanded: boolean | undefined;
127
+ let requestSpecRender: (() => void) | undefined;
128
+
129
+ const updateActivity = (ctx: ExtensionContext) => {
130
+ if (!enabled) {
131
+ return;
132
+ }
133
+
134
+ const theme = ctx.ui.theme;
135
+ const state = describeSpecPhase(phase);
136
+ const detail = state.detail ? ` · ${state.detail}` : "";
137
+ ctx.ui.setStatus(
138
+ "spec-mode",
139
+ `${theme.fg("accent", theme.bold("π SPEC"))}${theme.fg("dim", ` · ${state.index} / ${state.label}${detail}`)}`,
140
+ );
141
+ ctx.ui.setWorkingMessage(`${state.index} / ${state.label}${detail}`);
142
+ requestSpecRender?.();
143
+ };
144
+
145
+ const applyMode = (ctx: ExtensionContext) => {
146
+ if (!enabled) {
147
+ return;
148
+ }
149
+
150
+ if (toolsWereExpanded === undefined) {
151
+ toolsWereExpanded = ctx.ui.getToolsExpanded();
152
+ }
153
+
154
+ ctx.ui.setToolsExpanded(false);
155
+ ctx.ui.setWorkingVisible(false);
156
+ ctx.ui.setWorkingIndicator({ frames: [] });
157
+ ctx.ui.setHiddenThinkingLabel("01 / REASONING · SEALED");
158
+
159
+ ctx.ui.setHeader((_tui, theme) => ({
160
+ invalidate() {},
161
+ render(width: number): string[] {
162
+ if (width < 44) {
163
+ return [
164
+ renderEdge("┌", "┐", "─ π / SPEC ", width, (text) => theme.fg("borderAccent", text)),
165
+ truncateToWidth(theme.fg("dim", "SPECPI · QUIET TECHNICAL EXECUTION"), width, ""),
166
+ renderEdge("└", "┘", "─ 00 ", width, (text) => theme.fg("borderMuted", text)),
167
+ ];
168
+ }
169
+
170
+ const innerWidth = width - 4;
171
+ const row = (left: string, right: string) =>
172
+ `${theme.fg("borderMuted", "│")} ${splitLine(left, right, innerWidth)} ${theme.fg("borderMuted", "│")}`;
173
+
174
+ return [
175
+ renderEdge("┌", "┐", "─ π SPECPI / SPEC EXECUTION ", width, (text) =>
176
+ theme.fg("borderAccent", text),
177
+ ),
178
+ row(theme.fg("text", theme.bold("SYSTEM SPECPI")), theme.fg("dim", "MODE FOCUSED")),
179
+ row(theme.fg("muted", "STREAM HELD UNTIL COMPLETE"), theme.fg("dim", "TOOLS COLLAPSED")),
180
+ renderEdge("└", "┘", "─ HUMAN-DIRECTED · EVIDENCE-LED ", width, (text) =>
181
+ theme.fg("borderMuted", text),
182
+ ),
183
+ ];
184
+ },
185
+ }));
186
+
187
+ ctx.ui.setFooter((tui, theme, footerData) => {
188
+ const unsubscribe = footerData.onBranchChange(() => tui.requestRender());
189
+
190
+ return {
191
+ dispose: unsubscribe,
192
+ invalidate() {},
193
+ render(width: number): string[] {
194
+ const state = describeSpecPhase(phase);
195
+ const detail = state.detail ? ` · ${state.detail}` : "";
196
+ const usage = ctx.getContextUsage();
197
+ const context =
198
+ usage?.percent === null || usage?.percent === undefined
199
+ ? "CTX --"
200
+ : `CTX ${usage.percent.toFixed(0)}%`;
201
+ const branch = footerData.getGitBranch();
202
+ const model = ctx.model?.id || "NO MODEL";
203
+ const left = theme.fg("accent", `${state.index} / ${state.label}${detail}`);
204
+ const right = theme.fg("dim", `${context} · ${model}${branch ? ` · ${branch}` : ""}`);
205
+
206
+ return [splitLine(left, right, width)];
207
+ },
208
+ };
209
+ });
210
+
211
+ ctx.ui.setWidget("spec-mode", (tui, theme) => {
212
+ const renderNow = () => tui.requestRender();
213
+ requestSpecRender = renderNow;
214
+
215
+ return {
216
+ dispose() {
217
+ if (requestSpecRender === renderNow) {
218
+ requestSpecRender = undefined;
219
+ }
220
+ },
221
+ invalidate() {},
222
+ render(width: number): string[] {
223
+ const state = describeSpecPhase(phase);
224
+ const detail = state.detail ? ` · ${state.detail}` : "";
225
+ const phaseText = `${state.index} / ${state.label}${detail}`;
226
+ const run = `${countLabel(turnCount, "T")} · ${countLabel(toolCount, "X")}`;
227
+ const scope = !workflowScope.active
228
+ ? "UNSET"
229
+ : workflowScope.pending > 0 || workflowScope.indeterminate
230
+ ? "REVIEW"
231
+ : "CLEAN";
232
+
233
+ if (width < 44) {
234
+ return [
235
+ renderEdge("┌", "┐", "─ ACTIVE SPEC ", width, (text) => theme.fg("borderAccent", text)),
236
+ truncateToWidth(theme.fg("accent", phaseText), width, ""),
237
+ truncateToWidth(theme.fg("dim", `${run} · SCOPE ${scope} · OUTPUT HELD`), width, ""),
238
+ renderEdge("└", "┘", "─ /spec exits ", width, (text) => theme.fg("borderMuted", text)),
239
+ ];
240
+ }
241
+
242
+ const innerWidth = width - 4;
243
+ const row = (left: string, right: string) =>
244
+ `${theme.fg("borderMuted", "│")} ${splitLine(left, right, innerWidth)} ${theme.fg("borderMuted", "│")}`;
245
+
246
+ return [
247
+ renderEdge("┌", "┐", "─ ACTIVE SPECIFICATION ", width, (text) =>
248
+ theme.fg("borderAccent", text),
249
+ ),
250
+ row(theme.fg("accent", theme.bold(phaseText)), theme.fg("dim", `RUN ${run}`)),
251
+ row(
252
+ theme.fg("muted", "OUTPUT RESPONSE HELD · TOOLS COLLAPSED"),
253
+ theme.fg("dim", `SCOPE ${scope}`),
254
+ ),
255
+ renderEdge("└", "┘", "─ CTRL+ALT+Z / EXIT SPEC MODE ", width, (text) =>
256
+ theme.fg("borderMuted", text),
257
+ ),
258
+ ];
259
+ },
260
+ };
261
+ });
262
+
263
+ updateActivity(ctx);
264
+ };
265
+
266
+ const clearMode = (ctx: ExtensionContext) => {
267
+ requestSpecRender = undefined;
268
+ ctx.ui.setStatus("spec-mode", undefined);
269
+ ctx.ui.setWidget("spec-mode", undefined);
270
+ ctx.ui.setFooter(undefined);
271
+ ctx.ui.setWorkingIndicator();
272
+ ctx.ui.setWorkingMessage();
273
+ ctx.ui.setWorkingVisible(true);
274
+ ctx.ui.setHiddenThinkingLabel();
275
+ ctx.ui.setHeader(undefined);
276
+ if (toolsWereExpanded !== undefined) {
277
+ ctx.ui.setToolsExpanded(toolsWereExpanded);
278
+ }
279
+
280
+ toolsWereExpanded = undefined;
281
+ };
282
+
283
+ const persistMode = () => {
284
+ pi.appendEntry(SPEC_STATE_ENTRY, { enabled, toolsWereExpanded });
285
+ };
286
+
287
+ const setMode = (next: boolean, ctx: ExtensionContext, persist = true) => {
288
+ if (enabled === next) {
289
+ ctx.ui.notify(`Spec mode is already ${enabled ? "on" : "off"}.`, "info");
290
+
291
+ return;
292
+ }
293
+
294
+ enabled = next;
295
+ phase = "ready";
296
+ if (enabled) {
297
+ applyMode(ctx);
298
+ ctx.ui.notify("SPEC MODE / ACTIVE · live response held · tools collapsed", "info");
299
+ } else {
300
+ clearMode(ctx);
301
+ ctx.ui.notify("Spec mode disabled. Previous interface restored.", "info");
302
+ }
303
+
304
+ if (persist) {
305
+ persistMode();
306
+ }
307
+ };
308
+
309
+ const toggleMode = (ctx: ExtensionContext) => setMode(!enabled, ctx);
310
+
311
+ pi.registerMarkdownTransformer((markdown, context) => transformSpecMarkdown(markdown, context, enabled));
312
+
313
+ pi.on("session_start", async (_event, ctx) => {
314
+ enabled = false;
315
+ phase = "ready";
316
+ turnCount = 0;
317
+ toolCount = 0;
318
+ workflowScope = { active: false, pending: 0, indeterminate: false };
319
+ toolsWereExpanded = undefined;
320
+ requestSpecRender = undefined;
321
+
322
+ for (const entry of ctx.sessionManager.getBranch()) {
323
+ if (entry.type === "custom" && entry.customType === SPEC_STATE_ENTRY) {
324
+ const state = entry.data as { enabled?: boolean; toolsWereExpanded?: boolean } | undefined;
325
+ enabled = Boolean(state?.enabled);
326
+ toolsWereExpanded = state?.toolsWereExpanded;
327
+ }
328
+ }
329
+
330
+ if (enabled) {
331
+ applyMode(ctx);
332
+ }
333
+ });
334
+
335
+ pi.events.on("specpi:workflow-status", (state: any) => {
336
+ workflowScope = {
337
+ active: state?.active === true,
338
+ pending: Number.isInteger(state?.pending) ? Math.max(0, state.pending) : 0,
339
+ indeterminate: state?.indeterminate === true,
340
+ };
341
+ requestSpecRender?.();
342
+ });
343
+
344
+ pi.on("session_shutdown", (_event, ctx) => {
345
+ if (enabled) {
346
+ clearMode(ctx);
347
+ } else {
348
+ requestSpecRender = undefined;
349
+ }
350
+ });
351
+
352
+ pi.on("agent_start", async (_event, ctx) => {
353
+ if (!enabled) {
354
+ return;
355
+ }
356
+
357
+ phase = "thinking";
358
+ updateActivity(ctx);
359
+ });
360
+
361
+ pi.on("turn_start", async (_event, ctx) => {
362
+ if (!enabled) {
363
+ return;
364
+ }
365
+
366
+ turnCount += 1;
367
+ phase = "reasoning";
368
+ updateActivity(ctx);
369
+ });
370
+
371
+ pi.on("tool_execution_start", async (event, ctx) => {
372
+ if (!enabled) {
373
+ return;
374
+ }
375
+
376
+ toolCount += 1;
377
+ ctx.ui.setToolsExpanded(false);
378
+ phase = `using ${event.toolName}`;
379
+ updateActivity(ctx);
380
+ });
381
+
382
+ pi.on("tool_execution_end", async (event, ctx) => {
383
+ if (!enabled) {
384
+ return;
385
+ }
386
+
387
+ phase = event.isError ? `${event.toolName} failed` : "synthesizing";
388
+ updateActivity(ctx);
389
+ });
390
+
391
+ pi.on("agent_settled", async (_event, ctx) => {
392
+ if (!enabled) {
393
+ return;
394
+ }
395
+
396
+ phase = "ready";
397
+ updateActivity(ctx);
398
+ });
399
+
400
+ pi.on("before_agent_start", async (event) => {
401
+ if (!enabled || event.systemPrompt.includes("[SPEC MODE — SPEC EXECUTION]")) {
402
+ return;
403
+ }
404
+
405
+ return { systemPrompt: `${event.systemPrompt}${SPEC_SYSTEM_GUIDANCE}` };
406
+ });
407
+
408
+ pi.registerCommand("spec", {
409
+ description: "Toggle immersive spec execution (held live stream, collapsed tools, reduced chrome)",
410
+ getArgumentCompletions: (prefix: string) => {
411
+ const options = ["on", "off", "status"];
412
+ const matches = options.filter((option) => option.startsWith(prefix.trim().toLowerCase()));
413
+
414
+ return matches.length > 0 ? matches.map((value) => ({ value, label: value })) : null;
415
+ },
416
+ handler: async (args, ctx) => {
417
+ const action = args.trim().toLowerCase();
418
+ if (!action) {
419
+ toggleMode(ctx);
420
+
421
+ return;
422
+ }
423
+
424
+ if (action === "on") {
425
+ setMode(true, ctx);
426
+
427
+ return;
428
+ }
429
+
430
+ if (action === "off") {
431
+ setMode(false, ctx);
432
+
433
+ return;
434
+ }
435
+
436
+ if (action === "status") {
437
+ const state = describeSpecPhase(phase);
438
+ const detail = state.detail ? ` · ${state.detail}` : "";
439
+ ctx.ui.notify(
440
+ enabled
441
+ ? `SPEC MODE / ACTIVE · ${state.index} / ${state.label}${detail} · ${countLabel(turnCount, "T")} · ${countLabel(toolCount, "X")}`
442
+ : "Spec mode is off.",
443
+ "info",
444
+ );
445
+
446
+ return;
447
+ }
448
+
449
+ ctx.ui.notify("Usage: /spec [on|off|status]", "error");
450
+ },
451
+ });
452
+
453
+ pi.registerShortcut(Key.ctrlAlt("z"), {
454
+ description: "Toggle Spec focused execution mode",
455
+ handler: async (ctx) => toggleMode(ctx),
456
+ });
457
+ }
@@ -0,0 +1,114 @@
1
+ {
2
+ "schema": 1,
3
+ "capabilities": [
4
+ {
5
+ "id": "local-browser-automation",
6
+ "title": "Local browser automation and visual regression",
7
+ "aliases": [
8
+ "browser-automation",
9
+ "browser-automation-visual-regression",
10
+ "browser-visual-regression-testing",
11
+ "local-browser-visual-regression",
12
+ "local-browser-visual-regression-testing"
13
+ ],
14
+ "shippedVersion": "0.1.0",
15
+ "shippedAt": "2026-08-28T00:00:00.000Z",
16
+ "validations": [
17
+ "browser-runtime-smoke"
18
+ ]
19
+ },
20
+ {
21
+ "id": "durable-retirement-proof",
22
+ "title": "Durable re-runnable retirement proofs",
23
+ "aliases": [],
24
+ "shippedVersion": "0.7.0",
25
+ "shippedAt": "2026-08-29T00:00:00.000Z",
26
+ "validations": [
27
+ "wishlist-state-smoke"
28
+ ]
29
+ },
30
+ {
31
+ "id": "improvement-journal",
32
+ "title": "Improvement journal with evidence history",
33
+ "aliases": [],
34
+ "shippedVersion": "0.7.0",
35
+ "shippedAt": "2026-08-29T00:00:00.000Z",
36
+ "validations": [
37
+ "wishlist-state-smoke"
38
+ ]
39
+ },
40
+ {
41
+ "id": "loop-health-metric",
42
+ "title": "Loop health metrics",
43
+ "aliases": [],
44
+ "shippedVersion": "0.7.0",
45
+ "shippedAt": "2026-08-29T00:00:00.000Z",
46
+ "validations": [
47
+ "wishlist-state-smoke"
48
+ ]
49
+ },
50
+ {
51
+ "id": "context-rich-reopen",
52
+ "title": "Context-rich reopens for retired gaps",
53
+ "aliases": [],
54
+ "shippedVersion": "0.7.0",
55
+ "shippedAt": "2026-08-29T00:00:00.000Z",
56
+ "validations": [
57
+ "wishlist-state-smoke"
58
+ ]
59
+ },
60
+ {
61
+ "id": "command-guard",
62
+ "title": "Cross-platform session command guard",
63
+ "aliases": [
64
+ "command-protection",
65
+ "dangerou-command-protection",
66
+ "session-command-safety"
67
+ ],
68
+ "shippedVersion": "0.8.0",
69
+ "shippedAt": "2026-08-31T00:00:00.000Z",
70
+ "validations": [
71
+ "command-guard-smoke"
72
+ ]
73
+ },
74
+ {
75
+ "id": "scope-drift-monitor",
76
+ "title": "Session scope drift monitor",
77
+ "aliases": [
78
+ "scope-monitor",
79
+ "scope-drift-monitoring"
80
+ ],
81
+ "shippedVersion": "0.8.4",
82
+ "shippedAt": "2026-09-01T00:00:00.000Z",
83
+ "validations": [
84
+ "scope-drift-monitor-smoke"
85
+ ]
86
+ },
87
+ {
88
+ "id": "guided-experiment-worktree",
89
+ "title": "Guided detached experiment worktrees",
90
+ "aliases": [
91
+ "experiment-worktree",
92
+ "isolated-experiment-worktree"
93
+ ],
94
+ "shippedVersion": "0.8.4",
95
+ "shippedAt": "2026-09-01T00:00:00.000Z",
96
+ "validations": [
97
+ "guided-experiment-worktrees-smoke"
98
+ ]
99
+ },
100
+ {
101
+ "id": "completion-challenge",
102
+ "title": "Structured completion challenge",
103
+ "aliases": [
104
+ "completion-review",
105
+ "completion-readiness-challenge"
106
+ ],
107
+ "shippedVersion": "0.8.4",
108
+ "shippedAt": "2026-09-01T00:00:00.000Z",
109
+ "validations": [
110
+ "completion-challenge-smoke"
111
+ ]
112
+ }
113
+ ]
114
+ }