mandrel 2.65.0 → 2.67.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/.agents/agents/acceptance-critic.md +7 -7
- package/.agents/agents/auditor.md +17 -18
- package/.agents/agents/plan-critic.md +5 -5
- package/.agents/agents/story-worker.md +5 -5
- package/.agents/docs/agentrc-reference.json +2 -1
- package/.agents/docs/configuration.md +2 -1
- package/.agents/docs/execution-reference.md +27 -5
- package/.agents/docs/workflows.md +4 -2
- package/.agents/instructions.md +12 -13
- package/.agents/rules/ci-remediation.md +3 -3
- package/.agents/rules/gherkin-standards.md +3 -2
- package/.agents/rules/git-conventions-reference.md +17 -8
- package/.agents/rules/git-conventions.md +10 -8
- package/.agents/rules/testing-standards.md +8 -7
- package/.agents/runtime-deps.json +1 -1
- package/.agents/schemas/agentrc.schema.json +6 -1
- package/.agents/scripts/boot-sweep.js +97 -9
- package/.agents/scripts/bootstrap.js +94 -89
- package/.agents/scripts/{git-cleanup.js → clean-git.js} +2 -2
- package/.agents/scripts/clean-temp.js +54 -0
- package/.agents/scripts/clean-worktrees.js +593 -0
- package/.agents/scripts/drain-pending-cleanup.js +5 -4
- package/.agents/scripts/lib/baselines/duplication-scanner.js +17 -7
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +78 -78
- package/.agents/scripts/lib/clean-temp.js +440 -0
- package/.agents/scripts/lib/cli/standard-args.js +60 -76
- package/.agents/scripts/lib/cli-args.js +26 -0
- package/.agents/scripts/lib/config/gates/shared.js +3 -3
- package/.agents/scripts/lib/config-settings-schema-delivery.js +11 -2
- package/.agents/scripts/lib/feedback-loop/graduate-steps.js +205 -0
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +47 -782
- package/.agents/scripts/lib/feedback-loop/graduator-gh.js +449 -0
- package/.agents/scripts/lib/generated/agentrc-validator.js +1 -1
- package/.agents/scripts/lib/observability/close-telemetry.js +330 -0
- package/.agents/scripts/lib/observability/runtime-friction.js +2 -0
- package/.agents/scripts/lib/observability/signal-validator.js +17 -5
- package/.agents/scripts/lib/observability/source-classifier.js +3 -1
- package/.agents/scripts/lib/orchestration/code-review.js +22 -0
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
- package/.agents/scripts/lib/orchestration/plan-metrics.js +76 -63
- package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +149 -97
- package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +23 -0
- package/.agents/scripts/lib/orchestration/run-epilogue.js +6 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +2 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +349 -263
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +21 -7
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +4 -0
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +327 -314
- package/.agents/scripts/lib/orchestration/ticket-validator.js +19 -36
- package/.agents/scripts/lib/signals/detectors/common.js +63 -51
- package/.agents/scripts/lib/single-story-sweep.js +2 -2
- package/.agents/scripts/lib/temp-removal.js +110 -0
- package/.agents/scripts/lib/temp-retention.js +122 -73
- package/.agents/scripts/lib/transpile.js +28 -3
- package/.agents/scripts/lib/worktree/canonical-path.js +34 -0
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +15 -4
- package/.agents/scripts/single-story-close.js +10 -2
- package/.agents/scripts/single-story-confirm-merge.js +267 -238
- package/.agents/scripts/single-story-init.js +120 -17
- package/.agents/skills/core/idea-refinement/SKILL.md +6 -6
- package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -2
- package/.agents/workflows/audit-architecture.md +5 -4
- package/.agents/workflows/audit-documentation.md +5 -5
- package/.agents/workflows/audit-performance.md +10 -10
- package/.agents/workflows/{git-cleanup.md → clean-git.md} +10 -10
- package/.agents/workflows/clean-temp.md +67 -0
- package/.agents/workflows/clean-worktrees.md +63 -0
- package/.agents/workflows/git-deliver.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +11 -10
- package/.agents/workflows/helpers/audit-lens-core.md +30 -57
- package/.agents/workflows/helpers/deliver-digest.md +2 -2
- package/.agents/workflows/helpers/deliver-reference.md +3 -1
- package/.agents/workflows/helpers/deliver-story-reference.md +2 -2
- package/.agents/workflows/helpers/deliver-story.md +6 -1
- package/.agents/workflows/helpers/parallel-tooling.md +16 -18
- package/.agents/workflows/mandrel-deliver.md +1 -1
- package/.agents/workflows/mandrel-plan.md +6 -5
- package/docs/CHANGELOG.md +39 -0
- package/lib/cli/guarded-sync.js +87 -0
- package/lib/cli/sync-agents.js +9 -92
- package/lib/cli/sync-commands.js +9 -101
- package/package.json +2 -2
|
@@ -229,10 +229,60 @@ export function decideStoryBranchSeed({ localHas, remoteHas }) {
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
/**
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
232
|
+
* Remove closed-Story `.worktrees/story-<id>` trees through the boot sweep's
|
|
233
|
+
* own seam (`runWorktreeSweep`: same lock, same invariants). The Story being
|
|
234
|
+
* initialized is always kept, whatever its ticket state. Never throws: a
|
|
235
|
+
* failure lands in the returned outcome, which rides the init envelope.
|
|
236
|
+
*
|
|
237
|
+
* @returns {Promise<object>} `{ ok, reaped, skipped, reason?, error? }`.
|
|
238
|
+
*/
|
|
239
|
+
export async function reapClosedStoryWorktrees({
|
|
240
|
+
cwd,
|
|
241
|
+
storyBranch,
|
|
242
|
+
provider,
|
|
243
|
+
lockPath,
|
|
244
|
+
lockTimeoutMs,
|
|
245
|
+
worktreeSweepFn,
|
|
246
|
+
acquireLockFn,
|
|
247
|
+
}) {
|
|
248
|
+
const logger = {
|
|
249
|
+
info: (m) => progress('CLEANUP', m),
|
|
250
|
+
warn: (m) => progress('CLEANUP', `⚠️ ${m}`),
|
|
251
|
+
};
|
|
252
|
+
try {
|
|
253
|
+
const { runWorktreeSweep } = await import('./boot-sweep.js');
|
|
254
|
+
const outcome = await runWorktreeSweep({
|
|
255
|
+
root: cwd,
|
|
256
|
+
provider,
|
|
257
|
+
lockPath,
|
|
258
|
+
lockTimeoutMs,
|
|
259
|
+
...(worktreeSweepFn ? { sweepFn: worktreeSweepFn } : {}),
|
|
260
|
+
...(acquireLockFn ? { acquireLockFn } : {}),
|
|
261
|
+
logger,
|
|
262
|
+
logTag: '[worktree-sweep]',
|
|
263
|
+
keepPaths: [path.join(cwd, '.worktrees', storyBranch)],
|
|
264
|
+
});
|
|
265
|
+
if (outcome.reaped?.length > 0) {
|
|
266
|
+
progress(
|
|
267
|
+
'CLEANUP',
|
|
268
|
+
`🧹 removed ${outcome.reaped.length} closed-Story worktree(s).`,
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
return outcome;
|
|
272
|
+
} catch (err) {
|
|
273
|
+
const msg = err?.message ?? String(err);
|
|
274
|
+
logger.warn(`worktree sweep threw (init continues): ${msg}`);
|
|
275
|
+
return { ok: false, error: msg, reaped: [], skipped: [] };
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Reap merged `story-*` branches (excluding the current one), then the
|
|
281
|
+
* closed-Story worktrees. Never blocks init. Protected candidates (unpushed
|
|
282
|
+
* work, dirty worktree, open Story) are skipped; the lockfile is shared with
|
|
283
|
+
* `boot-sweep.js` via `resolveSweepLockPath` so concurrent reaps cannot race.
|
|
284
|
+
*
|
|
285
|
+
* @returns {Promise<{ worktreeSweep: object }>}
|
|
236
286
|
*/
|
|
237
287
|
export async function reapMergedStoryBranches({
|
|
238
288
|
cwd,
|
|
@@ -241,6 +291,8 @@ export async function reapMergedStoryBranches({
|
|
|
241
291
|
config,
|
|
242
292
|
provider,
|
|
243
293
|
injectedSweep,
|
|
294
|
+
worktreeSweepFn,
|
|
295
|
+
acquireLockFn,
|
|
244
296
|
}) {
|
|
245
297
|
const sweepFn =
|
|
246
298
|
injectedSweep ??
|
|
@@ -248,7 +300,37 @@ export async function reapMergedStoryBranches({
|
|
|
248
300
|
const tempRoot = config?.project?.paths?.tempRoot ?? 'temp';
|
|
249
301
|
const lockPath = resolveSweepLockPath({ cwd, tempRoot });
|
|
250
302
|
const lockTimeoutMs =
|
|
251
|
-
config
|
|
303
|
+
config?.delivery?.worktreeIsolation?.sweepLockMs ?? 60_000;
|
|
304
|
+
await reapMergedBranches({
|
|
305
|
+
cwd,
|
|
306
|
+
baseBranch,
|
|
307
|
+
storyBranch,
|
|
308
|
+
provider,
|
|
309
|
+
sweepFn,
|
|
310
|
+
lockPath,
|
|
311
|
+
lockTimeoutMs,
|
|
312
|
+
});
|
|
313
|
+
const worktreeSweep = await reapClosedStoryWorktrees({
|
|
314
|
+
cwd,
|
|
315
|
+
storyBranch,
|
|
316
|
+
provider,
|
|
317
|
+
lockPath,
|
|
318
|
+
lockTimeoutMs,
|
|
319
|
+
worktreeSweepFn,
|
|
320
|
+
acquireLockFn,
|
|
321
|
+
});
|
|
322
|
+
return { worktreeSweep };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async function reapMergedBranches({
|
|
326
|
+
cwd,
|
|
327
|
+
baseBranch,
|
|
328
|
+
storyBranch,
|
|
329
|
+
provider,
|
|
330
|
+
sweepFn,
|
|
331
|
+
lockPath,
|
|
332
|
+
lockTimeoutMs,
|
|
333
|
+
}) {
|
|
252
334
|
try {
|
|
253
335
|
const sweep = await sweepFn({
|
|
254
336
|
cwd,
|
|
@@ -300,9 +382,12 @@ export async function reapMergedStoryBranches({
|
|
|
300
382
|
* @param {object} opts.config
|
|
301
383
|
* @param {object} opts.provider
|
|
302
384
|
* @param {Function|undefined} opts.injectedSweep
|
|
385
|
+
* @param {Function} [opts.worktreeSweepFn] Test override for the
|
|
386
|
+
* closed-Story worktree sweep.
|
|
303
387
|
* @param {Function} opts.progress
|
|
304
388
|
* @param {import('./lib/git/cached-fetch.js').FetchCache} [opts.fetchCache]
|
|
305
389
|
* Test override; production shares the module singleton.
|
|
390
|
+
* @returns {Promise<{ worktreeSweep: object }>}
|
|
306
391
|
*/
|
|
307
392
|
export async function materializeBaseBranch({
|
|
308
393
|
cwd,
|
|
@@ -311,6 +396,7 @@ export async function materializeBaseBranch({
|
|
|
311
396
|
config,
|
|
312
397
|
provider,
|
|
313
398
|
injectedSweep,
|
|
399
|
+
worktreeSweepFn,
|
|
314
400
|
progress,
|
|
315
401
|
fetchCache,
|
|
316
402
|
}) {
|
|
@@ -326,13 +412,14 @@ export async function materializeBaseBranch({
|
|
|
326
412
|
);
|
|
327
413
|
}
|
|
328
414
|
|
|
329
|
-
await reapMergedStoryBranches({
|
|
415
|
+
const { worktreeSweep } = await reapMergedStoryBranches({
|
|
330
416
|
cwd,
|
|
331
417
|
baseBranch,
|
|
332
418
|
storyBranch,
|
|
333
419
|
config,
|
|
334
420
|
provider,
|
|
335
421
|
injectedSweep,
|
|
422
|
+
worktreeSweepFn,
|
|
336
423
|
});
|
|
337
424
|
|
|
338
425
|
if (!branchExistsLocally(baseBranch, cwd)) {
|
|
@@ -342,10 +429,19 @@ export async function materializeBaseBranch({
|
|
|
342
429
|
`Failed to fetch base branch ${baseBranch}: ${r.stderr || '(no stderr)'}`,
|
|
343
430
|
);
|
|
344
431
|
}
|
|
345
|
-
return;
|
|
432
|
+
return { worktreeSweep };
|
|
346
433
|
}
|
|
347
434
|
|
|
348
|
-
|
|
435
|
+
fastForwardBase({ cwd, baseBranch, progress });
|
|
436
|
+
return { worktreeSweep };
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* `git fetch` leaves local base at the old tip until fast-forwarded.
|
|
441
|
+
*
|
|
442
|
+
* @param {{ cwd: string, baseBranch: string, progress: Function }} opts
|
|
443
|
+
*/
|
|
444
|
+
function fastForwardBase({ cwd, baseBranch, progress }) {
|
|
349
445
|
const ffPlan = planFastForward({ cwd, baseBranch });
|
|
350
446
|
const ff = executeFastForward({
|
|
351
447
|
cwd,
|
|
@@ -461,6 +557,7 @@ export async function runSingleStoryInit({
|
|
|
461
557
|
injectedProvider,
|
|
462
558
|
injectedConfig,
|
|
463
559
|
injectedSweep,
|
|
560
|
+
injectedWorktreeSweep,
|
|
464
561
|
injectedAcquireLease,
|
|
465
562
|
steal = false,
|
|
466
563
|
injectedVerifyRemote,
|
|
@@ -533,6 +630,7 @@ export async function runSingleStoryInit({
|
|
|
533
630
|
let workCwd = cwd;
|
|
534
631
|
let worktreeCreated = false;
|
|
535
632
|
let installStatus = { status: 'skipped', reason: 'dry-run' };
|
|
633
|
+
let worktreeSweep = null;
|
|
536
634
|
|
|
537
635
|
if (!dryRun) {
|
|
538
636
|
const acquire = injectedAcquireLease ?? acquireStoryLease;
|
|
@@ -563,15 +661,17 @@ export async function runSingleStoryInit({
|
|
|
563
661
|
await rollUpContainerEpic(provider, storyId, config);
|
|
564
662
|
|
|
565
663
|
try {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
664
|
+
({ worktreeSweep } =
|
|
665
|
+
(await injectedMaterialize({
|
|
666
|
+
cwd,
|
|
667
|
+
baseBranch,
|
|
668
|
+
storyBranch,
|
|
669
|
+
config,
|
|
670
|
+
provider,
|
|
671
|
+
injectedSweep,
|
|
672
|
+
worktreeSweepFn: injectedWorktreeSweep,
|
|
673
|
+
progress,
|
|
674
|
+
})) ?? {});
|
|
575
675
|
injectedSeedBranch({ cwd, storyBranch, baseBranch, progress });
|
|
576
676
|
({ workCwd, worktreeCreated, installStatus } =
|
|
577
677
|
await injectedProvisionWorktree({
|
|
@@ -610,6 +710,9 @@ export async function runSingleStoryInit({
|
|
|
610
710
|
installStatus,
|
|
611
711
|
dependenciesInstalled,
|
|
612
712
|
installFailed: installStatus.status === 'failed',
|
|
713
|
+
// Closed-Story worktree sweep outcome; a failure degrades here, never
|
|
714
|
+
// into an init failure. `null` under --dry-run.
|
|
715
|
+
worktreeSweep: worktreeSweep ?? null,
|
|
613
716
|
dryRun,
|
|
614
717
|
remoteVerified: remote.remoteVerified,
|
|
615
718
|
remoteProbe: { remoteUrl: remote.remoteUrl, detail: remote.detail },
|
|
@@ -22,12 +22,12 @@ description:
|
|
|
22
22
|
|
|
23
23
|
## Activation
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
Invoked directly by the operator (`idea-refine` / `ideate`), ahead of
|
|
26
|
+
planning — it is not a step of
|
|
27
|
+
[`/mandrel-plan`](../../../workflows/mandrel-plan.md), whose Gate #1 stops only
|
|
28
|
+
for a HITL unknown or a duplicate. The skill sharpens freeform intent into the
|
|
29
|
+
canonical planning sections, so a saved one-pager feeds
|
|
30
|
+
`/mandrel-plan <path>` (seed-file mode) directly.
|
|
31
31
|
|
|
32
32
|
## Detailed Instructions
|
|
33
33
|
|
|
@@ -109,8 +109,7 @@ not a soft preference.
|
|
|
109
109
|
convenience to work around. Confirm authenticated state with a
|
|
110
110
|
`take_snapshot` before driving.
|
|
111
111
|
- **No headless fallback.** The chrome-devtools MCP surface is a host-provided
|
|
112
|
-
runtime dependency. If it is unavailable,
|
|
113
|
-
— never fall back to the retired headless BDD runner.
|
|
112
|
+
runtime dependency. If it is unavailable, report a clear error and stop.
|
|
114
113
|
|
|
115
114
|
## 4. Mode — Known-Scenario Sweep (`/qa-run`)
|
|
116
115
|
|
|
@@ -23,10 +23,11 @@ Per the core's Scope interpretation:
|
|
|
23
23
|
|
|
24
24
|
## Execution strategy
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Sequential inline execution is the fallback (see the
|
|
26
|
+
Dispatch this lens as one `subagent_type: auditor` call. Fan its dimensions
|
|
27
|
+
out across parallel `auditor` subagents (parallel-tooling Rule 3), merging
|
|
28
|
+
under the self-cross-check, only when the operator explicitly asks for
|
|
29
|
+
per-dimension fan-out. Sequential inline execution is the fallback (see the
|
|
30
|
+
core's Execution strategy).
|
|
30
31
|
|
|
31
32
|
## Step 0: Tool-first detection (mandatory — run before any LLM dimension)
|
|
32
33
|
|
|
@@ -62,11 +62,11 @@ with the config-driven target set above.
|
|
|
62
62
|
|
|
63
63
|
## Execution strategy
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
Execution strategy).
|
|
65
|
+
Dispatch this lens as one `subagent_type: auditor` call. Fan its per-doc /
|
|
66
|
+
per-dimension verification out across parallel `auditor` subagents
|
|
67
|
+
(parallel-tooling Rule 3), merging under the self-cross-check, only when the
|
|
68
|
+
operator explicitly asks for per-dimension fan-out. Sequential inline
|
|
69
|
+
execution is the fallback (see the core's Execution strategy).
|
|
70
70
|
|
|
71
71
|
## Step 1: Deterministic Signal First
|
|
72
72
|
|
|
@@ -27,18 +27,18 @@ Per the core's Scope interpretation:
|
|
|
27
27
|
|
|
28
28
|
## Execution strategy
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Sequential inline execution is the fallback (see the
|
|
30
|
+
Dispatch this lens as one `subagent_type: auditor` call. Fan its resource
|
|
31
|
+
dimensions out across parallel `auditor` subagents (parallel-tooling Rule 3),
|
|
32
|
+
merging under the self-cross-check, only when the operator explicitly asks for
|
|
33
|
+
per-dimension fan-out. Sequential inline execution is the fallback (see the
|
|
34
|
+
core's Execution strategy).
|
|
34
35
|
|
|
35
36
|
> **Measurement is non-mutating, not forbidden.** This lens is read-only with
|
|
36
|
-
> respect to source, but
|
|
37
|
-
>
|
|
38
|
-
>
|
|
39
|
-
>
|
|
40
|
-
>
|
|
41
|
-
> `.claude/workflows/audit-performance.workflow.js`.
|
|
37
|
+
> respect to source, but the auditor MUST be allowed to *run* measurements. It
|
|
38
|
+
> runs only **non-mutating** commands — profilers, timers, bundle-stat and
|
|
39
|
+
> file-size probes — and never a command that writes source, installs
|
|
40
|
+
> packages, or mutates git state or labels. The one write is the report
|
|
41
|
+
> artifact.
|
|
42
42
|
|
|
43
43
|
## Step 0: Measure before you judge (mandatory)
|
|
44
44
|
|
|
@@ -5,9 +5,9 @@ description: >-
|
|
|
5
5
|
`git stash` entries — each step gated by operator confirmation.
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# /git
|
|
8
|
+
# /clean-git [--fast-forward-main] [--prune-remotes] [--branches] [--stashes] [--execute] [--remote] [--yes] [--include-content-merged] [--drop-stashes <ref>] [--exclude <pattern>] [--json]
|
|
9
9
|
|
|
10
|
-
`/git
|
|
10
|
+
`/clean-git` folds the four cleanup steps operators routinely run by hand
|
|
11
11
|
after a busy session into a single pipeline with per-step confirmation. It is a
|
|
12
12
|
**recovery tool**, not a routine chore: the delivering flows already reap their
|
|
13
13
|
own merged refs and fast-forward the base branch (see
|
|
@@ -22,13 +22,13 @@ Reach for it when the automated hygiene left an unusual state behind.
|
|
|
22
22
|
> skill citation.
|
|
23
23
|
|
|
24
24
|
The enumeration + reap logic lives in
|
|
25
|
-
[`git
|
|
25
|
+
[`clean-git.js`](../scripts/clean-git.js) — it computes the candidate list,
|
|
26
26
|
the skip taxonomy, the detection signals, and the JSON envelope, and prints them
|
|
27
27
|
itself. Without `--execute` the script is a **dry-run preview**; nothing is
|
|
28
28
|
mutated. When no phase flag is passed, **all four phases run** sequentially; a
|
|
29
29
|
phase flag narrows the run. A failure in one phase does not short-circuit the
|
|
30
30
|
others — each runs and reports independently. The script documents its own
|
|
31
|
-
flags: `node .agents/scripts/git
|
|
31
|
+
flags: `node .agents/scripts/clean-git.js --help`.
|
|
32
32
|
|
|
33
33
|
## Phases
|
|
34
34
|
|
|
@@ -65,24 +65,24 @@ merged-PR branch in scope unless `--exclude`d.
|
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
67
|
# Preview all four phases (no mutation).
|
|
68
|
-
node .agents/scripts/git
|
|
68
|
+
node .agents/scripts/clean-git.js
|
|
69
69
|
|
|
70
70
|
# Run everything non-interactively, including origin refs. Branches detected
|
|
71
71
|
# only by content-equivalence keep their origin ref — see the note below.
|
|
72
|
-
node .agents/scripts/git
|
|
72
|
+
node .agents/scripts/clean-git.js --execute --remote --yes
|
|
73
73
|
|
|
74
74
|
# Same, but also delete the origin refs of content-merged branches. Nobody is
|
|
75
75
|
# watching, so opting in is the whole confirmation this delete ever gets.
|
|
76
|
-
node .agents/scripts/git
|
|
76
|
+
node .agents/scripts/clean-git.js --execute --remote --yes \
|
|
77
77
|
--include-content-merged
|
|
78
78
|
|
|
79
79
|
# Only fast-forward main.
|
|
80
|
-
node .agents/scripts/git
|
|
80
|
+
node .agents/scripts/clean-git.js --fast-forward-main --execute
|
|
81
81
|
|
|
82
82
|
# Only sweep merged branches + their origin refs.
|
|
83
|
-
node .agents/scripts/git
|
|
83
|
+
node .agents/scripts/clean-git.js --branches --execute --remote
|
|
84
84
|
|
|
85
85
|
# Drop specific stashes under --yes.
|
|
86
|
-
node .agents/scripts/git
|
|
86
|
+
node .agents/scripts/clean-git.js --stashes --execute --yes \
|
|
87
87
|
--drop-stashes 'stash@{0}' --drop-stashes 'stash@{2}'
|
|
88
88
|
```
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: >-
|
|
3
|
+
Clear the temp-tree backlog the land-time purge cannot attribute: sort every
|
|
4
|
+
top-level entry under the project's tempRoot into framework, closed-issue,
|
|
5
|
+
aged and kept buckets, preview by default, and delete only confirmed buckets.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# /clean-temp [--execute] [--yes] [--json]
|
|
9
|
+
|
|
10
|
+
The land-time and boot-time purges only reap what they can attribute: the
|
|
11
|
+
framework's own temp layouts and `temp/scratch/`. Everything else an agent
|
|
12
|
+
dropped at the temp root is reported and left alone, so a busy consumer's temp
|
|
13
|
+
tree grows without bound. `/clean-temp` is the operator's catch-up for that
|
|
14
|
+
backlog. It classifies and deletes through the same temp-retention engine the
|
|
15
|
+
purges use — there is no second walker.
|
|
16
|
+
|
|
17
|
+
The script documents its own flags:
|
|
18
|
+
`node .agents/scripts/clean-temp.js --help`. Without `--execute` it is a
|
|
19
|
+
**dry-run preview**; nothing is deleted.
|
|
20
|
+
|
|
21
|
+
## Buckets
|
|
22
|
+
|
|
23
|
+
Every top-level entry under tempRoot lands in exactly one:
|
|
24
|
+
|
|
25
|
+
| Bucket | What it holds | Unattended (`--yes`) |
|
|
26
|
+
| --- | --- | --- |
|
|
27
|
+
| **framework** | A framework layout holding artifacts the auto-purge would take — Story-keyed on a closed Story, or past `staleDays`. | Deleted |
|
|
28
|
+
| **closed-issue** | An unrecognized entry whose basename names exactly one issue id, and that issue reads `closed`. | Deleted |
|
|
29
|
+
| **aged** | An unrecognized entry naming no id, or several, older than `staleDays`. | **Never** — an age heuristic has no attribution, so it needs a human |
|
|
30
|
+
| **kept** | Everything else, with the reason: issue open, issue read failed, too recent, reserved, or nothing spent. | Kept |
|
|
31
|
+
|
|
32
|
+
An id is a standalone run of up to seven digits in the basename. A basename
|
|
33
|
+
naming two ids is never attributed to either — it is treated as id-less.
|
|
34
|
+
|
|
35
|
+
## Constraint
|
|
36
|
+
|
|
37
|
+
> [!WARNING] `--execute` deletes files. Interactive runs confirm each bucket;
|
|
38
|
+
> `--yes` deletes the framework and closed-issue buckets only.
|
|
39
|
+
|
|
40
|
+
- Reads fail safe: an open issue, or one whose read fails, keeps its entry.
|
|
41
|
+
- `qa/`, `cache/`, `*.lock` and `signals.ndjson` at any depth are never
|
|
42
|
+
deleted.
|
|
43
|
+
- Project-scoped: the script exits 1 without deleting anything when the
|
|
44
|
+
resolved tempRoot is not inside the project root it was invoked from. It
|
|
45
|
+
never touches `$TMPDIR` or a sibling checkout.
|
|
46
|
+
|
|
47
|
+
## Steps
|
|
48
|
+
|
|
49
|
+
1. Preview and read the table (bucket, entry, size, age, reason):
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
node .agents/scripts/clean-temp.js
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
2. Delete, confirming each bucket:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
node .agents/scripts/clean-temp.js --execute
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Unattended, `--execute --yes` deletes the framework and closed-issue buckets
|
|
62
|
+
and reports the aged bucket as left for a human. `--json` emits the envelope
|
|
63
|
+
(per-bucket totals and `bytesReclaimed`) instead of the table.
|
|
64
|
+
|
|
65
|
+
Going forward, put ad-hoc scratch under `temp/scratch/story-<id>/` (or
|
|
66
|
+
`temp/scratch/` with no Story): a Story's landing reaps its scratch directory,
|
|
67
|
+
and the boot sweep age-floors the rest.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: >-
|
|
3
|
+
Reclaim disk from dead worktrees: list every worktree of this project as a
|
|
4
|
+
removal candidate (closed Story, merged branch, orphaned directory, detached
|
|
5
|
+
HEAD) or as kept with a reason, then remove candidates only on `--execute`.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# /clean-worktrees [--execute] [--yes] [--json]
|
|
9
|
+
|
|
10
|
+
Every Story worktree carries its own `node_modules`, so a worktree left behind
|
|
11
|
+
costs gigabytes. Workflow boot already removes `.worktrees/story-<id>` trees
|
|
12
|
+
whose Story is closed or `agent::done` (the boot sweep in
|
|
13
|
+
[`boot-sweep.js`](../scripts/boot-sweep.js)); `/clean-worktrees` is the
|
|
14
|
+
**recovery tool** for everything that sweep does not own — the backlog, trees
|
|
15
|
+
on non-Story branches, directories git no longer registers, and detached
|
|
16
|
+
trees such as the Claude Code app's `.claude/worktrees/*`.
|
|
17
|
+
|
|
18
|
+
The enumeration, classification and removal live in
|
|
19
|
+
[`clean-worktrees.js`](../scripts/clean-worktrees.js), which documents its own
|
|
20
|
+
flags: `node .agents/scripts/clean-worktrees.js --help`.
|
|
21
|
+
|
|
22
|
+
## Steps
|
|
23
|
+
|
|
24
|
+
1. **Preview.** Run `node .agents/scripts/clean-worktrees.js` (dry-run, the
|
|
25
|
+
default). It prints one row per worktree — class, path, size, branch/HEAD,
|
|
26
|
+
action — and removes nothing. Show the operator the table.
|
|
27
|
+
2. **Confirm.** Ask the operator which candidates to remove. Do not proceed
|
|
28
|
+
on your own judgment.
|
|
29
|
+
3. **Remove.** Re-run with `--execute`. In a terminal it asks per entry;
|
|
30
|
+
`--execute --yes` removes every non-`detached` candidate without asking.
|
|
31
|
+
`--json` emits the envelope, including `bytesReclaimed`.
|
|
32
|
+
|
|
33
|
+
## Classes
|
|
34
|
+
|
|
35
|
+
| Class | Candidate when |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| `closed-story` | `.worktrees/story-<id>` on `story-<id>` whose Story is closed or `agent::done`. |
|
|
38
|
+
| `merged-branch` | Any other branch whose PR is MERGED and whose HEAD is the merged head. |
|
|
39
|
+
| `orphan-dir` | A directory under `.worktrees/` that `git worktree list` does not register. |
|
|
40
|
+
| `detached` | A registered worktree with a detached HEAD. |
|
|
41
|
+
|
|
42
|
+
Everything else is **kept** with its reason: the main checkout, an open Story,
|
|
43
|
+
an unmerged branch, a dirty tree, unpushed commits, a tree in use.
|
|
44
|
+
|
|
45
|
+
## Constraint
|
|
46
|
+
|
|
47
|
+
> [!WARNING] `--execute` deletes worktree directories. Without it the script
|
|
48
|
+
> only previews.
|
|
49
|
+
|
|
50
|
+
- **Project-scoped.** Only worktrees inside the invoking checkout's project
|
|
51
|
+
root are candidates; one registered elsewhere is reported, never removed.
|
|
52
|
+
- **Unique work survives.** A dirty tree, or a HEAD no remote-tracking ref
|
|
53
|
+
contains, is never removed.
|
|
54
|
+
- **Live trees survive.** The tree this process runs from is never removed;
|
|
55
|
+
on macOS and Linux a tree any live process uses is refused outright.
|
|
56
|
+
- **`detached` needs a person.** A detached tree carries no Story label and a
|
|
57
|
+
live session may own it (a blanket prune once destroyed a live delivery's
|
|
58
|
+
worktree), so it is removed only on a per-entry interactive yes — never
|
|
59
|
+
under `--yes`.
|
|
60
|
+
- **Removal goes through the worktree removal seam** (Windows lock retry and
|
|
61
|
+
the pending-cleanup hand-off), never raw deletion.
|
|
62
|
+
- **Branches are untouched.** Deleting local or remote branches is
|
|
63
|
+
[`/clean-git`](clean-git.md)'s job.
|
|
@@ -77,7 +77,7 @@ node .agents/scripts/boot-sweep.js \
|
|
|
77
77
|
--current "$(git rev-parse --abbrev-ref HEAD)"
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
The safe subset of the `/git
|
|
80
|
+
The safe subset of the `/clean-git` phases: fast-forwards the base branch,
|
|
81
81
|
prunes stale remote-tracking refs, and reaps merged branches. It never
|
|
82
82
|
touches the stash, never reaps a candidate with unpushed work / dirty
|
|
83
83
|
worktree / open parent ticket, and always exits `0` — a failed sweep is
|
|
@@ -32,8 +32,8 @@ per-criterion, mid-delivery, and evaluates the actual work product.
|
|
|
32
32
|
authors the Story's verdict, and it covers **every** `acceptance[]` item in
|
|
33
33
|
one file. Which pass is named by the ceremony decision
|
|
34
34
|
(`verdictOwner: 'fresh-critic' | 'inline-self-eval'` from
|
|
35
|
-
`resolveCeremonyForRisk`),
|
|
36
|
-
|
|
35
|
+
`resolveCeremonyForRisk`), which follows the **ceremony profile
|
|
36
|
+
alone**:
|
|
37
37
|
|
|
38
38
|
> ```bash
|
|
39
39
|
> node <main-repo>/.agents/scripts/ceremony-derive.js --story <storyId> --cwd <workCwd>
|
|
@@ -43,9 +43,8 @@ per-criterion, mid-delivery, and evaluates the actual work product.
|
|
|
43
43
|
> classes **for review depth**, and resolves the owner (`mode`, `reason`,
|
|
44
44
|
> `verdictOwner`): **`minimal` / `standard` → `inline`** (the default — you
|
|
45
45
|
> author the verdict yourself), **`strict` → `fresh`** (dispatch the
|
|
46
|
-
> maker-blind critic). The derived level
|
|
47
|
-
>
|
|
48
|
-
> any sensitive path.
|
|
46
|
+
> maker-blind critic). The derived level feeds `review-depth.js`, not
|
|
47
|
+
> this decision; review depth resolves `deep` for any sensitive path.
|
|
49
48
|
|
|
50
49
|
**Never run both**, and never run a preliminary self-assessment before
|
|
51
50
|
dispatching a fresh critic — the redundant pre-pass buys no measurable
|
|
@@ -72,9 +71,10 @@ per-criterion, mid-delivery, and evaluates the actual work product.
|
|
|
72
71
|
> system prompt, no entry-doc @-closure) carrying the maker-blind
|
|
73
72
|
> invariant and the verdict schema standalone. With the kill-switch off
|
|
74
73
|
> (`roleScopedAgents: false`), fall back to
|
|
75
|
-
> `subagent_type: general-purpose`.
|
|
76
|
-
>
|
|
77
|
-
> any harness that carries `Agent` into
|
|
74
|
+
> `subagent_type: general-purpose`. Under sub-agent dispatch this loop
|
|
75
|
+
> runs inside a `story-worker`, so the critic sits at nesting depth 2
|
|
76
|
+
> (depth 1 inline) — supported by any harness that carries `Agent` into
|
|
77
|
+
> sub-agents (Claude Code ≥ 2.1.202).
|
|
78
78
|
|
|
79
79
|
Whichever pass owns it, the verdict:
|
|
80
80
|
+ Inspects the **change set it was handed** — the one `files` list above —
|
|
@@ -95,7 +95,7 @@ per-criterion, mid-delivery, and evaluates the actual work product.
|
|
|
95
95
|
without being respawned; a stale or absent stamp reports `spawn: true` and
|
|
96
96
|
the command runs for real. The credited run itself is stated once, in
|
|
97
97
|
[`deliver-digest.md`](deliver-digest.md) § 5.
|
|
98
|
-
+ Emits **one** verdict file under `temp
|
|
98
|
+
+ Emits **one** verdict file under `temp/scratch/story-<id>/` conforming to
|
|
99
99
|
[`acceptance-eval-verdict.schema.json`](../../schemas/acceptance-eval-verdict.schema.json):
|
|
100
100
|
one `{ index, criterion, verdict: met|partial|unmet, evidence,
|
|
101
101
|
verifyEvidence[] }` record per `acceptance[]` item, in acceptance-array
|
|
@@ -137,4 +137,5 @@ per-criterion, mid-delivery, and evaluates the actual work product.
|
|
|
137
137
|
(transition to `agent::blocked`) and post a `friction` comment naming the
|
|
138
138
|
unmet criteria and their evidence. Never silently proceed to close.
|
|
139
139
|
|
|
140
|
-
Write the verdict under `temp
|
|
140
|
+
Write the verdict under `temp/scratch/story-<id>/` only — a scratch artifact
|
|
141
|
+
the Story's landing reaps.
|