diff --git a/src/bmm-skills/4-implementation/bmad-code-review/steps/step-04-present.md b/src/bmm-skills/4-implementation/bmad-code-review/steps/step-04-present.md index b76448503..fb8d84772 100644 --- a/src/bmm-skills/4-implementation/bmad-code-review/steps/step-04-present.md +++ b/src/bmm-skills/4-implementation/bmad-code-review/steps/step-04-present.md @@ -86,7 +86,8 @@ Skip this section if `{spec_file}` is not set. #### Determine new status based on review outcome -- If all `decision-needed` and `patch` findings were resolved (fixed or dismissed) AND no unresolved HIGH/MEDIUM issues remain: set `{new_status}` = `done`. Update the story file Status section to `done`. +- If the live journey release-gate closeout above found missing evidence, red gates, skipped gates, blocked gates, environment-blocked gates, or incomplete/expired product-owner deferrals: keep `{new_status}` = `in-progress` regardless of resolved findings. Update the story file Status section to `in-progress` and record the blocker in the story file. +- If all `decision-needed` and `patch` findings were resolved (fixed or dismissed) AND no unresolved HIGH/MEDIUM issues remain AND live-gate blockers are cleared (or have complete, unexpired product-owner deferral): set `{new_status}` = `done`. Update the story file Status section to `done`. - If `patch` findings were left as action items, or unresolved issues remain: set `{new_status}` = `in-progress`. Update the story file Status section to `in-progress`. Save the story file. @@ -108,9 +109,11 @@ If `{sprint_status}` file does not exist, note that story status was updated in Re-open the story file after saving and verify the top-level `Status:` field equals `{new_status}`. -If `{sprint_status}` exists and `{story_key}` was found, re-open `{sprint_status}` after saving and verify `development_status[{story_key}]` also equals `{new_status}`. +Set `{reconciliation_result}` = `story file verified; sprint tracker verification skipped`. -If either artifact does not match, HALT with a closeout reconciliation failure instead of reporting completion. +If `{sprint_status}` exists and `{story_key}` was found, re-open `{sprint_status}` after saving and verify `development_status[{story_key}]` also equals `{new_status}`. If it matches, set `{reconciliation_result}` = `story markdown and sprint tracker agree on {new_status}`. + +If the story file does not match `{new_status}`, or if `{sprint_status}` was verified and `development_status[{story_key}]` does not match `{new_status}`, HALT with a closeout reconciliation failure instead of reporting completion. #### Completion summary @@ -121,7 +124,7 @@ If either artifact does not match, HALT with a closeout reconciliation failure i > **Action Items Created:** > **Deferred:** > **Dismissed:** -> **Reconciled:** story markdown and sprint tracker agree on `{new_status}` +> **Reconciled:** `{reconciliation_result}` ### 7. Next steps diff --git a/src/bmm-skills/4-implementation/bmad-sprint-status/SKILL.md b/src/bmm-skills/4-implementation/bmad-sprint-status/SKILL.md index 5b8d48608..0a94005db 100644 --- a/src/bmm-skills/4-implementation/bmad-sprint-status/SKILL.md +++ b/src/bmm-skills/4-implementation/bmad-sprint-status/SKILL.md @@ -290,8 +290,14 @@ If the command targets a story, set `story_key={{next_story_id}}` when prompted. Return -For each story entry in development_status, use `story_location` to open the matching story markdown file and compare its top-level `Status:` value with the tracker status when the tracker status is `review` or `done` - +For each story entry in development_status where the tracker status is `review` or `done`: + +- Resolve the story path from `story_location` using `{project-root}` as the base for relative paths. +- Open the matching story markdown file and read the top-level `Status:` value. +- If the file is missing, unreadable, or the top-level `Status:` value is missing, record a drift entry with the path and reason. +- Compare the markdown `Status:` value with the tracker status and record mismatches as drift entries. + + is_valid = false error = "Story/tracker status drift detected: {{drift_entries}}" suggestion = "Reconcile story markdown Status fields with sprint-status.yaml before closeout" diff --git a/test/test-closeout-reconciliation.js b/test/test-closeout-reconciliation.js index 2998da48d..ca322de19 100644 --- a/test/test-closeout-reconciliation.js +++ b/test/test-closeout-reconciliation.js @@ -44,22 +44,51 @@ assert( ); assert( - codeReview.includes('If either artifact does not match, HALT with a closeout reconciliation failure instead of reporting completion.'), + codeReview.includes( + 'If the story file does not match `{new_status}`, or if `{sprint_status}` was verified and `development_status[{story_key}]` does not match `{new_status}`, HALT with a closeout reconciliation failure instead of reporting completion.', + ), 'code-review halts when closeout reconciliation fails', ); assert(codeReview.includes('development_status[{story_key}]'), 'code-review verifies the sprint tracker entry during reconciliation'); assert( - codeReview.includes('story markdown and sprint tracker agree on `{new_status}`'), - 'code-review reports successful reconciliation in the completion summary', + codeReview.includes('Set `{reconciliation_result}` = `story file verified; sprint tracker verification skipped`.'), + 'code-review records when sprint tracker verification is skipped', ); -assert(sprintStatus.includes('story/tracker status drift detected'), 'sprint-status warns about story/tracker drift'); +assert( + codeReview.includes( + 'If the live journey release-gate closeout above found missing evidence, red gates, skipped gates, blocked gates, environment-blocked gates, or incomplete/expired product-owner deferrals: keep `{new_status}` = `in-progress` regardless of resolved findings.', + ), + 'code-review keeps live-gate blockers from being overwritten during final status selection', +); assert( - sprintStatus.includes('use `story_location` to open the matching story markdown file'), - 'sprint-status validate mode uses story_location when checking story files against tracker state', + codeReview.includes('> **Reconciled:** `{reconciliation_result}`'), + 'code-review reports reconciliation status conditionally in the completion summary', +); + +assert( + sprintStatus.includes('Resolve the story path from `story_location` using `{project-root}` as the base for relative paths.'), + 'sprint-status defines how story_location is resolved during validate mode', +); + +assert( + sprintStatus.includes( + 'If the file is missing, unreadable, or the top-level `Status:` value is missing, record a drift entry with the path and reason.', + ), + 'sprint-status treats missing story artifacts or missing Status values as validation failures', +); + +assert( + sprintStatus.includes('any drift_entries were recorded for review/done stories'), + 'sprint-status fails validate mode on any recorded reconciliation drift entries', +); + +assert( + sprintStatus.includes('story markdown Status fields with sprint-status.yaml before closeout'), + 'sprint-status reports reconciliation guidance when drift is detected', ); console.log(`\n${passed} passed, ${failed} failed\n`);