pi-gauntlet 8.2.1 → 8.4.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/CHANGELOG.md +8 -0
- package/README.md +5 -5
- package/agents/loop-implementer.md +19 -4
- package/bin/gauntlet-performance.mjs +477 -64
- package/bin/gauntlet-performance.test.mjs +27 -27
- package/bin/gauntlet-spec-index.mjs +290 -47
- package/bin/gauntlet-spec-index.test.mjs +42 -18
- package/bin/gauntlet-telemetry-seal.mjs +220 -77
- package/bin/gauntlet-telemetry-seal.test.mjs +222 -457
- package/extensions/lib/gauntlet-settings.test.ts +12 -14
- package/extensions/lib/gauntlet-settings.ts +7 -16
- package/extensions/lib/plan-check.test.ts +10 -0
- package/extensions/lib/plan-check.ts +2 -2
- package/extensions/lib/telemetry-branch.test.ts +102 -0
- package/extensions/lib/telemetry-branch.ts +145 -0
- package/extensions/lib/telemetry-diff.test.ts +2 -2
- package/extensions/lib/telemetry-diff.ts +1 -1
- package/extensions/lib/telemetry-leg.ts +163 -0
- package/extensions/lib/telemetry-record.ts +2 -0
- package/extensions/lib/vcs.ts +6 -23
- package/extensions/telemetry-leg-failures.test.ts +138 -0
- package/extensions/telemetry-leg-fields.test.ts +130 -0
- package/extensions/telemetry-leg-no-write.test.ts +90 -0
- package/extensions/telemetry-leg-rounds.test.ts +157 -0
- package/extensions/telemetry-leg-trackers.test.ts +114 -0
- package/extensions/telemetry-leg.test.ts +151 -0
- package/extensions/telemetry-settlement-contract.test.ts +67 -0
- package/extensions/telemetry.test.ts +44 -27
- package/extensions/telemetry.ts +68 -48
- package/extensions/test-support/git-env.mjs +13 -0
- package/package.json +1 -1
- package/skills/brainstorming/SKILL.md +3 -1
- package/skills/check-delivery/SKILL.md +2 -0
- package/skills/finishing-a-development-branch/SKILL.md +13 -13
- package/skills/finishing-a-development-branch/reference/verification-set.md +2 -2
- package/skills/gatekeep-pr/reference/batch.md +3 -1
- package/skills/gatekeep-pr/reference/cleanup.md +1 -1
- package/skills/gatekeep-pr/reference/post-selection-loop.md +7 -1
- package/skills/gauntlet-resume/reference/reconstruction.md +6 -6
- package/skills/gauntlet-resume/reference/seed.md +1 -6
- package/skills/loop-implementation/SKILL.md +40 -21
- package/skills/loop-implementation/reference/action-spend.md +10 -0
- package/skills/loop-implementation/reference/coordinated-legs.md +5 -5
- package/skills/loop-implementation/reference/interrupted-recovery.md +10 -0
- package/skills/loop-implementation/reference/unreachable-returns.md +13 -0
- package/skills/shape-ticket/SKILL.md +1 -1
- package/skills/shape-ticket/reference/repo-audit.md +2 -2
- package/skills/verification-before-completion/reference/conformance-check.md +1 -1
package/extensions/telemetry.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Telemetry extension: records
|
|
3
|
-
*
|
|
2
|
+
* Telemetry extension: records run and session-leg YAML from pi events alone.
|
|
3
|
+
* Session legs seal through telemetry-leg.ts at terminal settlement. Runs bind
|
|
4
|
+
* only after `phase_tracker start brainstorm` armed the
|
|
4
5
|
* flow, writes the record to disk without ever staging or committing it (the seal bin
|
|
5
6
|
* commits once at finish), and blocks `write` into an already-shipped spec while
|
|
6
7
|
* brainstorm is in progress. Pure helpers: extensions/lib/telemetry-*.ts. The default
|
|
@@ -14,7 +15,7 @@
|
|
|
14
15
|
import * as piRuntime from "@earendil-works/pi-coding-agent";
|
|
15
16
|
import { SettingsManager, getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
16
17
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
17
|
-
import { DEFAULT_TEST_COMMANDS, planDirsFor, resolveFlowGuards, resolveTelemetry, settingsErrorWarning, type TelemetryResolved } from "./lib/gauntlet-settings.ts";
|
|
18
|
+
import { DEFAULT_TELEMETRY_DIR, DEFAULT_TEST_COMMANDS, planDirsFor, resolveFlowGuards, resolveTelemetry, settingsErrorWarning, type TelemetryResolved } from "./lib/gauntlet-settings.ts";
|
|
18
19
|
import { loadGauntletSettings } from "./lib/gauntlet-settings-loader.ts";
|
|
19
20
|
import { checkoutOf } from "./lib/checkout.ts";
|
|
20
21
|
import { CONTEXT_DRAFT_MARKER, nextGauntletEntered } from "./lib/phase-tracker-helpers.ts";
|
|
@@ -24,6 +25,8 @@
|
|
|
24
25
|
import { COUNTED_USER_PHASES, REVIEWER_AGENTS, countFindings, countStructuredFindings, countOpenGaps, countReopens, insertedText, planTotals, textOf, usageToTokens } from "./lib/telemetry-collect.ts";
|
|
25
26
|
import { addTokens, capEvents, compact, currentPhase, derive, diffPhases, emptyAccumulators, emptyPhases, foldAccumulators, implementAutoCompletes, liveShipEvent, newRecord, parseRecord, serializeRecord, type Accumulators, type BaseEvent, type PhaseAcc, type PhaseKey, type PhaseMap, type TelemetryEvent, type TelemetryRecord, type Tokens } from "./lib/telemetry-record.ts";
|
|
26
27
|
import { guardReason } from "./lib/telemetry-ship.ts";
|
|
28
|
+
import { readTelemetryBranch, telemetryRepo, type syncTelemetryBranch } from "./lib/telemetry-branch.ts";
|
|
29
|
+
import { registerLegTelemetry } from "./lib/telemetry-leg.ts";
|
|
27
30
|
import { buildCouncil, parseAudit, parseChairReport, resolveMembers, type Cluster, type MemberResult } from "./lib/telemetry-council.ts";
|
|
28
31
|
|
|
29
32
|
// ---- deps seam -------------------------------------------------------------------
|
|
@@ -51,6 +54,9 @@
|
|
|
51
54
|
testCommands?: string[];
|
|
52
55
|
}
|
|
53
56
|
export interface Deps {
|
|
57
|
+
syncTelemetryBranch?: typeof syncTelemetryBranch;
|
|
58
|
+
telemetryRepo?: typeof telemetryRepo;
|
|
59
|
+
readTelemetryBranch?: typeof readTelemetryBranch;
|
|
54
60
|
fs: FsPort;
|
|
55
61
|
git: (args: string[], cwd: string) => Promise<GitResult>;
|
|
56
62
|
// jj runner; production uses realJj for both checkout detection and the ship-time diff.
|
|
@@ -205,13 +211,16 @@
|
|
|
205
211
|
let boundSpec: string | undefined; // repo-relative
|
|
206
212
|
let record: TelemetryRecord | undefined;
|
|
207
213
|
const predecessorLinks = new Set<string>();
|
|
208
|
-
let recordRel: string | undefined; //
|
|
214
|
+
let recordRel: string | undefined; // absolute record path outside the working tree
|
|
209
215
|
let buffer: TelemetryEvent[] = [];
|
|
210
216
|
let pending: Accumulators = emptyAccumulators();
|
|
211
217
|
let frozen = false;
|
|
218
|
+
let recordWritten = false;
|
|
212
219
|
let gauntletEntered = false;
|
|
213
220
|
let settingsWarned = false;
|
|
214
221
|
let sessionId = "";
|
|
222
|
+
const guardCache = new Map<string, Map<string, string>>();
|
|
223
|
+
let guardWarned = false;
|
|
215
224
|
let currentDir = "";
|
|
216
225
|
let councilPending: { memberResults: MemberResult[]; chair?: { model: string; dispatches: number; clusters?: Cluster[] } } | undefined;
|
|
217
226
|
|
|
@@ -235,7 +244,7 @@
|
|
|
235
244
|
// Disarmed and unbound: activity outside the gauntlet is never charged to the next run.
|
|
236
245
|
const live = (): Accumulators => record
|
|
237
246
|
? (record.accumulators[sessionId] ??= emptyAccumulators())
|
|
238
|
-
: gauntletEntered ? pending : emptyAccumulators();
|
|
247
|
+
: legAcc() ?? (gauntletEntered ? pending : emptyAccumulators());
|
|
239
248
|
|
|
240
249
|
const canBind = (): boolean => gauntletEntered && !frozen && phases.ship.status !== "complete";
|
|
241
250
|
|
|
@@ -252,8 +261,8 @@
|
|
|
252
261
|
const warn = (message: string) => pushEvent({ kind: "warning", message });
|
|
253
262
|
|
|
254
263
|
// ---- record store ----
|
|
255
|
-
// The
|
|
256
|
-
//
|
|
264
|
+
// The seal either keeps a stamped file for retry or removes it after committing.
|
|
265
|
+
// Neither may be overwritten by a later recorder flush.
|
|
257
266
|
const sealedAt = (path: string): boolean => {
|
|
258
267
|
const text = deps.fs.readFile(path);
|
|
259
268
|
const parsed = text ? parseRecord(text) : undefined;
|
|
@@ -266,8 +275,8 @@
|
|
|
266
275
|
frozen = true;
|
|
267
276
|
return;
|
|
268
277
|
}
|
|
269
|
-
const target =
|
|
270
|
-
if (sealedAt(target)) {
|
|
278
|
+
const target = recordRel;
|
|
279
|
+
if ((recordWritten && !deps.fs.exists(target)) || sealedAt(target)) {
|
|
271
280
|
frozen = true;
|
|
272
281
|
return;
|
|
273
282
|
}
|
|
@@ -281,6 +290,7 @@
|
|
|
281
290
|
deps.fs.mkdirp(dirname(target));
|
|
282
291
|
deps.fs.writeFile(target + ".tmp", serializeRecord(record));
|
|
283
292
|
deps.fs.rename(target + ".tmp", target);
|
|
293
|
+
recordWritten = true;
|
|
284
294
|
} catch (e) {
|
|
285
295
|
pushEvent({ kind: "warning", message: `record write failed: ${String((e as Error).message ?? e).split("\n")[0]}` });
|
|
286
296
|
}
|
|
@@ -288,27 +298,9 @@
|
|
|
288
298
|
|
|
289
299
|
const flush = writeRecord;
|
|
290
300
|
|
|
291
|
-
// Excluded untracked paths are invisible to `git status --porcelain`, skipped by
|
|
292
|
-
// `git add -A`, and never block `git worktree remove`; the seal stages with `git add -f`.
|
|
293
|
-
const ensureExcluded = async () => {
|
|
294
|
-
const common = await deps.git(["rev-parse", "--path-format=absolute", "--git-common-dir"], toplevel!);
|
|
295
|
-
const commonDir = common.stdout.trim();
|
|
296
|
-
if (common.code !== 0 || !commonDir) return void warn(`exclude skipped: git rev-parse --git-common-dir failed: ${common.stderr.trim().split("\n")[0]}`);
|
|
297
|
-
const line = `${currentDir.replace(/\/+$/, "")}/`;
|
|
298
|
-
const file = join(commonDir, "info", "exclude");
|
|
299
|
-
const existing = deps.fs.readFile(file) ?? "";
|
|
300
|
-
if (existing.split("\n").some((l) => l.trim() === line)) return;
|
|
301
|
-
try {
|
|
302
|
-
deps.fs.mkdirp(dirname(file));
|
|
303
|
-
deps.fs.writeFile(file, existing + (existing && !existing.endsWith("\n") ? "\n" : "") + line + "\n");
|
|
304
|
-
} catch (e) {
|
|
305
|
-
warn(`exclude write failed: ${String((e as Error).message ?? e).split("\n")[0]}`);
|
|
306
|
-
}
|
|
307
|
-
};
|
|
308
|
-
|
|
309
301
|
const loadOrCreate = (specRel: string): TelemetryRecord => {
|
|
310
302
|
const rel = recordPathFor(currentDir, specRel);
|
|
311
|
-
const existing = deps.fs.readFile(
|
|
303
|
+
const existing = deps.fs.readFile(rel);
|
|
312
304
|
const parsed = existing ? parseRecord(existing) : undefined;
|
|
313
305
|
if (parsed) {
|
|
314
306
|
// fold stale live blocks (sessions that died without shutdown) into total
|
|
@@ -324,13 +316,40 @@
|
|
|
324
316
|
return newRecord({ spec: specRel, session: sessionId, now: buffer[0]?.ts ?? deps.now(), runId: randomUUID() });
|
|
325
317
|
};
|
|
326
318
|
|
|
327
|
-
const
|
|
319
|
+
const shippedRecords = (top: string, snap: SettingsSnapshot): Map<string, string> => {
|
|
320
|
+
const key = JSON.stringify([top, snap.telemetry.remote]);
|
|
321
|
+
let records = guardCache.get(key);
|
|
322
|
+
if (!records) {
|
|
323
|
+
records = new Map();
|
|
324
|
+
guardCache.set(key, records);
|
|
325
|
+
const repo = (deps.telemetryRepo ?? telemetryRepo)(top);
|
|
326
|
+
const result = (deps.readTelemetryBranch ?? readTelemetryBranch)(top, { remote: snap.telemetry.remote, prefix: `${repo}/brainstorming/` });
|
|
327
|
+
for (const entry of result.records) {
|
|
328
|
+
const parsed = parseRecord(entry.bytes);
|
|
329
|
+
if (parsed?.shipped_at) records.set(parsed.spec, parsed.shipped_at);
|
|
330
|
+
}
|
|
331
|
+
if (result.warning && !guardWarned) {
|
|
332
|
+
guardWarned = true;
|
|
333
|
+
pi.sendMessage({ customType: "telemetry-warning", content: `telemetry guard unavailable: ${result.warning}`, display: true });
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return records;
|
|
337
|
+
};
|
|
328
338
|
|
|
329
339
|
const bind = async (specRel: string, snap: SettingsSnapshot, specToplevel: string, via: "git" | "jj") => {
|
|
330
|
-
if (
|
|
340
|
+
if (via === "git" && shippedRecords(specToplevel, snap).has(specRel)) return;
|
|
341
|
+
let metadataDir = join(specToplevel, ".jj");
|
|
342
|
+
if (via === "git") {
|
|
343
|
+
const common = await deps.git(["rev-parse", "--path-format=absolute", "--git-common-dir"], specToplevel);
|
|
344
|
+
if (common.code !== 0 || !common.stdout.trim()) {
|
|
345
|
+
warn(`record bind skipped: git common dir unavailable: ${common.stderr.trim().split("\n")[0]}`);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
metadataDir = common.stdout.trim();
|
|
349
|
+
}
|
|
331
350
|
toplevel = specToplevel;
|
|
332
351
|
checkoutVia = via;
|
|
333
|
-
currentDir =
|
|
352
|
+
currentDir = join(metadataDir, "gauntlet", "telemetry");
|
|
334
353
|
boundSpec = specRel;
|
|
335
354
|
recordRel = recordPathFor(currentDir, specRel);
|
|
336
355
|
predecessorLinks.clear();
|
|
@@ -363,7 +382,6 @@
|
|
|
363
382
|
if (snap.errors.length) warn(settingsErrorWarning(snap.errors));
|
|
364
383
|
if (snap.telemetry.warning) warn(snap.telemetry.warning);
|
|
365
384
|
}
|
|
366
|
-
if (via === "git") await ensureExcluded();
|
|
367
385
|
refreshLinks();
|
|
368
386
|
};
|
|
369
387
|
|
|
@@ -374,6 +392,7 @@
|
|
|
374
392
|
record = undefined;
|
|
375
393
|
recordRel = undefined;
|
|
376
394
|
frozen = false;
|
|
395
|
+
recordWritten = false;
|
|
377
396
|
councilPending = undefined;
|
|
378
397
|
};
|
|
379
398
|
|
|
@@ -400,18 +419,13 @@
|
|
|
400
419
|
const rebind = async (newSpec: string): Promise<boolean> => {
|
|
401
420
|
const from = recordRel!;
|
|
402
421
|
const to = recordPathFor(currentDir, newSpec);
|
|
403
|
-
if (deps.fs.exists(
|
|
422
|
+
if (deps.fs.exists(to)) {
|
|
404
423
|
warn(`record move skipped: ${to} already exists`);
|
|
405
424
|
return false;
|
|
406
425
|
}
|
|
407
|
-
const trackedResult = await deps.git(["ls-files", "--error-unmatch", "--", from], toplevel!);
|
|
408
|
-
const tracked = trackedResult.code === 0 && trackedResult.stdout.trim() !== "";
|
|
409
426
|
try {
|
|
410
|
-
deps.fs.mkdirp(dirname(
|
|
411
|
-
|
|
412
|
-
const mv = await deps.git(["mv", "--", from, to], toplevel!);
|
|
413
|
-
if (mv.code !== 0) deps.fs.rename(abs(from), abs(to));
|
|
414
|
-
} else deps.fs.rename(abs(from), abs(to));
|
|
427
|
+
deps.fs.mkdirp(dirname(to));
|
|
428
|
+
deps.fs.rename(from, to);
|
|
415
429
|
} catch (e) {
|
|
416
430
|
warn(`record move failed: ${String((e as Error).message ?? e).split("\n")[0]}`);
|
|
417
431
|
return false;
|
|
@@ -493,8 +507,11 @@
|
|
|
493
507
|
lastPlanTasks = undefined;
|
|
494
508
|
amendmentOpen = false;
|
|
495
509
|
pendingTest.clear();
|
|
510
|
+
if (sessionId !== ctx.sessionManager.getSessionId()) {
|
|
511
|
+
guardCache.clear();
|
|
512
|
+
guardWarned = false;
|
|
513
|
+
}
|
|
496
514
|
sessionId = ctx.sessionManager.getSessionId();
|
|
497
|
-
currentDir = snap.telemetry.dir;
|
|
498
515
|
const replay = replayBranch(ctx.sessionManager.getBranch(), snap.specDirs);
|
|
499
516
|
lastPlanTasks = replay.lastPlanTasks;
|
|
500
517
|
phases = replay.phases;
|
|
@@ -535,6 +552,10 @@
|
|
|
535
552
|
await applyPhaseDetails(event.details as never, snap);
|
|
536
553
|
return undefined;
|
|
537
554
|
}
|
|
555
|
+
if (event.toolName === "subagent" && legAcc()) {
|
|
556
|
+
await onSubagentResult(event.details, event.content, event.isError);
|
|
557
|
+
return undefined;
|
|
558
|
+
}
|
|
538
559
|
if (!record && !canBind()) return undefined;
|
|
539
560
|
if (event.toolName === "plan_check" && !event.isError) {
|
|
540
561
|
const d = event.details as { status?: string; specPath?: string; planPath?: string } | undefined;
|
|
@@ -605,7 +626,7 @@
|
|
|
605
626
|
}
|
|
606
627
|
if (loc.toplevel !== toplevel) {
|
|
607
628
|
// Another checkout: a fresh run, never a rename of this record.
|
|
608
|
-
if ((tool === "write" || tool === "edit") && isSpecPath(rel, snap.specDirs) && canBind() && !
|
|
629
|
+
if ((tool === "write" || tool === "edit") && isSpecPath(rel, snap.specDirs) && canBind() && (loc.via === "jj" || !shippedRecords(loc.toplevel, snap).has(rel))) {
|
|
609
630
|
unbind();
|
|
610
631
|
await bind(rel, snap, loc.toplevel, loc.via);
|
|
611
632
|
if (!boundSpec) return undefined;
|
|
@@ -751,7 +772,7 @@
|
|
|
751
772
|
if (!record || !toplevel || liveShipEvent(record.events)) return;
|
|
752
773
|
record.status = "shipped";
|
|
753
774
|
record.shipped_at = deps.now();
|
|
754
|
-
const out = await computeJjDiff({ jj: deps.jj ?? realJj, cwd: toplevel, spec: record.spec, dir:
|
|
775
|
+
const out = await computeJjDiff({ jj: deps.jj ?? realJj, cwd: toplevel, spec: record.spec, dir: DEFAULT_TELEMETRY_DIR, buckets: snap.telemetry.buckets, planDirs: snap.planDirs });
|
|
755
776
|
if (out.warning) warn(out.warning);
|
|
756
777
|
record.derived.modified_files = out.modified_files;
|
|
757
778
|
record.derived.diff = out.diff;
|
|
@@ -766,11 +787,9 @@
|
|
|
766
787
|
const rawPath = (event.input as { path?: unknown }).path;
|
|
767
788
|
if (typeof rawPath === "string" && phases.brainstorm.status === "in_progress") {
|
|
768
789
|
const loc = await locate(ctx, rawPath);
|
|
769
|
-
if (loc && isSpecPath(loc.rel, snap.specDirs)) {
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
const parsed = existing ? parseRecord(existing) : undefined;
|
|
773
|
-
if (parsed?.shipped_at) return { block: true, reason: guardReason(loc.rel, parsed.shipped_at, recRel) };
|
|
790
|
+
if (loc?.via === "git" && isSpecPath(loc.rel, snap.specDirs)) {
|
|
791
|
+
const shippedAt = shippedRecords(loc.toplevel, snap).get(loc.rel);
|
|
792
|
+
if (shippedAt) return { block: true, reason: guardReason(loc.rel, shippedAt, "gauntlet/telemetry") };
|
|
774
793
|
}
|
|
775
794
|
}
|
|
776
795
|
return undefined;
|
|
@@ -874,4 +893,5 @@
|
|
|
874
893
|
refreshLinks();
|
|
875
894
|
};
|
|
876
895
|
|
|
896
|
+
const legAcc = registerLegTelemetry(pi, deps, () => !!record);
|
|
877
897
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
export function isolatedGitEnv(scratch) {
|
|
5
|
+
const env = {
|
|
6
|
+
GIT_CONFIG_GLOBAL: join(scratch, "gitconfig"),
|
|
7
|
+
GIT_CONFIG_NOSYSTEM: "1",
|
|
8
|
+
GIT_AUTHOR_NAME: "test", GIT_AUTHOR_EMAIL: "test@example.test",
|
|
9
|
+
GIT_COMMITTER_NAME: "test", GIT_COMMITTER_EMAIL: "test@example.test",
|
|
10
|
+
};
|
|
11
|
+
writeFileSync(env.GIT_CONFIG_GLOBAL, "");
|
|
12
|
+
return env;
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -69,7 +69,7 @@ The spec is the first commit in a dedicated worktree, never a separate commit on
|
|
|
69
69
|
|
|
70
70
|
Read [reference/legs.md](reference/legs.md) and settle membership before creation. Invoke `/skill:using-git-worktrees` for each member with explicit `$ROOT` = its primary and `$BRANCH` = the shared branch; carry every `Worktree ready at <full-path>` value, home first. Root the home spec at `<home worktree>/<home spec dir>/<filename>.md`; root each dispatch and `git -C` in its owning leg's worktree. Keep the process cwd unchanged. Gather each leg's rules and record portable contracts and companion artifacts using that reference; a one-member run adds only its one-block `## Legs` section.
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
Keep spec, plan, and implementation in this worktree. Ship the spec and implementation; strip only the plan. Let `/skill:finishing-a-development-branch` seal and publish the telemetry record to the orphan `gauntlet/telemetry` branch before landing; keep recorder state in Git common-dir metadata, outside the working tree. In a plain jj workspace, retain local metadata and report that publication is unavailable. Use no worktree for explicit trivial one-off edits outside this flow.
|
|
73
73
|
|
|
74
74
|
## The Process
|
|
75
75
|
|
|
@@ -155,6 +155,8 @@ Start the message with `gauntlet: question` alone at column zero on the first ph
|
|
|
155
155
|
|
|
156
156
|
Use two rounds, targeting 300-500 words each, with one approval each; revisions remain within that approval point. Ask once per round; round-1 approval without correction confirms the predecessor. Round 1 covers architecture, responsibilities, data flow, and `supersedes <path>, <scope>` when applicable. Round 2 covers errors, edges, tests, and `## Documentation impact`. In round 2, read and apply [Documentation impact](reference/documentation-impact.md), including its verbatim template; cite that portable relative path in the spec; do not restate its categories. Ask the user when doc impact is unclear. Unreadable -> stop the step with a blocking error. Cite the draft's `Docs touched:` entries as candidates for "Materially amended existing docs"; admit or drop each by that owner's materiality bar, never list them automatically. In round 1, carry chosen `Removes:`/`Adds:` into Design as exhaustive `Remove:`/`Add:` imperatives naming every moving part; route unlisted parts through `reference/amendment-surface.md`. In round 2, write `## Tests`: one `<observable> - <test name or file>` per acceptance row and Design edge/error clause; `<observable> - none: <why>` for untestable.
|
|
157
157
|
|
|
158
|
+
In round 2, name every paid or external action needed for implementation or verification in Design with its runner, inputs and count for the whole flow across seed, rows, repairs and finish. Treat design approval as authorization within those bounds without per-step permission prompts; state that the loop treats an action without such a clause as unreachable, and require separate explicit authorization to add an unnamed action to Design.
|
|
159
|
+
|
|
158
160
|
## Ticket Handling
|
|
159
161
|
|
|
160
162
|
Read [Ticket Handling](reference/ticket-acceptance.md) at first use - before gathering context, even with no ticket. Steps 4 and 8 carry the same read behind an in-context check so a restored session (`gauntlet-resume` routes there) loads it once; a same-session redraw or a second brainstorm in one session reuses the content already in context. Unreadable -> stop with a blocking error, never reconstruct from memory.
|
|
@@ -262,6 +262,8 @@ Start the message with `gauntlet: gate=check-delivery/confirmation-gate` before
|
|
|
262
262
|
|
|
263
263
|
Start the message with `gauntlet: done delivered` when the batched write landed. Start the message with `gauntlet: done already-recorded` for idempotent delivery. Start the message with `gauntlet: done manual` when the write halves are emitted for manual execution. Start the message with `gauntlet: stuck` followed on that line by the concrete reason once the findings-comment gate is answered or no comment is offered; use this for any partial write failure too. A declined success write ends with `gauntlet: stuck write declined`.
|
|
264
264
|
|
|
265
|
+
After every selected write or follow-on step finishes, put one plain `<ref>: <outcome>` line below the terminal marker, including singleton and early STOP paths. Use the supplied ticket ref and exactly one outcome: `delivered`, `manual`, `declined`, `already recorded`, `skipped`, or `failed - <reason>`. Map a declined success write to `declined`, skipped-no-tracker to `skipped`, and a failed stage, findings-only path or partial write to `failed - <reason>`; report a terminal ticket without recorded delivery as `skipped`. Keep rows outside tracker payloads, without bullets, code fences or emphasis. In set mode use `reference/batch.md`'s one final roster settlement, never a per-ticket terminal marker while siblings continue.
|
|
266
|
+
|
|
265
267
|
Any `unexplained gap` or failed stage -> **failure path**: no status
|
|
266
268
|
advance, no delivery comment, no marker. The findings may be offered as a
|
|
267
269
|
**findings comment** (no `Delivered:` line, so it never trips idempotency) -
|
|
@@ -226,7 +226,7 @@ Rows 1-2 run the Option 1/2 blocks; row 3 runs the Keep block and row 4 the Disc
|
|
|
226
226
|
|
|
227
227
|
#### Strip the plan, seal the record (Options 1-3)
|
|
228
228
|
|
|
229
|
-
Run this on the feature branch before any landing path.
|
|
229
|
+
Run this on the feature branch before any landing path. Ship the spec and implementation; strip only the plan. Let the seal publish the telemetry record to the orphan `gauntlet/telemetry` branch without changing the product checkout, index or HEAD. Keep in-progress records in Git common-dir metadata, outside the working tree. Resolve `<bin>` as `<directory of this skill's SKILL.md>/../../bin` from the skill's `<location>` in the system prompt.
|
|
230
230
|
|
|
231
231
|
Read the existing artifact's path-only `**Spec:**` header (repo-relative, backticks stripped) before removal; pass that value as `--spec`, including for a line-1 ledger. Resolve the spec dirs with `gauntlet_setting({ key: "flowGuards" })` and use their sibling `plans` dirs for plan paths. Enumerate both PLAN_PATH candidates: the task plan paired by basename and `<sibling plans dir>/<spec stem>-ledger.md`. Preserve the spec and telemetry.
|
|
232
232
|
|
|
@@ -242,13 +242,13 @@ elif [ -f "$WORKTREE/$PLAN_PATH" ] && jj -R "$WORKTREE" root >/dev/null 2>&1; th
|
|
|
242
242
|
fi
|
|
243
243
|
done
|
|
244
244
|
|
|
245
|
-
# Seal the telemetry record: stamp shipped, compute the diff,
|
|
246
|
-
# The bin
|
|
245
|
+
# Seal the telemetry record: stamp shipped, compute the diff, publish to gauntlet/telemetry.
|
|
246
|
+
# The bin reports publication unavailable in a plain jj workspace and keeps local metadata.
|
|
247
247
|
# --option is pr for Options 1-2 and squash for Option 3; --spec is the artifact header's **Spec:** value (omit when no artifact is known).
|
|
248
248
|
node <bin>/gauntlet-telemetry-seal.mjs --worktree "$WORKTREE" --option <pr|squash> --base <base-branch> --spec <spec path>
|
|
249
249
|
```
|
|
250
250
|
|
|
251
|
-
|
|
251
|
+
Print the seal's stdout verbatim in the ship completion message. Continue landing on exit 0 for every telemetry outcome, including `no telemetry run`, `telemetry: skipped (unparseable record <path>)` and `telemetry: not pushed (<reason>)`; missing or malformed records and publication failures never block shipping. Retain an unparseable record in place and preserve unpublished telemetry for retry. In a plain jj workspace, report `telemetry: not pushed (jj workspace: no git remote)` and retain local metadata. Stop before the first landing command only on exit 1 for a usage error (invalid arguments or base), quote the bin's line and ask the user how to proceed.
|
|
252
252
|
#### PR-subject row 1: Push to the PR branch
|
|
253
253
|
For the close-absorbed-PR question, start the message with `gauntlet: gate=finishing-a-development-branch/pr-subject-row-1`. Read and apply [menu-layout](../brainstorming/reference/menu-layout.md). Do not report `done pushed` while this gate awaits an answer. Capture the scoped verification set and accepted-complete task/thread evidence before the strip. Refetch threads per recorded PR using the paginated single-`$n` query (one invocation per PR) owned by [pr-head](../using-git-worktrees/reference/pr-head.md); retain PR attribution and use the query's location rendering. For home, use the table below and omit threads now resolved on GitHub. For members, copy recorded dispositions and mark newly resolved or newly opened states from the refresh; never use home's first-parent history as member-fix proof.
|
|
254
254
|
| Refetched thread / evidence | Resolution |
|
|
@@ -346,7 +346,7 @@ git -C "$PRIMARY" commit -m "<imperative summary> (ref <ticket-id>)"
|
|
|
346
346
|
(cd "$PRIMARY" && <the Step 1 set>)
|
|
347
347
|
```
|
|
348
348
|
|
|
349
|
-
|
|
349
|
+
Keep only the spec and implementation in the staged squash tree; the stripped plan stays in feature history and the sealed telemetry record belongs to `gauntlet/telemetry`.
|
|
350
350
|
|
|
351
351
|
The post-squash re-verify is not optional - `git merge --squash` can surface conflict-resolution mistakes the worktree-side run couldn't catch. For a ledger, run the per-pair post-squash recipe in [verification-set.md](reference/verification-set.md) instead of the non-ledger subshell above.
|
|
352
352
|
|
|
@@ -356,7 +356,7 @@ Cleanup worktree (Step 6), then, if Step 6 removed the worktree, `git -C "$PRIMA
|
|
|
356
356
|
|
|
357
357
|
#### Option 4: Keep As-Is
|
|
358
358
|
|
|
359
|
-
Report: "Keeping branch <name>. Worktree preserved at <path>."
|
|
359
|
+
Report: "Keeping branch <name>. Worktree preserved at <path>." Leave telemetry in local metadata; Keep As-Is publishes no record.
|
|
360
360
|
|
|
361
361
|
**Don't cleanup worktree.**
|
|
362
362
|
|
|
@@ -374,7 +374,7 @@ Type 'discard' to confirm.
|
|
|
374
374
|
|
|
375
375
|
Wait for exact confirmation.
|
|
376
376
|
|
|
377
|
-
If confirmed: Cleanup worktree (Step 6), then, if Step 6 removed the worktree, `git -C "$PRIMARY" branch -D "$FEATURE"`.
|
|
377
|
+
If confirmed: Cleanup worktree (Step 6), then, if Step 6 removed the worktree, `git -C "$PRIMARY" branch -D "$FEATURE"`. Publish no telemetry for Discard; leave common-dir metadata outside worktree cleanup.
|
|
378
378
|
|
|
379
379
|
### Step 6: Cleanup Workspace
|
|
380
380
|
|
|
@@ -449,12 +449,12 @@ A host-owned worktree (Step 6: no `home:` match, or the legacy "Otherwise") keep
|
|
|
449
449
|
- **Fix:** Require typed "discard" confirmation
|
|
450
450
|
|
|
451
451
|
**Skipping the strip-and-seal block in Options 1-3 (any path that lands on base)**
|
|
452
|
-
- **Problem:**
|
|
453
|
-
- **Fix:** Run the block on `$WORKTREE` before the squash or
|
|
452
|
+
- **Problem:** Skipping the block ships an ephemeral plan or skips publication of the record the spec index reads.
|
|
453
|
+
- **Fix:** Run the block on `$WORKTREE` before the squash or push. Preserve the spec on `<base-branch>` and the telemetry record on `gauntlet/telemetry`; retain the plan only in feature history.
|
|
454
454
|
|
|
455
|
-
**
|
|
456
|
-
- **Problem:**
|
|
457
|
-
- **Fix:**
|
|
455
|
+
**Treating telemetry as a checkout deliverable**
|
|
456
|
+
- **Problem:** Staging a recorder file pollutes the product diff and bypasses branch publication.
|
|
457
|
+
- **Fix:** Let the seal own publication from common-dir metadata; stage no telemetry record in the product checkout.
|
|
458
458
|
|
|
459
459
|
## Completion
|
|
460
460
|
Start the final outcome message with exactly one terminal line before summaries. Start the message with `gauntlet: done pr-created` after successful normal or draft PR creation. Start the message with `gauntlet: done merged` after successful squash-merge and post-merge verification. Start the message with `gauntlet: done kept` after Keep. Start the message with `gauntlet: done discarded` only after typed discard authorization and successful discard. Start the message with `gauntlet: done pushed` after successful PR-subject push and body edit, only after the close-member gate is answered or no members qualify. Start the message with `gauntlet: stuck` followed on that line by a short concrete reason for an executed operation that fails, before error evidence or a return menu. Do not combine a terminal marker with a gate marker in one message; a subsequent menu starts a new gate message. Pending choices, implementation stalls and conformance escalation are not terminal outcomes. Read and apply [menu-layout](../brainstorming/reference/menu-layout.md). Preserve strip, seal, cleanup and ship-state rules below; terminal reporting does not complete ship after Discard or failure. Once the chosen option (Options 1-4 or PR-subject row 1 - not Discard) is executed successfully, mark the ship phase complete:
|
|
@@ -478,7 +478,7 @@ Once the merge (and any deploy) has landed, `/skill:check-delivery <ticket-ref>`
|
|
|
478
478
|
- Run any step without the `<worktree-path>` argument
|
|
479
479
|
- Auto-proceed past an undispositioned carried-open gap
|
|
480
480
|
- Skip the strip-and-seal block before the Option 3 squash or the Option 1/2 push
|
|
481
|
-
- Delete
|
|
481
|
+
- Delete common-dir telemetry metadata or stage it into the product checkout
|
|
482
482
|
|
|
483
483
|
**Always:**
|
|
484
484
|
- Verify, or apply the Step 1 skip rule, before offering options
|
|
@@ -19,7 +19,7 @@ For ledger commands outside a checkout's configured test-command matcher, verify
|
|
|
19
19
|
|
|
20
20
|
## Skip proof
|
|
21
21
|
|
|
22
|
-
**Skip rule.** Skip the run only when this set passed on a known clean commit in the verify phase of this session and no edit or write landed
|
|
22
|
+
**Skip rule.** Skip the run only when this set passed on a known clean commit in the verify phase of this session and no checkout edit or write landed since. Check the entire current tree: `git -C "$WORKTREE" diff --quiet <commit the run passed on> -- .` must exit 0, and `git -C "$WORKTREE" status --porcelain --untracked-files=all -- .` must exit 0 with empty output. Cover committed, staged, unstaged, and untracked changes without path exclusions; recorder metadata lives outside the working tree. Otherwise run it once; a missing verified commit, a failed check, or uncertainty means run.
|
|
23
23
|
|
|
24
24
|
Apply the proof independently in each owning checkout. Post-squash verification is mandatory; the skip proof does not cover a changed primary tree.
|
|
25
25
|
|
|
@@ -31,4 +31,4 @@ For a fresh-session retry after a ledger was stripped, recover the committed pre
|
|
|
31
31
|
|
|
32
32
|
## Post-squash execution
|
|
33
33
|
|
|
34
|
-
For Option 3, run each delivered leg's retained pairs in order as `(cd "<leg primary>" && <command>) > "$LOG_DIR/gate-<i>.log" 2>&1`, one command per pair
|
|
34
|
+
For Option 3, run each delivered leg's retained pairs in order as `(cd "<leg primary>" && <command>) > "$LOG_DIR/gate-<i>.log" 2>&1`, one command per pair; print `gate <i> exit <code> <log path>` after each and stop on the first failure. For coordinated delivery, run only the just-squashed leg's pairs at that leg's delivery step; every remaining leg gets its own post-squash run when delivered. A failed-squash receipt retry runs those same pairs against the delivered primary, not the feature worktree, and never repeats the squash. Retain the Step 1 command set and execution form for non-ledger subjects.
|
|
@@ -92,6 +92,8 @@ Show evidence only in-place from record/helper paths, then re-ask. Queue all oth
|
|
|
92
92
|
|
|
93
93
|
Run parent cleanup/stop only after all children finish, never on a live child's tree. For cleanup apply cleanup guards and ordered overrides. Keep the PR and post rows in the roster, set cleanup_done, and re-render with cleanup gone. Cleanup does not retire.
|
|
94
94
|
|
|
95
|
-
For non-final stop run `cleanup.md` `## Non-final stop` after all children finish. Under terminal overlays leave checkout as found and print leftover path/branch.
|
|
95
|
+
For non-final stop run `cleanup.md` `## Non-final stop` after all children finish. Under terminal overlays leave checkout as found and print leftover path/branch. Retain the PR's row from `post-selection-loop.md` `### Settlement rows` in the parent roster before deleting only that PR's record, helper outputs, and log_path files under existing teardown rules, then retire it. While any PR remains, start a retirement message with `gauntlet: retired #<N> - <reason>`, never `gauntlet: done` or `gauntlet: stuck`; keep private child result lines unchanged. Await all next-batch results and re-render every nonretired PR; repeat until all retire.
|
|
96
|
+
|
|
97
|
+
After the last PR retires and every selected cleanup or follow-on step finishes, render one final message. Start the message with `gauntlet: stuck retired` when any retained outcome is `stuck - <reason>`, otherwise `gauntlet: done retired`. Print every retained PR row below it in roster order, including PRs retired in earlier rounds. Emit no per-PR terminal message before this set settlement.
|
|
96
98
|
|
|
97
99
|
Initialize parent tracker tasks `pr-<N> state: <record path>`; mark in_progress on dispatch, complete on retirement, failed on unretried failure. Child trackers are session-local stages, not parent roster tasks. Use a plain checklist if plan_tracker is unavailable.
|
|
@@ -22,7 +22,7 @@ Consequence clause for the menu row (`decision-menu.md`): `remove <worktree.path
|
|
|
22
22
|
|
|
23
23
|
Follow the [shared layout](../../brainstorming/reference/menu-layout.md).
|
|
24
24
|
|
|
25
|
-
Start the message with `gauntlet: stuck` followed on that line by the reason: use the obstacle's `stuck - ` text as the reason, or `stopped at <template id>` for initial, assess-terminal, and contract-exception. On a failed reset/assertion use the actual error instead.
|
|
25
|
+
Finish the stop consequences below before the final message. Start the message with `gauntlet: stuck` followed on that line by the reason: use the obstacle's `stuck - ` text as the reason, or `stopped at <template id>` for initial, assess-terminal, and contract-exception. On a failed reset/assertion use the actual error instead. Print the PR row from `post-selection-loop.md` `### Settlement rows` with `stuck - <reason>` below the marker, then cleanup results. In set mode use `batch.md`'s per-PR retirement marker and retain the row for the final roster settlement.
|
|
26
26
|
|
|
27
27
|
Enter here for `stop` on the initial, assess-terminal, obstacle, or contract-exception menu. On a `rebased locally` head, run `git -C <worktree.path> reset --hard <pre_head>` first; assert `HEAD == <pre_head>` and a clean tree. Stop on a failed assertion.
|
|
28
28
|
|
|
@@ -195,9 +195,15 @@ On completion or timeout, refresh then run steps 2-5 of Re-render plus step 1's
|
|
|
195
195
|
|
|
196
196
|
Before posting or committing any external payload - review bodies, replies, commit subjects, tracker comments - re-read it against SKILL.md `## Wording rules` and any `## comms style` rules: ASCII only; no headings or template scaffolding under ~150 words; findings `file:line`-specific where one exists; ends on the fix or asked action, not a recap; never invents content to fill a section. The `AC coverage` block is the one exception: it posts verbatim, heading and checkbox rows included.
|
|
197
197
|
|
|
198
|
+
### Settlement rows
|
|
199
|
+
|
|
200
|
+
Emit the terminal message only after every selected follow-on step finishes and the cleanup pick is answered (run or declined by stop); a report or menu is not settlement. Below its marker print one plain `#<N>@<head sha>: <outcome>[ (<ticket ref>)]` row per PR in roster order, singleton included. Use the full assessed `headRefOid`, not the merge commit or a shortened display SHA. Use the last gathered head when assessment could not start. Append the parenthetical only when the PR title or body's first line names a ticket under the overrides `## Issue tracker` `refs:` convention; otherwise omit it. Use exactly `approved`, `merged`, `closed-pr`, `final`, `merge-queued`, or `stuck - <reason>`, retaining the actual stop/error reason. Keep rows without bullets, code fences or emphasis; put checkout paths and cleanup results after them.
|
|
201
|
+
|
|
202
|
+
For set mode, retain each row before deleting its PR record and follow `batch.md`'s retirement and final-settlement rules. Keep private child result lines unchanged.
|
|
203
|
+
|
|
198
204
|
### Teardown
|
|
199
205
|
|
|
200
|
-
Start the message with `gauntlet: done approved` for stop on approved. Start the message with `gauntlet: done merged` for stop on own-merge. Start the message with `gauntlet: done closed-pr` for stop on closed-pr. Start the message with `gauntlet: done final` for stop on final. Start the message with `gauntlet: done merge-queued` for stop on merge-queued.
|
|
206
|
+
Start the message with `gauntlet: done approved` for stop on approved. Start the message with `gauntlet: done merged` for stop on own-merge. Start the message with `gauntlet: done closed-pr` for stop on closed-pr. Start the message with `gauntlet: done final` for stop on final. Start the message with `gauntlet: done merge-queued` for stop on merge-queued. Print the row from `### Settlement rows`, then the checkout path and local branch. A report Verdict above a menu is not a terminal. Start the message with `gauntlet: stuck` followed by the actual error on any hard assertion failure, including a delegated fix-wave restore/assert failure; print the PR row with `stuck - <actual error>` below it and end with the actual error, without a continuation menu. In set mode use `batch.md`'s per-PR retirement marker until every PR retires.
|
|
201
207
|
|
|
202
208
|
Delegate non-final stop (initial, assess terminal, obstacle, contract exception) to `cleanup.md` `## Non-final stop`. Keep terminal cleanup behind its own pick; terminal stop leaves the checkout as found and reports its path and local branch. Render cleanup only on terminal templates; never run cleanup steps 2-4 for a non-final stop.
|
|
203
209
|
|
|
@@ -94,16 +94,16 @@ After confirmation:
|
|
|
94
94
|
2. Call `phase_tracker({ action: "start", phase: "brainstorm" })`, then `phase_tracker({ action: "skip", phase: "brainstorm", reason: "resume: <spec path>" })`, then `phase_tracker({ action: "start", phase: "plan" })`.
|
|
95
95
|
3. Admit exactly one positive safe-integer `cap:` or `allowance:` header; both or neither is `ledger header malformed: <observed header lines>`, requiring human repair by hand. Call `plan_check({ planPath: <absolute ledger path> })` with legacy-compatible admission. On FAIL, stop with findings, retaining plan in_progress. Start the message with `gauntlet: stuck` followed by the findings reason on that line; never enter `## Plan repair` or writing-plans. On PASS, call `phase_tracker({ action: "skip", phase: "plan", reason: "resume: <spec> (loop engine)" })`, then `phase_tracker({ action: "start", phase: "implement" })`. Use the resolved absolute spec path as `<spec>` in the reason. Restore implement through refresh, not `init`.
|
|
96
96
|
4. **Shared allowance boundary (every ledger restore, before phase continuation).** Read `/skill:loop-implementation`. Skip Entry step 1's allowance seeding; restore base, verification, iterations, row attempts and decisions from the ledger without reseeding. Resolve `gauntlet_setting({ key: "loopEngine" })` exactly once at the driver's authorized-restore grant boundary and retain `escalateAfter` in driver memory for this run; share this read with step 4.2, never refresh it inside the loop. On an unavailable tool, stop before any dispatch or write with `cannot resolve escalation threshold: settings tool unavailable (loopEngine.escalateAfter)`. Apply the following order on bare-worktree and brief routes alike:
|
|
97
|
-
1. Apply pending recovery below against the unmigrated ledger, including home returns whose child commit removed the tuple. Validate the original one-commit `<pre-dispatch HEAD>..HEAD` range against its unchanged before-image. For companions, retain unchanged home HEAD and admit the expected iterations-bump-plus-tuple dirt. On unrecoverable attribution or before-image, stop before refresh. Resolve ambiguous-history admission before tracker calls as required below.
|
|
97
|
+
1. Apply pending recovery below against the unmigrated ledger, including home returns whose child commit removed the tuple. Validate the original one-commit `<pre-dispatch HEAD>..HEAD` range against its unchanged before-image. For companions, retain unchanged home HEAD and admit the expected iterations-bump-plus-tuple dirt. On unrecoverable attribution or before-image, stop before refresh. Resolve ambiguous-history admission before tracker calls as required below. Apply the driver's ownership-fenced interrupted recovery before migration; migration changes neither admission ranges nor the home HEAD used for recovery.
|
|
98
98
|
2. Migrate only an admitted, clean, tuple-free ledger with no retained recovery stop. With `cap:` and no `allowance:`, reuse the single restore-boundary settings read and replace the cap line with `allowance: <maxIterationsPerRow>`. Commit only that header replacement as parent `loop: migrate allowance`; preserve iterations, every attempts cell and all decisions. With an existing allowance, use it without allowance resolution or a migration commit; the restore read supplies `escalateAfter` only. Defer migration through any retained recovery stop or dirty tree; after authorized recovery reaches a clean tuple-free tree, migrate before the next dispatch or verification.
|
|
99
99
|
3. Require `allowance:` and no `cap:` before continuation, including for all-closed ledgers; otherwise stop with `ledger has no allowance: line - resume through /skill:gauntlet-resume`. For a retained recovery stop or unresolved dirty tree, defer this final validation with migration and project the unmigrated ledger solely to report the stop.
|
|
100
100
|
4. Call `plan_tracker({ action: "refresh", path: <absolute ledger path> })` exactly once after phase restoration, recovery and eligible migration/final validation, before any unresolved dirty-tree stop or dispatch. On verify/ship, restore the saved mixed task list first as `brief-contract.md` specifies; on implement, use no `init`. On refresh failure, stop with its findings and leave the existing task list untouched. Read `plan_tracker status` and report its exact observed state, never an inferred list.
|
|
101
|
-
Render the allowance header, rows, iterations, attempts, effective allowances and all decisions; distinguish the latest operational `row <n> ...` decision from the last physical history line. Compute each effective allowance as the header plus one per matching retry decision; ignore the retry's audit ceiling. Keep `cap +N` history as inert text and restore no funded rounds.
|
|
101
|
+
Render the allowance header, rows, iterations, attempts, effective allowances and all decisions; distinguish the latest operational `row <n> ...` decision from the last physical history line. Compute each effective allowance as the header plus one per matching retry decision; ignore the retry's audit ceiling. Keep `cap +N` history as inert text and restore no funded rounds. For a recovered pending tuple with a recoverable before-image, apply the driver's ## Interrupted recovery below allowance before continuation; require exclusive checkout provenance and confirmed owned-job exits before any reset. Report unresolved cleanup or ownership with the driver's recovery-incomplete header, saved unresolved-row counts and discard-authorization menu, preserving changes and making no redispatch. Keep unrecoverable before-image and ambiguous-history stops. Resume the original phase only after this boundary: implement at `Before each dispatch`; verify/ship at `After the loop` step 1.
|
|
102
102
|
5. Continue at loop step 5, the delivered driver's `Before each dispatch`, with durable iterations and retry data; an all-closed ledger completes implement through refresh and takes `After the loop` step 0. A migrated unresolved row at or above its effective allowance stops under rule 2 without an attempt; keep an allowance-stopped row `failed`. Render a row whose `pending:` tuple is restored or retained as `row <n> in_progress (pending tuple restored)`. Never reset counters or reseed a validated seed.
|
|
103
103
|
|
|
104
104
|
### Pending recovery
|
|
105
105
|
|
|
106
|
-
Recover before dispatch or verification on every ledger restore. Run the read-only admission-evidence check below before phase/task restoration on every ledger route, including briefs; resolve its human question before any tracker call. Apply recovery writes only at the ordered pending-recovery step. Read the durable `pending:` tuple (target row or
|
|
106
|
+
Recover before dispatch or verification on every ledger restore. Run the read-only admission-evidence check below before phase/task restoration on every ledger route, including briefs; resolve its human question before any tracker call. Apply recovery writes only at the ordered pending-recovery step. Read the durable `pending:` tuple (target row, seed or verify, repo, pre-dispatch HEAD); the parent's uncommitted iterations bump plus tuple is expected dispatch dirt, not a reason to skip recovery. Route `pending: verify` through the loop driver's After the loop step 1 repair admission/recovery before the row/seed paths below; preserve its reserved count and consumed iteration without a second marker or bump.
|
|
107
107
|
|
|
108
108
|
With no `pending:` on disk, inspect home commit history before any refresh. Find the latest home child row/seed commit that changed this ledger, even when unrelated commits follow it; read its subject and body with `git -C <worktree> log --format=%H -- <ledger path>` and `git -C <worktree> show -s --format=%B <sha>`. Exclude returns superseded by a parent admission/repair or approved ledger mutation proven by the evidence below; never replay those over the parent's current ledger.
|
|
109
109
|
|
|
@@ -116,13 +116,13 @@ Revalidate a clean, exactly-one-commit return through the home row below. For la
|
|
|
116
116
|
| Confirmed admission state | Action |
|
|
117
117
|
|---|---|
|
|
118
118
|
| Confirmed settled | Preserve the current ledger and continue normal restore; require fresh verification/conformance, not historical gate credit. |
|
|
119
|
-
| Confirmed interrupted | Validate the entire `<pre-dispatch HEAD>..HEAD` range, never just `HEAD^..HEAD`, through the home row below.
|
|
119
|
+
| Confirmed interrupted | Validate the entire `<pre-dispatch HEAD>..HEAD` range, never just `HEAD^..HEAD`, through the home row below. Use the recovered tuple and before-image without another bump. Confirmation that admission was interrupted proves no ownership of later writes: apply the driver's ## Interrupted recovery only with retained exclusive checkout/write provenance and owned-job exit evidence. Missing evidence preserves child and unrelated changes at recovery incomplete; project saved row cells without overwriting the ledger. A completed recovery writes the parent repair with iterations at the recovered consumed count. |
|
|
120
120
|
| Still unknown | Stop without mutation; ask for the missing admission evidence, never turn uncertainty into a discard/reset decision. |
|
|
121
121
|
|
|
122
122
|
| Tuple target | Recovery |
|
|
123
123
|
|---|---|
|
|
124
|
-
| Home leg or single-member `home` | Reconstruct the pre-dispatch ledger from `HEAD:<ledger path>` when HEAD is the tuple SHA, otherwise from `<pre-dispatch HEAD>:<ledger path>`. Compare live HEAD with that SHA. Admit exactly one new home commit with subject `loop: row <n> <status>` or `loop: seed evidence` only through the driver's `## Validate the return`: require a clean tree and the bounded row/decision result, allowing only the consumed iteration and pending removal header deltas. Clear the tuple through the admitted close or ordinary-miss repair.
|
|
125
|
-
| Actual multi-member companion | Apply `/skill:loop-implementation` `reference/coordinated-legs.md` `## Resume`: require unchanged home HEAD and exactly one new companion commit with tuple attribution, then validate its result. Record that leg's admitted rows, decisions or seeded state and SHA, removing the tuple in the home-ledger commit; never rerun the recovered row or its admitted extras.
|
|
124
|
+
| Home leg or single-member `home` | Reconstruct the pre-dispatch ledger from `HEAD:<ledger path>` when HEAD is the tuple SHA, otherwise from `<pre-dispatch HEAD>:<ledger path>`. Compare live HEAD with that SHA. Admit exactly one new home commit with subject `loop: row <n> <status>` or `loop: seed evidence` only through the driver's `## Validate the return`: require a clean tree and the bounded row/decision result, allowing only the consumed iteration and pending removal header deltas. Clear the tuple through the admitted close or ordinary-miss repair. Route zero new commits, multiple commits, dirty or non-DONE returns through the driver's ## Interrupted recovery against the tuple HEAD and its ledger before-image. Require exclusive checkout provenance and verified process identity/exit before reset; after clean/zero-commit observations write the repair with the already-consumed iteration, clear pending and continue at Before each dispatch. Preserve every change at recovery incomplete when ownership or cleanup is unresolved. Unrelated movement, attribution mismatch or an unrecoverable before-image stops without redispatch or double counting. |
|
|
125
|
+
| Actual multi-member companion | Apply `/skill:loop-implementation` `reference/coordinated-legs.md` `## Resume`: require unchanged home HEAD and exactly one new companion commit with tuple attribution, then validate its result. Record that leg's admitted rows, decisions or seeded state and SHA, removing the tuple in the home-ledger commit; never rerun the recovered row or its admitted extras. Route dirty, zero/multiple-commit or non-DONE boundaries through that reference's ownership-fenced interrupted recovery, using the companion tuple HEAD and saved home ledger; preserve state on unrelated movement, attribution mismatch, unrecoverable before-image or unresolved ownership/cleanup; route post-boundary result-validation misses through ordinary `loop: repair row <n>` or seed `loop: repair seed` repair in that protocol, clear `pending:` and preserve the consumed iteration. |
|
|
126
126
|
|
|
127
127
|
## Spec with plan
|
|
128
128
|
|
|
@@ -13,12 +13,7 @@ Before starting the seed sequence, read the seed's header. A `**Home:**` header
|
|
|
13
13
|
stop: `not tracked: <seed> - commit it first`. Line 1 of `<seed>` equal to
|
|
14
14
|
`# CONTEXT DRAFT - NOT A SPEC - fully replaced at spec-writing` -> stop:
|
|
15
15
|
`context draft, not a spec: <seed> - finish brainstorming`.
|
|
16
|
-
3. **Not shipped.**
|
|
17
|
-
(`piGauntlet.telemetry.dir`, default `.pi/gauntlet/telemetry`). A `shipped_at:` or
|
|
18
|
-
`abandoned_at:` line carrying a value -> stop:
|
|
19
|
-
`spec already shipped at <ts> - write a superseding spec`. No record, or
|
|
20
|
-
`in_progress`: continue; the extension folds this run into the record on
|
|
21
|
-
`plan_check` pass.
|
|
16
|
+
3. **Not shipped.** Resolve `piGauntlet.telemetry` from preset and primary project settings; `enabled: false` skips this check. Run `git -C <primary> rev-parse --git-common-dir`; resolve a relative result against `<primary>` and read `<git common dir>/gauntlet/telemetry/<rel with .md -> .yaml>`. When git resolution fails and `jj root` in `<primary>` succeeds, read `<jj root>/.jj/gauntlet/telemetry/<rel with .md -> .yaml>` instead. When the local record is absent, fetch `gauntlet/telemetry` from `telemetry.remote` (default `origin`) and read `<owner>/<repo>/brainstorming/<rel with .md -> .yaml>` from the fetched ref, using the last fetched ref on fetch failure. Degrade to the local-only check when no fetched ref is available, including jj-only workspaces. A valued `shipped_at:` or `abandoned_at:` stops: `spec already shipped at <ts> - write a superseding spec`. No record or `in_progress`: continue; let the extension bind on `plan_check` pass.
|
|
22
17
|
4. **Name.** `<name>` = basename of `<rel>` minus `.md`, minus a leading `YYYY-MM-DD-`:
|
|
23
18
|
`2026-09-17-gh-31-gauntlet-resume.md` -> `gh-31-gauntlet-resume`. Same shape as
|
|
24
19
|
brainstorming's Filename Convention slug; wrappers receive it as `<name>`.
|