mandrel 2.64.0 → 2.66.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 +8 -7
- package/.agents/agents/auditor.md +20 -20
- package/.agents/agents/plan-critic.md +8 -7
- package/.agents/agents/story-worker.md +7 -7
- package/.agents/audit-checklists/quality.md +3 -0
- package/.agents/docs/agentrc-reference.json +1 -9
- package/.agents/docs/configuration.md +8 -7
- package/.agents/docs/execution-reference.md +27 -5
- package/.agents/instructions.md +10 -12
- package/.agents/rules/ci-remediation.md +3 -3
- package/.agents/rules/gherkin-standards.md +3 -2
- package/.agents/rules/git-conventions-reference.md +12 -3
- package/.agents/rules/git-conventions.md +9 -7
- package/.agents/rules/testing-standards.md +8 -7
- package/.agents/runtime-deps.json +1 -1
- package/.agents/schemas/agentrc.schema.json +6 -13
- package/.agents/schemas/audit-rules.schema.json +1 -1
- package/.agents/schemas/story-deliver-terminal.schema.json +5 -0
- package/.agents/scripts/bootstrap.js +102 -91
- package/.agents/scripts/check-context-budget.js +1 -1
- package/.agents/scripts/lib/ITicketingProvider.js +1 -3
- package/.agents/scripts/lib/audit-suite/findings.js +1 -17
- package/.agents/scripts/lib/audit-suite/frontmatter.js +0 -28
- package/.agents/scripts/lib/audit-suite/index.js +0 -6
- package/.agents/scripts/lib/audit-suite/selector.js +0 -31
- package/.agents/scripts/lib/baselines/duplication-scanner.js +17 -7
- package/.agents/scripts/lib/bootstrap/agents-md-fold.js +156 -0
- package/.agents/scripts/lib/bootstrap/commit-push.js +1 -1
- package/.agents/scripts/lib/bootstrap/manifest.js +2 -2
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +91 -107
- 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/review-chain-default.js +13 -0
- package/.agents/scripts/lib/config-settings-schema-delivery.js +2 -2
- package/.agents/scripts/lib/config-settings-schema-quality.js +11 -13
- package/.agents/scripts/lib/doc-tiers.js +25 -6
- 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/metrics-ledger.js +0 -72
- package/.agents/scripts/lib/observability/runtime-friction.js +2 -0
- package/.agents/scripts/lib/observability/signal-validator.js +17 -5
- package/.agents/scripts/lib/orchestration/code-review.js +33 -6
- package/.agents/scripts/lib/orchestration/epic-rollup.js +29 -12
- package/.agents/scripts/lib/orchestration/merge-block-class.js +20 -4
- package/.agents/scripts/lib/orchestration/merge-poll.js +41 -22
- package/.agents/scripts/lib/orchestration/plan-metrics.js +76 -63
- package/.agents/scripts/lib/orchestration/required-checks.js +147 -0
- package/.agents/scripts/lib/orchestration/review-providers/code-review.js +203 -0
- package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +29 -4
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +3 -2
- package/.agents/scripts/lib/orchestration/run-epilogue.js +6 -0
- package/.agents/scripts/lib/orchestration/single-story-close/failed-terminal.js +1 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +2 -12
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +370 -268
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +21 -7
- package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +112 -82
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +4 -0
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +393 -313
- package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +12 -87
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +3 -0
- package/.agents/scripts/lib/orchestration/ticket-validator.js +19 -36
- package/.agents/scripts/lib/signals/detectors/common.js +63 -51
- package/.agents/scripts/lib/templates/decomposer-prompts.js +5 -24
- package/.agents/scripts/lib/transpile.js +28 -3
- package/.agents/scripts/providers/github/issues.js +14 -23
- package/.agents/scripts/single-story-close.js +10 -2
- package/.agents/scripts/single-story-confirm-merge.js +267 -238
- package/.agents/scripts/sync-claude-agents.js +1 -1
- 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/audit-quality.md +42 -7
- package/.agents/workflows/helpers/acceptance-self-eval.md +9 -9
- package/.agents/workflows/helpers/audit-lens-core.md +30 -57
- package/.agents/workflows/helpers/code-review.md +15 -38
- package/.agents/workflows/helpers/deliver-digest.md +2 -2
- package/.agents/workflows/helpers/deliver-reference.md +7 -3
- package/.agents/workflows/helpers/deliver-story.md +9 -1
- package/.agents/workflows/helpers/parallel-tooling.md +16 -18
- package/.agents/workflows/helpers/plan-reference.md +9 -8
- package/.agents/workflows/mandrel-deliver.md +3 -2
- package/.agents/workflows/mandrel-plan.md +11 -7
- package/.agents/workflows/mandrel-update.md +5 -3
- package/docs/CHANGELOG.md +57 -0
- package/lib/cli/claude-code-version.js +73 -0
- package/lib/cli/doctor.js +2 -2
- package/lib/cli/guarded-sync.js +87 -0
- package/lib/cli/registry.js +9 -0
- package/lib/cli/sync-agents.js +9 -92
- package/lib/cli/sync-commands.js +9 -101
- package/lib/cli/uninstall.js +37 -9
- package/lib/migrations/index.js +2 -0
- package/lib/migrations/steps/2.65.0-fold-claude-md-into-agents-md.js +38 -0
- package/package.json +3 -2
- package/.agents/scripts/lib/audit-suite/lens-diff-floor.js +0 -99
- package/.agents/scripts/lib/audit-suite/runner.js +0 -205
- package/.agents/scripts/lib/audit-suite/substitutions.js +0 -96
- package/.agents/scripts/lib/audit-suite/workflow-loader.js +0 -37
- package/.agents/scripts/lib/orchestration/story-close/phases/local-lens-review.js +0 -234
|
@@ -25,6 +25,17 @@ function resolveFlag(paramValue, parsedValue) {
|
|
|
25
25
|
return paramValue ?? parsedValue;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* A boolean flag: param beats parsed arg, absent reads as `false`.
|
|
30
|
+
*
|
|
31
|
+
* @param {unknown} paramValue
|
|
32
|
+
* @param {unknown} parsedValue
|
|
33
|
+
* @returns {boolean}
|
|
34
|
+
*/
|
|
35
|
+
function booleanFlag(paramValue, parsedValue) {
|
|
36
|
+
return !!resolveFlag(paramValue, parsedValue);
|
|
37
|
+
}
|
|
38
|
+
|
|
28
39
|
/**
|
|
29
40
|
* Junk reads as absent (so the config default applies), never coerced.
|
|
30
41
|
*
|
|
@@ -88,7 +99,7 @@ const RETIRED_FLAGS = Object.freeze({
|
|
|
88
99
|
* @param {string[]} argv
|
|
89
100
|
* @throws {Error}
|
|
90
101
|
*/
|
|
91
|
-
function assertNoRetiredFlags(argv) {
|
|
102
|
+
export function assertNoRetiredFlags(argv) {
|
|
92
103
|
for (const [flag, why] of Object.entries(RETIRED_FLAGS)) {
|
|
93
104
|
const present = argv.some((a) => a === flag || a.startsWith(`${flag}=`));
|
|
94
105
|
if (!present) continue;
|
|
@@ -102,8 +113,8 @@ function assertNoRetiredFlags(argv) {
|
|
|
102
113
|
/**
|
|
103
114
|
* Returns raw wait-for-merge intent; the runner resolves it after the arm.
|
|
104
115
|
*
|
|
105
|
-
* @param {{ storyIdParam, cwdParam, skipValidationParam, skipSyncParam, noAutoMergeParam, waitForMergeParam, noWaitForMergeParam, maxWaitSecondsParam, mergeWatchModeParam, rerunAdvisoryParam, overrideReviewBlockParam }} raw
|
|
106
|
-
* @returns {{ storyId, cwd, skipValidation, skipSync, noAutoMerge, waitForMergeExplicit, noWaitForMerge, maxWaitSeconds, mergeWatchMode, rerunAdvisory, overrideReviewBlock }}
|
|
116
|
+
* @param {{ storyIdParam, cwdParam, skipValidationParam, skipSyncParam, noAutoMergeParam, waitForMergeParam, noWaitForMergeParam, maxWaitSecondsParam, mergeWatchModeParam, rerunAdvisoryParam, overrideReviewBlockParam, workerTokensParam }} raw
|
|
117
|
+
* @returns {{ storyId, cwd, skipValidation, skipSync, noAutoMerge, waitForMergeExplicit, noWaitForMerge, maxWaitSeconds, mergeWatchMode, rerunAdvisory, overrideReviewBlock, workerTokens }}
|
|
107
118
|
*/
|
|
108
119
|
export function parseCloseOptions({
|
|
109
120
|
storyIdParam,
|
|
@@ -117,6 +128,7 @@ export function parseCloseOptions({
|
|
|
117
128
|
mergeWatchModeParam,
|
|
118
129
|
rerunAdvisoryParam,
|
|
119
130
|
overrideReviewBlockParam,
|
|
131
|
+
workerTokensParam,
|
|
120
132
|
}) {
|
|
121
133
|
// An injecting caller never reads argv (the host's flags are not its
|
|
122
134
|
// business), so `parsed` stays empty and the retired-flag guard is skipped.
|
|
@@ -145,17 +157,19 @@ export function parseCloseOptions({
|
|
|
145
157
|
resolveFlag(mergeWatchModeParam, parsed.mergeWatchMode),
|
|
146
158
|
),
|
|
147
159
|
rerunAdvisory: intAtLeast(rerunAdvisory, 0),
|
|
148
|
-
skipValidation:
|
|
149
|
-
skipSync:
|
|
150
|
-
noAutoMerge:
|
|
160
|
+
skipValidation: booleanFlag(skipValidationParam, parsed.skipValidation),
|
|
161
|
+
skipSync: booleanFlag(skipSyncParam, parsed.skipSync),
|
|
162
|
+
noAutoMerge: booleanFlag(noAutoMergeParam, parsed.noAutoMerge),
|
|
151
163
|
waitForMergeExplicit:
|
|
152
164
|
typeof waitForMergeExplicit === 'boolean'
|
|
153
165
|
? waitForMergeExplicit
|
|
154
166
|
: undefined,
|
|
155
|
-
noWaitForMerge:
|
|
167
|
+
noWaitForMerge: booleanFlag(noWaitForMergeParam, parsed.noWaitForMerge),
|
|
156
168
|
// Both doors validate, so neither can arm a reasonless override.
|
|
157
169
|
overrideReviewBlock: parseOverrideReviewBlock(
|
|
158
170
|
resolveFlag(overrideReviewBlockParam, parsed.overrideReviewBlock),
|
|
159
171
|
),
|
|
172
|
+
// Raw: validated best-effort by the runner, never thrown on.
|
|
173
|
+
workerTokens: resolveFlag(workerTokensParam, parsed.workerTokens),
|
|
160
174
|
};
|
|
161
175
|
}
|
|
@@ -282,11 +282,61 @@ async function stepPlanRunLabelReap({
|
|
|
282
282
|
}
|
|
283
283
|
|
|
284
284
|
/**
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
|
|
289
|
-
|
|
285
|
+
* The local-checkout mutations (ref reap first, then fast-forward) under a
|
|
286
|
+
* cross-process lock on the main checkout, since concurrent closes race on
|
|
287
|
+
* the base ref and worktree registry. Best-effort: on timeout they run anyway.
|
|
288
|
+
*/
|
|
289
|
+
async function runLockedLocalSteps({
|
|
290
|
+
storyId,
|
|
291
|
+
storyBranch,
|
|
292
|
+
baseBranch,
|
|
293
|
+
cwd,
|
|
294
|
+
config,
|
|
295
|
+
progress,
|
|
296
|
+
gitSpawnFn,
|
|
297
|
+
planFastForwardFn,
|
|
298
|
+
executeFastForwardFn,
|
|
299
|
+
acquireLockWithWaitFn,
|
|
300
|
+
}) {
|
|
301
|
+
const lockCfg = config?.delivery?.postLandLock ?? {};
|
|
302
|
+
const lock = await acquireLockWithWaitFn({
|
|
303
|
+
lockPath: postLandLockPath(cwd),
|
|
304
|
+
waitMs: lockCfg.waitMs,
|
|
305
|
+
pollMs: lockCfg.pollMs,
|
|
306
|
+
timeoutMs: lockCfg.timeoutMs,
|
|
307
|
+
ownerId: `post-land-${storyId}`,
|
|
308
|
+
});
|
|
309
|
+
if (!lock.acquired) {
|
|
310
|
+
progress?.(
|
|
311
|
+
'POST-LAND',
|
|
312
|
+
`⚠️ post-land lock not acquired (${lock.reason}); proceeding unserialized.`,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
const refCleanup = await step(
|
|
317
|
+
() => stepRefCleanup({ cwd, storyBranch, progress, gitSpawnFn }),
|
|
318
|
+
{ name: 'local ref cleanup', progress },
|
|
319
|
+
);
|
|
320
|
+
const baseFastForward = await step(
|
|
321
|
+
() =>
|
|
322
|
+
stepBaseFastForward({
|
|
323
|
+
cwd,
|
|
324
|
+
baseBranch,
|
|
325
|
+
progress,
|
|
326
|
+
planFastForwardFn,
|
|
327
|
+
executeFastForwardFn,
|
|
328
|
+
}),
|
|
329
|
+
{ name: 'base fast-forward', progress },
|
|
330
|
+
);
|
|
331
|
+
return { refCleanup, baseFastForward };
|
|
332
|
+
} finally {
|
|
333
|
+
if (lock.acquired) lock.release();
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Run the post-land tail: friction markers, then the GitHub steps and the
|
|
339
|
+
* locked local steps concurrently, then temp purge, then lease release.
|
|
290
340
|
*
|
|
291
341
|
* @param {object} args
|
|
292
342
|
* @param {number} args.storyId
|
|
@@ -348,87 +398,67 @@ export async function runPostLandTail({
|
|
|
348
398
|
config,
|
|
349
399
|
});
|
|
350
400
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
provider,
|
|
356
|
-
config,
|
|
357
|
-
cwd,
|
|
358
|
-
progress,
|
|
359
|
-
captureStoryFollowUpsFn,
|
|
360
|
-
}),
|
|
361
|
-
{ name: 'follow-up capture', progress },
|
|
362
|
-
);
|
|
363
|
-
const statusResync = await step(
|
|
364
|
-
() =>
|
|
365
|
-
stepStatusResync({
|
|
366
|
-
storyId,
|
|
367
|
-
provider,
|
|
368
|
-
config,
|
|
369
|
-
progress,
|
|
370
|
-
reassertStatusColumnFn,
|
|
371
|
-
}),
|
|
372
|
-
{ name: 'status-column resync', progress },
|
|
373
|
-
);
|
|
374
|
-
await step(
|
|
375
|
-
() =>
|
|
376
|
-
stepPlanRunLabelReap({
|
|
377
|
-
storyId,
|
|
378
|
-
provider,
|
|
379
|
-
progress,
|
|
380
|
-
reapPlanRunLabelsForStoryFn,
|
|
381
|
-
}),
|
|
382
|
-
{ name: 'plan-run label reap', progress },
|
|
383
|
-
);
|
|
384
|
-
|
|
385
|
-
const epicRollup = await step(
|
|
386
|
-
() =>
|
|
387
|
-
stepEpicRollup({
|
|
388
|
-
storyId,
|
|
389
|
-
provider,
|
|
390
|
-
config,
|
|
391
|
-
progress,
|
|
392
|
-
rollUpEpicForStoryFn,
|
|
393
|
-
}),
|
|
394
|
-
{ name: 'epic rollup', progress },
|
|
395
|
-
);
|
|
396
|
-
|
|
397
|
-
const lockCfg = config?.delivery?.postLandLock ?? {};
|
|
398
|
-
const lock = await acquireLockWithWaitFn({
|
|
399
|
-
lockPath: postLandLockPath(cwd),
|
|
400
|
-
waitMs: lockCfg.waitMs,
|
|
401
|
-
pollMs: lockCfg.pollMs,
|
|
402
|
-
timeoutMs: lockCfg.timeoutMs,
|
|
403
|
-
ownerId: `post-land-${storyId}`,
|
|
404
|
-
});
|
|
405
|
-
if (!lock.acquired) {
|
|
406
|
-
progress?.(
|
|
407
|
-
'POST-LAND',
|
|
408
|
-
`⚠️ post-land lock not acquired (${lock.reason}); proceeding unserialized.`,
|
|
409
|
-
);
|
|
410
|
-
}
|
|
411
|
-
let refCleanup;
|
|
412
|
-
let baseFastForward;
|
|
413
|
-
try {
|
|
414
|
-
refCleanup = await step(
|
|
415
|
-
() => stepRefCleanup({ cwd, storyBranch, progress, gitSpawnFn }),
|
|
416
|
-
{ name: 'local ref cleanup', progress },
|
|
417
|
-
);
|
|
418
|
-
baseFastForward = await step(
|
|
401
|
+
// The GitHub steps are independent of each other and of the local
|
|
402
|
+
// lock-held mutations, so all of them run concurrently.
|
|
403
|
+
const [followUps, statusResync, , epicRollup, local] = await Promise.all([
|
|
404
|
+
step(
|
|
419
405
|
() =>
|
|
420
|
-
|
|
406
|
+
stepFollowUps({
|
|
407
|
+
storyId,
|
|
408
|
+
provider,
|
|
409
|
+
config,
|
|
421
410
|
cwd,
|
|
422
|
-
baseBranch,
|
|
423
411
|
progress,
|
|
424
|
-
|
|
425
|
-
executeFastForwardFn,
|
|
412
|
+
captureStoryFollowUpsFn,
|
|
426
413
|
}),
|
|
427
|
-
{ name: '
|
|
428
|
-
)
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
414
|
+
{ name: 'follow-up capture', progress },
|
|
415
|
+
),
|
|
416
|
+
step(
|
|
417
|
+
() =>
|
|
418
|
+
stepStatusResync({
|
|
419
|
+
storyId,
|
|
420
|
+
provider,
|
|
421
|
+
config,
|
|
422
|
+
progress,
|
|
423
|
+
reassertStatusColumnFn,
|
|
424
|
+
}),
|
|
425
|
+
{ name: 'status-column resync', progress },
|
|
426
|
+
),
|
|
427
|
+
step(
|
|
428
|
+
() =>
|
|
429
|
+
stepPlanRunLabelReap({
|
|
430
|
+
storyId,
|
|
431
|
+
provider,
|
|
432
|
+
progress,
|
|
433
|
+
reapPlanRunLabelsForStoryFn,
|
|
434
|
+
}),
|
|
435
|
+
{ name: 'plan-run label reap', progress },
|
|
436
|
+
),
|
|
437
|
+
step(
|
|
438
|
+
() =>
|
|
439
|
+
stepEpicRollup({
|
|
440
|
+
storyId,
|
|
441
|
+
provider,
|
|
442
|
+
config,
|
|
443
|
+
progress,
|
|
444
|
+
rollUpEpicForStoryFn,
|
|
445
|
+
}),
|
|
446
|
+
{ name: 'epic rollup', progress },
|
|
447
|
+
),
|
|
448
|
+
runLockedLocalSteps({
|
|
449
|
+
storyId,
|
|
450
|
+
storyBranch,
|
|
451
|
+
baseBranch,
|
|
452
|
+
cwd,
|
|
453
|
+
config,
|
|
454
|
+
progress,
|
|
455
|
+
gitSpawnFn,
|
|
456
|
+
planFastForwardFn,
|
|
457
|
+
executeFastForwardFn,
|
|
458
|
+
acquireLockWithWaitFn,
|
|
459
|
+
}),
|
|
460
|
+
]);
|
|
461
|
+
const { refCleanup, baseFastForward } = local;
|
|
432
462
|
|
|
433
463
|
// After every step that reads the temp artifacts; `signals.ndjson` survives.
|
|
434
464
|
const tempPurge = await step(
|
|
@@ -65,6 +65,7 @@ async function postAuditRecord({ post, surface }) {
|
|
|
65
65
|
* prUrl: string,
|
|
66
66
|
* prNumber: number|null,
|
|
67
67
|
* criticalCount: number,
|
|
68
|
+
* criticalByProvider?: Record<string, number>,
|
|
68
69
|
* reason: string,
|
|
69
70
|
* config?: object,
|
|
70
71
|
* emitFrictionFn?: typeof emitRuntimeFriction,
|
|
@@ -77,6 +78,7 @@ export async function handleOverriddenReviewBlock({
|
|
|
77
78
|
prUrl,
|
|
78
79
|
prNumber,
|
|
79
80
|
criticalCount,
|
|
81
|
+
criticalByProvider = {},
|
|
80
82
|
reason,
|
|
81
83
|
config,
|
|
82
84
|
emitFrictionFn = emitRuntimeFriction,
|
|
@@ -104,6 +106,8 @@ export async function handleOverriddenReviewBlock({
|
|
|
104
106
|
details: {
|
|
105
107
|
prUrl,
|
|
106
108
|
criticalCount,
|
|
109
|
+
// The false-positive proxy: attributed like the halt it rejected.
|
|
110
|
+
criticalByProvider,
|
|
107
111
|
reason: reason.slice(0, REASON_SIGNAL_LIMIT),
|
|
108
112
|
},
|
|
109
113
|
config,
|