fix: executor updates ROADMAP.md and REQUIREMENTS.md per-plan

gsd-executor had no instruction to update ROADMAP or REQUIREMENTS after
completing a plan — both stayed unchecked throughout milestone execution.

- Add `roadmap update-plan-progress` call to executor state_updates
- Add `requirements mark-complete` CLI command to gsd-tools
- Wire requirement marking into executor and execute-plan workflows
- Include ROADMAP.md and REQUIREMENTS.md in executor final commit

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lex Christopherson
2026-02-17 13:19:02 -06:00
parent c609f3d0de
commit 1764abc615
4 changed files with 112 additions and 4 deletions

View File

@@ -382,12 +382,25 @@ node ~/.claude/get-shit-done/bin/gsd-tools.cjs state record-session \
--stopped-at "Completed ${PHASE}-${PLAN}-PLAN.md"
```
```bash
# Update ROADMAP.md progress for this phase (plan counts, status)
node ~/.claude/get-shit-done/bin/gsd-tools.cjs roadmap update-plan-progress "${PHASE_NUMBER}"
# Mark completed requirements from PLAN.md frontmatter
# Extract the `requirements` array from the plan's frontmatter, then mark each complete
node ~/.claude/get-shit-done/bin/gsd-tools.cjs requirements mark-complete ${REQ_IDS}
```
**Requirement IDs:** Extract from the PLAN.md frontmatter `requirements:` field (e.g., `requirements: [AUTH-01, AUTH-02]`). Pass all IDs to `requirements mark-complete`. If the plan has no requirements field, skip this step.
**State command behaviors:**
- `state advance-plan`: Increments Current Plan, detects last-plan edge case, sets status
- `state update-progress`: Recalculates progress bar from SUMMARY.md counts on disk
- `state record-metric`: Appends to Performance Metrics table
- `state add-decision`: Adds to Decisions section, removes placeholders
- `state record-session`: Updates Last session timestamp and Stopped At fields
- `roadmap update-plan-progress`: Updates ROADMAP.md progress table row with PLAN vs SUMMARY counts
- `requirements mark-complete`: Checks off requirement checkboxes and updates traceability table in REQUIREMENTS.md
**Extract decisions from SUMMARY.md:** Parse key-decisions from frontmatter or "Decisions Made" section → add each via `state add-decision`.
@@ -399,7 +412,7 @@ node ~/.claude/get-shit-done/bin/gsd-tools.cjs state add-blocker "Blocker descri
<final_commit>
```bash
node ~/.claude/get-shit-done/bin/gsd-tools.cjs commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md
node ~/.claude/get-shit-done/bin/gsd-tools.cjs commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md .planning/REQUIREMENTS.md
```
Separate from per-task commits — captures execution results only.
@@ -432,6 +445,7 @@ Plan execution complete when:
- [ ] Authentication gates handled and documented
- [ ] SUMMARY.md created with substantive content
- [ ] STATE.md updated (position, decisions, issues, session)
- [ ] Final metadata commit made
- [ ] ROADMAP.md updated with plan progress (via `roadmap update-plan-progress`)
- [ ] Final metadata commit made (includes SUMMARY.md, STATE.md, ROADMAP.md)
- [ ] Completion format returned to orchestrator
</success_criteria>

View File

@@ -41,6 +41,10 @@
* roadmap analyze Full roadmap parse with disk status
* roadmap update-plan-progress <N> Update progress table row from disk (PLAN vs SUMMARY counts)
*
* Requirements Operations:
* requirements mark-complete <ids> Mark requirement IDs as complete in REQUIREMENTS.md
* Accepts: REQ-01,REQ-02 or REQ-01 REQ-02 or [REQ-01, REQ-02]
*
* Milestone Operations:
* milestone complete <version> Archive milestone, create MILESTONES.md
* [--name <name>]
@@ -3131,6 +3135,75 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
}, raw, `${summaryCount}/${planCount} ${status}`);
}
// ─── Requirements Mark Complete ───────────────────────────────────────────────
function cmdRequirementsMarkComplete(cwd, reqIdsRaw, raw) {
if (!reqIdsRaw || reqIdsRaw.length === 0) {
error('requirement IDs required. Usage: requirements mark-complete REQ-01,REQ-02 or REQ-01 REQ-02');
}
// Accept comma-separated, space-separated, or bracket-wrapped: [REQ-01, REQ-02]
const reqIds = reqIdsRaw
.join(' ')
.replace(/[\[\]]/g, '')
.split(/[,\s]+/)
.map(r => r.trim())
.filter(Boolean);
if (reqIds.length === 0) {
error('no valid requirement IDs found');
}
const reqPath = path.join(cwd, '.planning', 'REQUIREMENTS.md');
if (!fs.existsSync(reqPath)) {
output({ updated: false, reason: 'REQUIREMENTS.md not found', ids: reqIds }, raw, 'no requirements file');
return;
}
let reqContent = fs.readFileSync(reqPath, 'utf-8');
const updated = [];
const notFound = [];
for (const reqId of reqIds) {
let found = false;
// Update checkbox: - [ ] **REQ-ID** → - [x] **REQ-ID**
const checkboxPattern = new RegExp(`(-\\s*\\[)[ ](\\]\\s*\\*\\*${reqId}\\*\\*)`, 'gi');
if (checkboxPattern.test(reqContent)) {
reqContent = reqContent.replace(checkboxPattern, '$1x$2');
found = true;
}
// Update traceability table: | REQ-ID | Phase N | Pending | → | REQ-ID | Phase N | Complete |
const tablePattern = new RegExp(`(\\|\\s*${reqId}\\s*\\|[^|]+\\|)\\s*Pending\\s*(\\|)`, 'gi');
if (tablePattern.test(reqContent)) {
// Re-read since test() advances lastIndex for global regex
reqContent = reqContent.replace(
new RegExp(`(\\|\\s*${reqId}\\s*\\|[^|]+\\|)\\s*Pending\\s*(\\|)`, 'gi'),
'$1 Complete $2'
);
found = true;
}
if (found) {
updated.push(reqId);
} else {
notFound.push(reqId);
}
}
if (updated.length > 0) {
fs.writeFileSync(reqPath, reqContent, 'utf-8');
}
output({
updated: updated.length > 0,
marked_complete: updated,
not_found: notFound,
total: reqIds.length,
}, raw, `${updated.length}/${reqIds.length} requirements marked complete`);
}
// ─── Phase Complete (Transition) ──────────────────────────────────────────────
function cmdPhaseComplete(cwd, phaseNum, raw) {
@@ -5078,6 +5151,16 @@ async function main() {
break;
}
case 'requirements': {
const subcommand = args[1];
if (subcommand === 'mark-complete') {
cmdRequirementsMarkComplete(cwd, args.slice(2), raw);
} else {
error('Unknown requirements subcommand. Available: mark-complete');
}
break;
}
case 'phase': {
const subcommand = args[1];
if (subcommand === 'next-decimal') {

View File

@@ -106,7 +106,7 @@ Execute each wave in sequence. Within a wave: parallel if `PARALLELIZATION=true`
prompt="
<objective>
Execute plan {plan_number} of phase {phase_number}-{phase_name}.
Commit each task atomically. Create SUMMARY.md. Update STATE.md.
Commit each task atomically. Create SUMMARY.md. Update STATE.md and ROADMAP.md.
</objective>
<execution_context>
@@ -128,6 +128,7 @@ Execute each wave in sequence. Within a wave: parallel if `PARALLELIZATION=true`
- [ ] Each task committed individually
- [ ] SUMMARY.md created in plan directory
- [ ] STATE.md updated with position and decisions
- [ ] ROADMAP.md updated with plan progress (via `roadmap update-plan-progress`)
</success_criteria>
"
)

View File

@@ -386,11 +386,21 @@ node ~/.claude/get-shit-done/bin/gsd-tools.cjs roadmap update-plan-progress "${P
Counts PLAN vs SUMMARY files on disk. Updates progress table row with correct count and status (`In Progress` or `Complete` with date).
</step>
<step name="update_requirements">
Mark completed requirements from the PLAN.md frontmatter `requirements:` field:
```bash
node ~/.claude/get-shit-done/bin/gsd-tools.cjs requirements mark-complete ${REQ_IDS}
```
Extract requirement IDs from the plan's frontmatter (e.g., `requirements: [AUTH-01, AUTH-02]`). If no requirements field, skip.
</step>
<step name="git_commit_metadata">
Task code already committed per-task. Commit plan metadata:
```bash
node ~/.claude/get-shit-done/bin/gsd-tools.cjs commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md
node ~/.claude/get-shit-done/bin/gsd-tools.cjs commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md .planning/REQUIREMENTS.md
```
</step>