fix: preserve Status/LastActivity/Progress in Current Position during begin-phase

cmdStateBeginPhase replaced the entire ## Current Position section with only
Phase and Plan lines, destroying Status, Last activity, and Progress fields.
cmdStateAdvancePlan then failed to update these fields since they no longer
existed.

Now begin-phase updates individual lines within Current Position instead of
replacing the whole section. Also adds updateCurrentPositionFields helper so
advance-plan keeps the Current Position body in sync with bold frontmatter
fields.

Fixes #1365

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-03-24 19:25:14 -04:00
parent 4ef309e0e4
commit 43d1787670
2 changed files with 191 additions and 5 deletions

View File

@@ -1482,6 +1482,130 @@ describe('milestone-scoped phase counting in frontmatter', () => {
});
});
// ─────────────────────────────────────────────────────────────────────────────
// begin-phase — field preservation (#1365)
// ─────────────────────────────────────────────────────────────────────────────
describe('state begin-phase preserves Current Position fields (#1365)', () => {
let tmpDir;
beforeEach(() => {
tmpDir = createTempProject();
});
afterEach(() => {
cleanup(tmpDir);
});
test('begin-phase preserves Status, Last activity, and Progress in Current Position', () => {
const stateMd = `# Project State
**Current Phase:** 1
**Current Phase Name:** setup
**Total Phases:** 5
**Current Plan:** 0
**Total Plans in Phase:** 0
**Status:** Ready to plan
**Last Activity:** 2026-03-20
**Last Activity Description:** Roadmap created
## Current Position
Phase: 1 of 5 (setup)
Plan: 0 of ? in current phase
Status: Ready to plan
Last activity: 2026-03-20 -- Roadmap created
Progress: [..........] 0%
## Decisions Made
| Phase | Decision | Rationale |
|-------|----------|-----------|
`;
fs.writeFileSync(path.join(tmpDir, '.planning', 'STATE.md'), stateMd);
const result = runGsdTools(
['state', 'begin-phase', '--phase', '1', '--name', 'setup', '--plans', '4'],
tmpDir
);
assert.ok(result.success, `Command failed: ${result.error}`);
const content = fs.readFileSync(
path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8'
);
// Extract the Current Position section
const posMatch = content.match(/## Current Position\s*\n([\s\S]*?)(?=\n##|$)/i);
assert.ok(posMatch, 'Current Position section should exist');
const posSection = posMatch[1];
// Phase and Plan lines should be updated
assert.ok(/^Phase:.*EXECUTING/m.test(posSection), 'Phase line should say EXECUTING');
assert.ok(/^Plan:.*1 of 4/m.test(posSection), 'Plan line should show 1 of 4');
// Status, Last activity, and Progress must still be present (the bug destroys these)
assert.ok(/^Status:/m.test(posSection),
'Status field must be preserved in Current Position');
assert.ok(/^Last activity:/m.test(posSection),
'Last activity field must be preserved in Current Position');
assert.ok(/^Progress:/m.test(posSection),
'Progress field must be preserved in Current Position');
});
test('advance-plan can update Status after begin-phase', () => {
// Simulates the full workflow: begin-phase then advance through all plans
const stateMd = `# Project State
**Current Phase:** 1
**Current Phase Name:** setup
**Total Phases:** 5
**Current Plan:** 0
**Total Plans in Phase:** 0
**Status:** Ready to plan
**Last Activity:** 2026-03-20
**Last Activity Description:** Roadmap created
## Current Position
Phase: 1 of 5 (setup)
Plan: 0 of ? in current phase
Status: Ready to plan
Last activity: 2026-03-20 -- Roadmap created
Progress: [..........] 0%
## Decisions Made
| Phase | Decision | Rationale |
|-------|----------|-----------|
`;
fs.writeFileSync(path.join(tmpDir, '.planning', 'STATE.md'), stateMd);
// Step 1: begin-phase
const beginResult = runGsdTools(
['state', 'begin-phase', '--phase', '1', '--name', 'setup', '--plans', '2'],
tmpDir
);
assert.ok(beginResult.success, `begin-phase failed: ${beginResult.error}`);
// Step 2: advance-plan to go from plan 1 to plan 2
const adv1 = runGsdTools(['state', 'advance-plan'], tmpDir);
assert.ok(adv1.success, `advance-plan 1 failed: ${adv1.error}`);
// Step 3: advance-plan again — plan 2 of 2 is the last, should set "Phase complete"
const adv2 = runGsdTools(['state', 'advance-plan'], tmpDir);
assert.ok(adv2.success, `advance-plan 2 failed: ${adv2.error}`);
const content = fs.readFileSync(
path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8'
);
const posMatch = content.match(/## Current Position\s*\n([\s\S]*?)(?=\n##|$)/i);
assert.ok(posMatch, 'Current Position section should exist after advance-plan');
const posSection = posMatch[1];
// After advancing past all plans, Status should say "Phase complete"
assert.ok(/Status:.*Phase complete/i.test(posSection),
'Status should be updated to "Phase complete" after last advance-plan');
});
});
// ─────────────────────────────────────────────────────────────────────────────
// summary-extract command
// ─────────────────────────────────────────────────────────────────────────────