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:
@@ -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
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user