@ikunin/sprintpilot 2.2.23 → 2.2.25
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.
|
@@ -1461,22 +1461,21 @@ function cmdStart(opts) {
|
|
|
1461
1461
|
);
|
|
1462
1462
|
}
|
|
1463
1463
|
if (profile.lint_enabled) {
|
|
1464
|
-
//
|
|
1465
|
-
//
|
|
1466
|
-
//
|
|
1464
|
+
// v2.2.24: lint_enabled wires verifyDevGreen → post-green-gates.js
|
|
1465
|
+
// (lint-changed + lint-test-pitfalls + ci-parity scan). lint_blocking
|
|
1466
|
+
// governs whether a failed gate halts the autopilot or passes
|
|
1467
|
+
// through with a warning. The v2.2.23 "not wired" warning is gone —
|
|
1468
|
+
// lint runs for real now.
|
|
1467
1469
|
ledger.append(
|
|
1468
1470
|
{
|
|
1469
1471
|
kind: 'state_transition',
|
|
1470
1472
|
detail: {
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
+
lint_enabled: true,
|
|
1474
|
+
lint_blocking: !!profile.lint_blocking,
|
|
1473
1475
|
},
|
|
1474
1476
|
},
|
|
1475
1477
|
{ projectRoot },
|
|
1476
1478
|
);
|
|
1477
|
-
process.stderr.write(
|
|
1478
|
-
'[autopilot] WARN git.lint.enabled=true but no LINT_CHECK phase exists yet (planned for v2.3.0). Bake lint into your test command for now.\n',
|
|
1479
|
-
);
|
|
1480
1479
|
}
|
|
1481
1480
|
|
|
1482
1481
|
// Worktree health check — once per session, after lock acquire so we
|
|
@@ -1647,7 +1646,7 @@ function cmdRecord(opts) {
|
|
|
1647
1646
|
profile.enabled === false && stateMachine.shouldSkipVerifyWhenGitDisabled(runtime.phase);
|
|
1648
1647
|
let verifyResult;
|
|
1649
1648
|
if (signal.status === 'success' && !isGitDisabledPhase) {
|
|
1650
|
-
verifyResult = verifyMod.verify(runtime, signal.output, { projectRoot });
|
|
1649
|
+
verifyResult = verifyMod.verify(runtime, signal.output, { projectRoot, profile });
|
|
1651
1650
|
ledger.append(
|
|
1652
1651
|
{ kind: 'verify_result', phase: runtime.phase, ok: verifyResult.ok, issues: verifyResult.issues || [] },
|
|
1653
1652
|
{ projectRoot },
|
|
@@ -1656,7 +1655,7 @@ function cmdRecord(opts) {
|
|
|
1656
1655
|
verifyResult = verifyMod.verifyWithOverride(
|
|
1657
1656
|
runtime,
|
|
1658
1657
|
signal.output || {},
|
|
1659
|
-
{ projectRoot },
|
|
1658
|
+
{ projectRoot, profile },
|
|
1660
1659
|
signal.evidence || {},
|
|
1661
1660
|
);
|
|
1662
1661
|
ledger.append(
|
|
@@ -164,6 +164,54 @@ function autoDetectTestFiles(ctx, baseBranch) {
|
|
|
164
164
|
return out;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
// Invoke scripts/post-green-gates.js when profile.lint_enabled is true.
|
|
168
|
+
// Returns null when:
|
|
169
|
+
// - profile.lint_enabled is false / absent (feature opt-out)
|
|
170
|
+
// - the script is missing (partial install)
|
|
171
|
+
// - projectRoot is unset
|
|
172
|
+
// Returns { failed: bool, summary?: string } on a real run.
|
|
173
|
+
//
|
|
174
|
+
// The script's contract: exit 0 = all gates pass, exit !=0 = at least
|
|
175
|
+
// one gate failed. JSON report on stdout when invoked with --json (we
|
|
176
|
+
// pass that flag). Failure summary captured for the issue message.
|
|
177
|
+
function runPostGreenGates(ctx) {
|
|
178
|
+
if (!ctx || !ctx.profile || !ctx.profile.lint_enabled) return null;
|
|
179
|
+
if (!ctx.projectRoot) return null;
|
|
180
|
+
const scriptRel = nodePath.join('_Sprintpilot', 'scripts', 'post-green-gates.js');
|
|
181
|
+
const scriptAbs = nodePath.join(ctx.projectRoot, scriptRel);
|
|
182
|
+
let fs;
|
|
183
|
+
try {
|
|
184
|
+
fs = ctx.fs || nodeFs;
|
|
185
|
+
if (!fs.existsSync(scriptAbs)) return null;
|
|
186
|
+
} catch {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
const cp = require('node:child_process');
|
|
190
|
+
try {
|
|
191
|
+
const r = cp.spawnSync(
|
|
192
|
+
'node',
|
|
193
|
+
[scriptAbs, '--json', '--project-root', ctx.projectRoot],
|
|
194
|
+
{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 120_000 },
|
|
195
|
+
);
|
|
196
|
+
if (r.status === 0) return { failed: false };
|
|
197
|
+
// Try to extract a brief summary from the JSON output. Fall back
|
|
198
|
+
// to the exit code if parsing fails.
|
|
199
|
+
let summary = `exit ${r.status}`;
|
|
200
|
+
try {
|
|
201
|
+
const parsed = JSON.parse(r.stdout || '{}');
|
|
202
|
+
if (parsed && parsed.failed_gate) summary = `failed_gate=${parsed.failed_gate}`;
|
|
203
|
+
if (parsed && parsed.first_issue) summary += `: ${parsed.first_issue}`;
|
|
204
|
+
} catch {
|
|
205
|
+
/* keep exit-code summary */
|
|
206
|
+
}
|
|
207
|
+
return { failed: true, summary };
|
|
208
|
+
} catch (_e) {
|
|
209
|
+
// Script crashed (e.g. ENOENT for node). Treat as non-failing —
|
|
210
|
+
// the lint phase should not gate the autopilot on its own bugs.
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
167
215
|
// Probe the underlying git state to confirm a STORY_DONE signal whose
|
|
168
216
|
// `git_steps_completed` flag was omitted. Returns true iff:
|
|
169
217
|
// - commit_sha resolves locally (git cat-file -e <sha>)
|
|
@@ -227,6 +275,7 @@ function verify(state, signalOutput, context) {
|
|
|
227
275
|
runner: (context && context.runner) || null,
|
|
228
276
|
projectRoot: (context && context.projectRoot) || '.',
|
|
229
277
|
augmented: (context && context.augmented) || null,
|
|
278
|
+
profile: (context && context.profile) || null,
|
|
230
279
|
};
|
|
231
280
|
const out = signalOutput || {};
|
|
232
281
|
// Effective state: fall forward to signal.output for identity fields
|
|
@@ -275,15 +324,27 @@ function verifyCreateStory(state, _out, ctx) {
|
|
|
275
324
|
const override = ctx.augmented || {};
|
|
276
325
|
const ackMissingFm = override && override.acknowledge_missing_front_matter === true;
|
|
277
326
|
if (!fm && !ackMissingFm) issues.push('story file missing YAML front-matter');
|
|
278
|
-
// AC presence — look for
|
|
279
|
-
|
|
327
|
+
// AC presence — look for an Acceptance Criteria section with at
|
|
328
|
+
// least one list entry. Accepts:
|
|
329
|
+
// - heading levels ##, ###, #### (BMad standard is ##; some templates
|
|
330
|
+
// nest AC under Dev Notes which would use ###)
|
|
331
|
+
// - "Acceptance Criteria" / "Acceptance criteria" / "AC" (the abbr
|
|
332
|
+
// appears in some templates)
|
|
333
|
+
// - bullet markers `-` or `*` or numbered `1.` / `1)` lists
|
|
334
|
+
if (
|
|
335
|
+
text &&
|
|
336
|
+
!/#{2,4}\s+(?:Acceptance Criteria|Acceptance criteria|AC)\b[\s\S]*?\n[ \t]*(?:[-*]|\d+[.)])\s+\S/i.test(
|
|
337
|
+
text,
|
|
338
|
+
)
|
|
339
|
+
) {
|
|
280
340
|
issues.push('Acceptance Criteria section missing or empty');
|
|
281
341
|
}
|
|
282
342
|
// Tasks/Subtasks section with at least one task checkbox — required by
|
|
283
343
|
// BMad bookkeeping. `bmad-create-story` produces unchecked `[ ]`
|
|
284
344
|
// entries; `bmad-dev-story` flips them to `[x]`. If neither is present,
|
|
285
|
-
// dev-story will have nothing to check off.
|
|
286
|
-
|
|
345
|
+
// dev-story will have nothing to check off. Accept heading levels
|
|
346
|
+
// ## / ### / #### (templates sometimes nest Tasks under Dev Notes).
|
|
347
|
+
if (text && !/#{2,4}\s+Tasks(?:\s*\/\s*Subtasks)?[\s\S]*?(?:\[ \]|\[x\])/i.test(text)) {
|
|
287
348
|
issues.push(
|
|
288
349
|
'Tasks (or Tasks/Subtasks) section with at least one `[ ]` or `[x]` checkbox missing',
|
|
289
350
|
);
|
|
@@ -381,6 +442,20 @@ function verifyDevGreen(state, out, ctx) {
|
|
|
381
442
|
issues.push('tests_run must be a positive number (per AGENTS.md test-result format)');
|
|
382
443
|
}
|
|
383
444
|
}
|
|
445
|
+
// Post-GREEN gates: lint-changed + lint-test-pitfalls + ci-parity scan.
|
|
446
|
+
// Composed pipeline lives in scripts/post-green-gates.js. Only fires
|
|
447
|
+
// when profile.lint_enabled === true. Blocking vs non-blocking
|
|
448
|
+
// governed by profile.lint_blocking. Pre-2.2.24 the script existed
|
|
449
|
+
// and was documented as "called by the orchestrator after GREEN
|
|
450
|
+
// verify" but nothing actually invoked it.
|
|
451
|
+
const lintResult = runPostGreenGates(ctx);
|
|
452
|
+
if (lintResult) {
|
|
453
|
+
if (lintResult.failed && (ctx.profile && ctx.profile.lint_blocking)) {
|
|
454
|
+
issues.push(
|
|
455
|
+
`post-green-gates failed (lint_blocking=true): ${lintResult.summary || 'see ledger detail'}`,
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
384
459
|
return { ok: issues.length === 0, issues };
|
|
385
460
|
}
|
|
386
461
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ikunin/sprintpilot",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.25",
|
|
4
4
|
"description": "Sprintpilot — autopilot and multi-agent addon for BMad Method v6: git workflow, parallel agents, autonomous story execution",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|