@ikunin/sprintpilot 2.2.22 → 2.2.24

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.
@@ -1460,6 +1460,23 @@ function cmdStart(opts) {
1460
1460
  '[autopilot] WARN ma.parallel_stories=true but the state machine is not yet wired for parallel dispatch (planned for v2.3.0). Stories will run sequentially this session.\n',
1461
1461
  );
1462
1462
  }
1463
+ if (profile.lint_enabled) {
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.
1469
+ ledger.append(
1470
+ {
1471
+ kind: 'state_transition',
1472
+ detail: {
1473
+ lint_enabled: true,
1474
+ lint_blocking: !!profile.lint_blocking,
1475
+ },
1476
+ },
1477
+ { projectRoot },
1478
+ );
1479
+ }
1463
1480
 
1464
1481
  // Worktree health check — once per session, after lock acquire so we
1465
1482
  // don't compete with another active session for the same .worktrees
@@ -1629,7 +1646,7 @@ function cmdRecord(opts) {
1629
1646
  profile.enabled === false && stateMachine.shouldSkipVerifyWhenGitDisabled(runtime.phase);
1630
1647
  let verifyResult;
1631
1648
  if (signal.status === 'success' && !isGitDisabledPhase) {
1632
- verifyResult = verifyMod.verify(runtime, signal.output, { projectRoot });
1649
+ verifyResult = verifyMod.verify(runtime, signal.output, { projectRoot, profile });
1633
1650
  ledger.append(
1634
1651
  { kind: 'verify_result', phase: runtime.phase, ok: verifyResult.ok, issues: verifyResult.issues || [] },
1635
1652
  { projectRoot },
@@ -1638,7 +1655,7 @@ function cmdRecord(opts) {
1638
1655
  verifyResult = verifyMod.verifyWithOverride(
1639
1656
  runtime,
1640
1657
  signal.output || {},
1641
- { projectRoot },
1658
+ { projectRoot, profile },
1642
1659
  signal.evidence || {},
1643
1660
  );
1644
1661
  ledger.append(
@@ -172,6 +172,16 @@ function flatToProfile(resolved, profileName) {
172
172
  // --stale-minutes. 0 disables the auto-takeover entirely (locks are
173
173
  // never considered stale; manual `autopilot off` required).
174
174
  lock_stale_timeout_minutes: coerceInt(get(resolved, 'git.lock.stale_timeout_minutes'), 30),
175
+ // git.lint.* — documented in modules/git/config.yaml as a future
176
+ // post-DEV_GREEN lint phase. Currently NOT wired into the state
177
+ // machine (no LINT_CHECK phase emitted). v2.2.23 plumbs the config
178
+ // to the typed Profile so users see the shape and cmdStart emits an
179
+ // experimental warning when lint_enabled=true (mirroring
180
+ // parallel_stories handling). Full state-machine integration is
181
+ // tracked for v2.3.0+.
182
+ lint_enabled: coerceBool(get(resolved, 'git.lint.enabled'), false),
183
+ lint_blocking: coerceBool(get(resolved, 'git.lint.blocking'), false),
184
+ lint_output_limit: coerceInt(get(resolved, 'git.lint.output_limit'), 100),
175
185
  // git.platform.provider + base_url — forwarded to create-pr.js when
176
186
  // the orchestrator opens or polls PRs. 'auto' delegates platform
177
187
  // detection to create-pr.js (currently defaults to github).
@@ -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
@@ -381,6 +430,20 @@ function verifyDevGreen(state, out, ctx) {
381
430
  issues.push('tests_run must be a positive number (per AGENTS.md test-result format)');
382
431
  }
383
432
  }
433
+ // Post-GREEN gates: lint-changed + lint-test-pitfalls + ci-parity scan.
434
+ // Composed pipeline lives in scripts/post-green-gates.js. Only fires
435
+ // when profile.lint_enabled === true. Blocking vs non-blocking
436
+ // governed by profile.lint_blocking. Pre-2.2.24 the script existed
437
+ // and was documented as "called by the orchestrator after GREEN
438
+ // verify" but nothing actually invoked it.
439
+ const lintResult = runPostGreenGates(ctx);
440
+ if (lintResult) {
441
+ if (lintResult.failed && (ctx.profile && ctx.profile.lint_blocking)) {
442
+ issues.push(
443
+ `post-green-gates failed (lint_blocking=true): ${lintResult.summary || 'see ledger detail'}`,
444
+ );
445
+ }
446
+ }
384
447
  return { ok: issues.length === 0, issues };
385
448
  }
386
449
 
@@ -1,6 +1,6 @@
1
1
  addon:
2
2
  name: sprintpilot
3
- version: 2.2.22
3
+ version: 2.2.24
4
4
  description: Sprintpilot — autopilot and multi-agent addon for BMad Method (git workflow, parallel agents, autonomous story execution)
5
5
  bmad_compatibility: ">=6.2.0"
6
6
  modules:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikunin/sprintpilot",
3
- "version": "2.2.22",
3
+ "version": "2.2.24",
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": {