mandrel 1.62.0 → 1.64.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/scripts/agents-bootstrap-github.js +40 -48
- package/.agents/scripts/bootstrap.js +74 -60
- package/.agents/scripts/check-action-pinning.js +260 -0
- package/.agents/scripts/check-arch-cycles.js +38 -14
- package/.agents/scripts/epic-deliver-prepare.js +149 -104
- package/.agents/scripts/lib/baseline-snapshot.js +245 -141
- package/.agents/scripts/lib/bootstrap/branch-protection.js +8 -8
- package/.agents/scripts/lib/bootstrap/gh-preflight.js +3 -3
- package/.agents/scripts/lib/bootstrap/hitl-confirm.js +2 -2
- package/.agents/scripts/lib/bootstrap/merge-methods.js +7 -7
- package/.agents/scripts/lib/bootstrap/preflight.js +18 -15
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +5 -5
- package/.agents/scripts/lib/bootstrap/prompt.js +5 -1
- package/.agents/scripts/lib/detect-package-manager.js +2 -2
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +171 -137
- package/.agents/scripts/lib/onboard/init-tail.js +60 -69
- package/.agents/scripts/lib/orchestration/code-review.js +206 -168
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +71 -5
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +16 -2
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +101 -1
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +20 -42
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +12 -32
- package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +97 -60
- package/.agents/scripts/lib/orchestration/model-attribution.js +73 -45
- package/.agents/scripts/lib/orchestration/review-providers/parse-findings.js +97 -49
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +73 -69
- package/.agents/scripts/lib/orchestration/story-close-recovery.js +109 -79
- package/.agents/scripts/lib/signals/detectors/common.js +107 -0
- package/.agents/scripts/lib/signals/detectors/hotspot.js +12 -18
- package/.agents/scripts/lib/signals/detectors/retry.js +3 -40
- package/.agents/scripts/lib/signals/detectors/rework.js +3 -40
- package/.agents/scripts/lib/story-body/story-body.js +102 -76
- package/.agents/scripts/providers/github/blocked-by-add.js +252 -0
- package/.agents/scripts/providers/github/tickets.js +1 -1
- package/.agents/scripts/single-story-init.js +16 -3
- package/.agents/workflows/audit-architecture.md +9 -0
- package/.agents/workflows/helpers/deliver-stories.md +24 -2
- package/.agents/workflows/helpers/single-story-deliver.md +84 -1
- package/README.md +1 -1
- package/docs/CHANGELOG.md +43 -0
- package/lib/cli/init.js +66 -21
- package/lib/cli/sync.js +3 -3
- package/package.json +1 -1
- package/.agents/scripts/lib/onboard/detect-stack.js +0 -300
|
@@ -84,25 +84,22 @@ async function verifyApiAccess(provider) {
|
|
|
84
84
|
// target repo. Anything else (auth, scope, transport) is fatal.
|
|
85
85
|
if (!isApiAccessNotFoundError(err)) {
|
|
86
86
|
throw new Error(
|
|
87
|
-
`[
|
|
87
|
+
`[Bootstrap] API access verification failed: ${err.message}`,
|
|
88
88
|
);
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
async function ensureLabels(provider, log) {
|
|
94
|
-
log(`[
|
|
94
|
+
log(`[Bootstrap] Ensuring ${LABEL_TAXONOMY.length} labels...`);
|
|
95
95
|
const labels = await provider.ensureLabels(LABEL_TAXONOMY);
|
|
96
96
|
const missing = Array.isArray(labels.missing) ? labels.missing : [];
|
|
97
97
|
log(
|
|
98
|
-
`[
|
|
98
|
+
`[Bootstrap] Labels — created: ${labels.created.length}, skipped: ${labels.skipped.length}, missing: ${missing.length}`,
|
|
99
99
|
);
|
|
100
|
-
if (labels.created.length > 0) {
|
|
101
|
-
log(`[bootstrap] Created: ${labels.created.join(', ')}`);
|
|
102
|
-
}
|
|
103
100
|
if (missing.length > 0) {
|
|
104
101
|
log(
|
|
105
|
-
`[
|
|
102
|
+
`[Bootstrap] ⚠️ ${missing.length} label(s) were reported as created/skipped but are NOT present on the remote: ${missing.join(', ')}. Re-run bootstrap or create them manually with \`gh label create\`.`,
|
|
106
103
|
);
|
|
107
104
|
}
|
|
108
105
|
return labels;
|
|
@@ -119,19 +116,19 @@ async function resolveProject(provider, providerConfig, log) {
|
|
|
119
116
|
const result = await provider.resolveOrCreateProject();
|
|
120
117
|
if (result.scopesMissing) {
|
|
121
118
|
log(
|
|
122
|
-
`[
|
|
119
|
+
`[Bootstrap] Projects V2: token lacks the "project" scope — skipping board provisioning. ${PROJECTS_DOC_POINTER}`,
|
|
123
120
|
);
|
|
124
121
|
return fallback(true);
|
|
125
122
|
}
|
|
126
123
|
const projectNumber = result.projectNumber ?? null;
|
|
127
124
|
const created = !!result.created;
|
|
128
125
|
log(
|
|
129
|
-
`[
|
|
126
|
+
`[Bootstrap] ${created ? 'Created' : 'Using'} Project V2 #${projectNumber}.`,
|
|
130
127
|
);
|
|
131
128
|
return { projectNumber, created, skipped: false, scopesMissing: false };
|
|
132
129
|
} catch (err) {
|
|
133
130
|
log(
|
|
134
|
-
`[
|
|
131
|
+
`[Bootstrap] Projects V2 resolution failed: ${err.message}. ${PROJECTS_DOC_POINTER}`,
|
|
135
132
|
);
|
|
136
133
|
return fallback(false);
|
|
137
134
|
}
|
|
@@ -142,17 +139,17 @@ async function ensureStatusField(provider, log) {
|
|
|
142
139
|
const statusField = await provider.ensureStatusField(STATUS_FIELD_OPTIONS);
|
|
143
140
|
if (statusField.status === 'scopes-missing') {
|
|
144
141
|
log(
|
|
145
|
-
`[
|
|
142
|
+
`[Bootstrap] Projects V2 Status field: insufficient scopes. ${PROJECTS_DOC_POINTER}`,
|
|
146
143
|
);
|
|
147
144
|
} else {
|
|
148
145
|
const addedSuffix = statusField.added.length
|
|
149
146
|
? ` (added: ${statusField.added.join(', ')})`
|
|
150
147
|
: '';
|
|
151
|
-
log(`[
|
|
148
|
+
log(`[Bootstrap] Status field — ${statusField.status}${addedSuffix}`);
|
|
152
149
|
}
|
|
153
150
|
return statusField;
|
|
154
151
|
} catch (err) {
|
|
155
|
-
log(`[
|
|
152
|
+
log(`[Bootstrap] Status field provisioning failed: ${err.message}`);
|
|
156
153
|
return { status: 'skipped', added: [] };
|
|
157
154
|
}
|
|
158
155
|
}
|
|
@@ -162,16 +159,16 @@ async function ensureViews(provider, log) {
|
|
|
162
159
|
const views = await provider.ensureProjectViews(PROJECT_VIEW_DEFS);
|
|
163
160
|
if (views.unavailable) {
|
|
164
161
|
log(
|
|
165
|
-
`[
|
|
162
|
+
`[Bootstrap] Projects V2 Views unavailable — skipped ${views.skipped.join(', ')}.${views.error ? ` (${views.error})` : ''} ${PROJECTS_DOC_POINTER}`,
|
|
166
163
|
);
|
|
167
164
|
} else {
|
|
168
165
|
log(
|
|
169
|
-
`[
|
|
166
|
+
`[Bootstrap] Views — created: ${views.created.length}, skipped: ${views.skipped.length}`,
|
|
170
167
|
);
|
|
171
168
|
}
|
|
172
169
|
return views;
|
|
173
170
|
} catch (err) {
|
|
174
|
-
log(`[
|
|
171
|
+
log(`[Bootstrap] Views provisioning failed: ${err.message}`);
|
|
175
172
|
return { created: [], skipped: [], unavailable: false };
|
|
176
173
|
}
|
|
177
174
|
}
|
|
@@ -203,13 +200,13 @@ async function auditAndOptionallyReapWorkflows(
|
|
|
203
200
|
projectId = await resolveProjectIdByNumber({ provider, projectNumber });
|
|
204
201
|
} catch (err) {
|
|
205
202
|
log(
|
|
206
|
-
`[
|
|
203
|
+
`[Bootstrap] Workflow audit: could not resolve project id — ${err.message}.`,
|
|
207
204
|
);
|
|
208
205
|
return { skipped: true, reason: 'project-id-unresolved' };
|
|
209
206
|
}
|
|
210
207
|
if (!projectId) {
|
|
211
208
|
log(
|
|
212
|
-
`[
|
|
209
|
+
`[Bootstrap] Workflow audit: project #${projectNumber} not visible to viewer — skipping.`,
|
|
213
210
|
);
|
|
214
211
|
return { skipped: true, reason: 'project-not-visible' };
|
|
215
212
|
}
|
|
@@ -217,45 +214,40 @@ async function auditAndOptionallyReapWorkflows(
|
|
|
217
214
|
try {
|
|
218
215
|
audit = await auditProjectWorkflows({ provider, projectId });
|
|
219
216
|
} catch (err) {
|
|
220
|
-
log(`[
|
|
217
|
+
log(`[Bootstrap] Workflow audit failed: ${err.message} — skipping.`);
|
|
221
218
|
return { skipped: true, reason: 'audit-failed', error: err.message };
|
|
222
219
|
}
|
|
223
|
-
log(`[
|
|
220
|
+
log(`[Bootstrap] Workflow audit — ${formatAuditSummary(audit)}.`);
|
|
224
221
|
if (audit.conflicting.length === 0) {
|
|
225
222
|
return { audit, reaped: [], action: 'no-conflicts' };
|
|
226
223
|
}
|
|
227
224
|
const names = audit.conflicting.map((w) => w.name).join(', ');
|
|
228
225
|
if (!reap) {
|
|
229
226
|
log(
|
|
230
|
-
`[
|
|
231
|
-
`
|
|
232
|
-
`
|
|
233
|
-
`
|
|
234
|
-
`(a) re-run with --reap-conflicting-workflows to delete them, ` +
|
|
235
|
-
`or (b) toggle them off in the GitHub UI under ` +
|
|
236
|
-
`Project → Workflows. The orchestrator's post-merge ` +
|
|
237
|
-
`resync-status-column.js CLI defends against both unless you ` +
|
|
238
|
-
`also disable that step.`,
|
|
227
|
+
`[Bootstrap] ⚠️ Conflicting Projects V2 workflows enabled: ${names}. ` +
|
|
228
|
+
`They can leave closed Stories stuck at "In Progress". Fix: re-run ` +
|
|
229
|
+
`with --reap-conflicting-workflows, or disable them under ` +
|
|
230
|
+
`Project → Workflows.`,
|
|
239
231
|
);
|
|
240
232
|
return { audit, reaped: [], action: 'warn-only' };
|
|
241
233
|
}
|
|
242
234
|
log(
|
|
243
|
-
`[
|
|
235
|
+
`[Bootstrap] Reaping ${audit.conflicting.length} conflicting workflow(s): ${names}...`,
|
|
244
236
|
);
|
|
245
237
|
const { reaped } = await reapConflictingWorkflows({ provider, audit });
|
|
246
238
|
log(
|
|
247
|
-
`[
|
|
239
|
+
`[Bootstrap] ✅ Deleted ${reaped.length} workflow(s): ${reaped.map((r) => r.name).join(', ')}.`,
|
|
248
240
|
);
|
|
249
241
|
return { audit, reaped, action: 'reaped' };
|
|
250
242
|
}
|
|
251
243
|
|
|
252
244
|
async function ensureProjectFields(provider, project, log) {
|
|
253
245
|
log(
|
|
254
|
-
`[
|
|
246
|
+
`[Bootstrap] Ensuring ${PROJECT_FIELD_DEFS.length} project fields on project #${project.projectNumber}...`,
|
|
255
247
|
);
|
|
256
248
|
const fields = await provider.ensureProjectFields(PROJECT_FIELD_DEFS);
|
|
257
249
|
log(
|
|
258
|
-
`[
|
|
250
|
+
`[Bootstrap] Fields — created: ${fields.created.length}, skipped: ${fields.skipped.length}`,
|
|
259
251
|
);
|
|
260
252
|
return fields;
|
|
261
253
|
}
|
|
@@ -304,7 +296,7 @@ export async function runBootstrap(config, opts = {}) {
|
|
|
304
296
|
if (opts.githubAdminApproved !== true) {
|
|
305
297
|
const skipLog = opts.quiet ? () => {} : Logger.info;
|
|
306
298
|
skipLog(
|
|
307
|
-
'[
|
|
299
|
+
'[Bootstrap] GitHub-admin mutations skipped: github-admin phase group not approved (explicit opt-in required).',
|
|
308
300
|
);
|
|
309
301
|
return { skipped: true, reason: 'github-admin-not-approved' };
|
|
310
302
|
}
|
|
@@ -315,13 +307,13 @@ export async function runBootstrap(config, opts = {}) {
|
|
|
315
307
|
const providerName = config.provider ?? (config.github ? 'github' : null);
|
|
316
308
|
const providerConfig = providerName ? config[providerName] : null;
|
|
317
309
|
|
|
318
|
-
log('[
|
|
319
|
-
log(`[
|
|
320
|
-
log(`[
|
|
310
|
+
log('[Bootstrap] Starting idempotent setup...');
|
|
311
|
+
log(`[Bootstrap] Provider: ${providerName}`);
|
|
312
|
+
log(`[Bootstrap] Target: ${providerConfig?.owner}/${providerConfig?.repo}`);
|
|
321
313
|
|
|
322
|
-
log('[
|
|
314
|
+
log('[Bootstrap] Verifying API access...');
|
|
323
315
|
await verifyApiAccess(provider);
|
|
324
|
-
log('[
|
|
316
|
+
log('[Bootstrap] API access verified.');
|
|
325
317
|
|
|
326
318
|
const labels = await ensureLabels(provider, log);
|
|
327
319
|
const project = await resolveProject(provider, providerConfig, log);
|
|
@@ -336,7 +328,7 @@ export async function runBootstrap(config, opts = {}) {
|
|
|
336
328
|
views = await ensureViews(provider, log);
|
|
337
329
|
fields = await ensureProjectFields(provider, project, log);
|
|
338
330
|
} else {
|
|
339
|
-
log('[
|
|
331
|
+
log('[Bootstrap] No active project — skipping legacy project-field setup.');
|
|
340
332
|
}
|
|
341
333
|
|
|
342
334
|
// Story #2845 — audit project workflows for the ones that race against
|
|
@@ -407,7 +399,7 @@ export async function runBootstrap(config, opts = {}) {
|
|
|
407
399
|
log,
|
|
408
400
|
});
|
|
409
401
|
|
|
410
|
-
log('[
|
|
402
|
+
log('[Bootstrap] Done.');
|
|
411
403
|
return {
|
|
412
404
|
labels,
|
|
413
405
|
fields,
|
|
@@ -432,14 +424,14 @@ async function main() {
|
|
|
432
424
|
// before bootstrap proceeds.
|
|
433
425
|
try {
|
|
434
426
|
const { version } = await preflightGh();
|
|
435
|
-
Logger.info(`[
|
|
427
|
+
Logger.info(`[Bootstrap] gh CLI ${version} ready (auth verified).`);
|
|
436
428
|
} catch (err) {
|
|
437
429
|
if (
|
|
438
430
|
err instanceof GhNotInstalledError ||
|
|
439
431
|
err instanceof GhAuthError ||
|
|
440
432
|
err instanceof GhVersionError
|
|
441
433
|
) {
|
|
442
|
-
Logger.error(`[
|
|
434
|
+
Logger.error(`[Bootstrap] ${err.message}`);
|
|
443
435
|
process.exit(1);
|
|
444
436
|
}
|
|
445
437
|
throw err;
|
|
@@ -453,7 +445,7 @@ async function main() {
|
|
|
453
445
|
await preflightRuntimeDeps();
|
|
454
446
|
} catch (err) {
|
|
455
447
|
if (err instanceof MissingRuntimeDepsError) {
|
|
456
|
-
Logger.error(`[
|
|
448
|
+
Logger.error(`[Bootstrap] ${err.message}`);
|
|
457
449
|
process.exit(1);
|
|
458
450
|
}
|
|
459
451
|
throw err;
|
|
@@ -467,13 +459,13 @@ async function main() {
|
|
|
467
459
|
const config = resolveConfig();
|
|
468
460
|
|
|
469
461
|
if (!config.github) {
|
|
470
|
-
throw new Error('[
|
|
462
|
+
throw new Error('[Bootstrap] No "github" block found in .agentrc.json.');
|
|
471
463
|
}
|
|
472
464
|
|
|
473
465
|
try {
|
|
474
466
|
validateOrchestrationConfig(config);
|
|
475
467
|
} catch (err) {
|
|
476
|
-
Logger.error(`[
|
|
468
|
+
Logger.error(`[Bootstrap] ERROR: ${err.message}`);
|
|
477
469
|
process.exit(1);
|
|
478
470
|
}
|
|
479
471
|
|
|
@@ -513,13 +505,13 @@ async function main() {
|
|
|
513
505
|
// detailed summary only when mutations were actually attempted.
|
|
514
506
|
if (result.skipped) {
|
|
515
507
|
Logger.info(
|
|
516
|
-
`[
|
|
508
|
+
`[Bootstrap] GitHub-admin step skipped (${result.reason}). Re-run with --approve-github-admin (or --assume-yes) to apply.`,
|
|
517
509
|
);
|
|
518
510
|
} else {
|
|
519
511
|
printSummary(result);
|
|
520
512
|
}
|
|
521
513
|
} catch (err) {
|
|
522
|
-
throw new Error(`[
|
|
514
|
+
throw new Error(`[Bootstrap] runBootstrap failed: ${err.message}`);
|
|
523
515
|
}
|
|
524
516
|
}
|
|
525
517
|
|