fix: tighten closeout reconciliation rules

This commit is contained in:
Dicky Moore 2026-04-27 22:34:42 +01:00
parent 999fbea7a3
commit 44796ab116
3 changed files with 50 additions and 12 deletions

View File

@ -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:** <action_count>
> **Deferred:** <W>
> **Dismissed:** <R>
> **Reconciled:** story markdown and sprint tracker agree on `{new_status}`
> **Reconciled:** `{reconciliation_result}`
### 7. Next steps

View File

@ -290,8 +290,14 @@ If the command targets a story, set `story_key={{next_story_id}}` when prompted.
<action>Return</action>
</check>
<action>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`</action>
<check if="any story markdown status does not match its tracker status for review/done stories">
<action>For each story entry in development_status where the tracker status is `review` or `done`:</action>
- 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.
<check if="any drift_entries were recorded for review/done stories">
<template-output>is_valid = false</template-output>
<template-output>error = "Story/tracker status drift detected: {{drift_entries}}"</template-output>
<template-output>suggestion = "Reconcile story markdown Status fields with sprint-status.yaml before closeout"</template-output>

View File

@ -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`);