@ikunin/sprintpilot 2.2.7 → 2.2.9

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.
@@ -499,15 +499,50 @@ function composeRuntimeState(persisted, profile, projectRoot) {
499
499
  projectRoot,
500
500
  );
501
501
  if (rejection) {
502
- process.stderr.write(
503
- `[autopilot] WARN persisted current_story "${persistedCurrentStory}" rejected: ${rejection}. ` +
504
- 'Treating as null and falling through to queue / sprint-status resolution. ' +
505
- 'This typically means state was poisoned by an older orchestrator version (v2.1.3 / v2.1.4 pre-filter); ' +
506
- 'next emission will clean it up.\n',
507
- );
508
- resolvedStoryKey = null;
509
- resolvedEpic = null;
510
- resolvedStoryFilePath = null;
502
+ // Phase-aware rejection gate. The "marked done" rejection is NOT
503
+ // a poisoned-state signal when state.phase is a story-bound phase
504
+ // (CHECK_READINESS through STORY_LAND) at STORY_DONE the story
505
+ // IS expected to be marked done in sprint-status (verifyStoryDone
506
+ // enforces it). Pre-2.2.9 fix: any "done" rejection nulled
507
+ // story_key mid-record, producing branch "story/unknown" on
508
+ // commit_and_push_story.
509
+ //
510
+ // Epic-rollup-header / retrospective / not-in-sprint-status
511
+ // rejections are ALWAYS poison and fire regardless of phase.
512
+ const STORY_BOUND_PHASES = new Set([
513
+ STATES.CHECK_READINESS,
514
+ STATES.DEV_RED,
515
+ STATES.DEV_GREEN,
516
+ STATES.CODE_REVIEW,
517
+ STATES.PATCH_APPLY,
518
+ STATES.PATCH_RETEST,
519
+ STATES.STORY_DONE,
520
+ STATES.STORY_LAND,
521
+ ]);
522
+ const isDoneRejection = /already complete/.test(rejection);
523
+ const skipDoneRejection = isDoneRejection && STORY_BOUND_PHASES.has(phase);
524
+ if (!skipDoneRejection) {
525
+ process.stderr.write(
526
+ `[autopilot] WARN persisted current_story "${persistedCurrentStory}" rejected: ${rejection}. ` +
527
+ 'Treating as null and falling through to queue / sprint-status resolution. ' +
528
+ 'This typically means state was poisoned by an older orchestrator version (v2.1.3 / v2.1.4 pre-filter); ' +
529
+ 'next emission will clean it up.\n',
530
+ );
531
+ resolvedStoryKey = null;
532
+ resolvedEpic = null;
533
+ resolvedStoryFilePath = null;
534
+ // When the rejected story was at a phase that REQUIRES a story_key
535
+ // to emit a coherent action, also reset state.phase to flowStart.
536
+ // Otherwise the next emission produces a story-bound action (e.g.,
537
+ // commit_and_push_story) with null story_key → branch resolves to
538
+ // "story/unknown" → execution fails or corrupts the working tree.
539
+ if (STORY_BOUND_PHASES.has(phase)) {
540
+ process.stderr.write(
541
+ `[autopilot] WARN phase was "${phase}" (requires story_key) — resetting to ${flowStart} so next emission re-enters story-start.\n`,
542
+ );
543
+ phase = flowStart;
544
+ }
545
+ }
511
546
  }
512
547
  }
513
548
  // Explicit queue (populated by `autopilot start --stories` / `--epic`)
@@ -157,7 +157,17 @@ function verifyCreateStory(state, _out, ctx) {
157
157
  else {
158
158
  const text = readFileSafe(ctx.fs, state.story_file_path);
159
159
  const fm = frontMatter(text);
160
- if (!fm) issues.push('story file missing YAML front-matter');
160
+ // Escape hatch: when the LLM sends verify_override with evidence
161
+ // {acknowledge_missing_front_matter: true, decision_log_ref: '...'},
162
+ // skip the front-matter check ONLY for this verification call. AC +
163
+ // Tasks checks still run. Auditable via the verify_override ledger
164
+ // entry which captures evidence verbatim. Used when bmad-create-story
165
+ // can't or won't regenerate front-matter (e.g., legacy story files
166
+ // in repos that pre-date the front-matter convention and have a
167
+ // body the skill wants to preserve).
168
+ const override = ctx.augmented || {};
169
+ const ackMissingFm = override && override.acknowledge_missing_front_matter === true;
170
+ if (!fm && !ackMissingFm) issues.push('story file missing YAML front-matter');
161
171
  // AC presence — look for "## Acceptance Criteria" section with at least one bullet.
162
172
  if (text && !/##\s+Acceptance Criteria[\s\S]*?\n-\s+/.test(text)) {
163
173
  issues.push('Acceptance Criteria section missing or empty');
@@ -1,6 +1,6 @@
1
1
  addon:
2
2
  name: sprintpilot
3
- version: 2.2.7
3
+ version: 2.2.9
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.7",
3
+ "version": "2.2.9",
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": {