* fix(#2204): phase-completion writes 'All phases complete' per ADR-2207 completePhaseCore was writing the overloaded bare 'Milestone complete' on the last phase — the same string space the milestone-close verb owns for terminal state. Per ADR-2207, phase-completion now writes the existing intermediate value 'All phases complete' (already used in gsd2-import.cts). Milestone termination ('<version> milestone complete' / 'Awaiting next milestone') remains solely with milestoneCompleteCore. Status lifecycle: Ready to plan → All phases complete → <version> milestone complete → Awaiting next milestone. Changes: - src/state-transition.cts: completePhaseCore status value - src/phase.cts: #2028 guard comment - tests/state-transition.test.cjs: assertion + test name - tests/phase.test.cjs: 8 assertion updates (positive + negative) - tests/state.test.cjs: normalizeStateStatus test case + reset regex - tests/workstream.test.cjs: fixture status to terminal value - gsd-core/workflows/progress.md: Route D label - gsd-core/workflows/transition.md: Route B label - CONTEXT.md: Status lifecycle glossary entry (ADR-2207) - .changeset/brave-geese-jump.md * test(#2204): regenerate golden-install-parity fixtures + workflow-size baseline Workflow file edits (progress.md, transition.md) changed install payload hashes and pushed past the committed workflow-size baseline. Regenerated all 17 golden-install-parity fixtures + claude-local via the standalone gen script (which now also covers the local-scope claude layout). Updated workflow-size-baseline.json and agent-size-baseline.json via size:baseline. * fix(#2204): correct claude-local golden hashes + document gen-script limitation The gen-script's claude-local generation produces macOS-specific hashes incompatible with Linux CI (local-scope install embeds platform-varying node-runner paths). Reverted to manual update using Linux FAILURES.md +actual hashes for the 2 changed workflow files. Added explanatory comment in the gen script. * test(#2204): add isCompletedInventory coverage + clarify CONTEXT.md glossary Addresses orthogonal code-review findings (Medium #1 + #2): - Add isCompletedInventory test cases for ADR-2207 status lifecycle (terminal 'milestone complete' → true; intermediate 'All phases complete' → false; archived → true; active statuses → false) - Clarify CONTEXT.md glossary: note that isCompletedInventory intentionally excludes the intermediate value * docs: backfill changeset PR number (#2259) * docs(#2204): add Status lifecycle table to state-md reference (ADR-2207)
This commit is contained in:
@@ -2940,7 +2940,7 @@ describe('phase complete command', () => {
|
||||
assert.strictEqual(output.next_phase, null, 'no next phase');
|
||||
|
||||
const state = fs.readFileSync(path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
|
||||
assert.ok(state.includes('Milestone complete'), 'status should be milestone complete');
|
||||
assert.ok(state.includes('All phases complete'), 'status should be All phases complete');
|
||||
});
|
||||
|
||||
// #1591: when the active milestone's phase checklist is wrapped in a
|
||||
@@ -3031,8 +3031,8 @@ describe('phase complete command', () => {
|
||||
// still in progress.
|
||||
const state = fs.readFileSync(path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
|
||||
assert.ok(
|
||||
!/Milestone complete/i.test(state),
|
||||
'a mid-milestone phase must not flip STATE.md to "Milestone complete" (#1591)',
|
||||
!/Milestone complete|All phases complete/i.test(state),
|
||||
'a mid-milestone phase must not flip STATE.md to milestone-complete (#1591)',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -3101,8 +3101,8 @@ describe('phase complete command', () => {
|
||||
|
||||
const state = fs.readFileSync(path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
|
||||
assert.ok(
|
||||
!/Milestone complete/i.test(state),
|
||||
'a mid-milestone phase must not flip STATE.md to "Milestone complete" — bold checklist (#1591)',
|
||||
!/Milestone complete|All phases complete/i.test(state),
|
||||
'a mid-milestone phase must not flip STATE.md to milestone-complete — bold checklist (#1591)',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -3182,8 +3182,8 @@ describe('phase complete command', () => {
|
||||
|
||||
const state = fs.readFileSync(path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
|
||||
assert.ok(
|
||||
!/Milestone complete/i.test(state),
|
||||
'a mid-milestone phase must not flip STATE.md to "Milestone complete" (#1752)',
|
||||
!/Milestone complete|All phases complete/i.test(state),
|
||||
'a mid-milestone phase must not flip STATE.md to milestone-complete (#1752)',
|
||||
);
|
||||
const tpMatch = state.match(/total_phases:\s*(\d+)/);
|
||||
assert.ok(tpMatch, 'STATE.md must carry a total_phases value after phase.complete');
|
||||
@@ -3747,7 +3747,7 @@ describe('phase complete command', () => {
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const state = fs.readFileSync(path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
|
||||
assert.ok(state.includes('Milestone complete'), 'plain Status field should be updated');
|
||||
assert.ok(state.includes('All phases complete'), 'plain Status field should be updated');
|
||||
assert.ok(state.includes('Not started'), 'plain Plan field should be updated');
|
||||
// Verify compound format preserved
|
||||
assert.ok(state.match(/Phase:.*of\s+1/), 'should preserve "of N" in compound Phase format');
|
||||
@@ -4169,7 +4169,7 @@ describe('#2028 — phase complete milestone-end + workstream guard', () => {
|
||||
// A complement phase numbered AFTER Phase 9 but executed first. Completing the
|
||||
// numerically-highest phase must not read as milestone-end while a lower phase
|
||||
// is still outstanding (the isLastPhase blocks only checked for HIGHER phases).
|
||||
test('does NOT stamp "Milestone complete" when a lower-numbered phase is still outstanding', () => {
|
||||
test('does NOT stamp "All phases complete" when a lower-numbered phase is still outstanding', () => {
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||
`# Roadmap\n\n- [ ] Phase 9: Introspection\n- [ ] Phase 10: Complement\n\n### Phase 9: Introspection\n**Goal:** baseline\n\n### Phase 10: Complement\n**Goal:** complement\n`
|
||||
@@ -4198,8 +4198,8 @@ describe('#2028 — phase complete milestone-end + workstream guard', () => {
|
||||
|
||||
const state = fs.readFileSync(path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
|
||||
assert.ok(
|
||||
!/Milestone complete/i.test(state),
|
||||
'STATE.md must NOT flip to "Milestone complete" while a lower phase is outstanding',
|
||||
!/Milestone complete|All phases complete/i.test(state),
|
||||
'STATE.md must NOT flip to milestone-complete while a lower phase is outstanding',
|
||||
);
|
||||
assert.ok(/Ready to plan/i.test(state), 'status should be "Ready to plan"');
|
||||
assert.match(
|
||||
@@ -4236,7 +4236,7 @@ describe('#2028 — phase complete milestone-end + workstream guard', () => {
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.is_last_phase, true, 'all lower phases complete → Phase 10 is milestone-end');
|
||||
const state = fs.readFileSync(path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
|
||||
assert.ok(/Milestone complete/i.test(state), 'status should be "Milestone complete"');
|
||||
assert.ok(/All phases complete/i.test(state), 'status should be "All phases complete"');
|
||||
});
|
||||
|
||||
// The lower-phase scan must not treat an unrelated checklist line that merely
|
||||
@@ -4318,9 +4318,9 @@ describe('#2028 — phase complete milestone-end + workstream guard', () => {
|
||||
rootState,
|
||||
'root STATE.md must NOT be written when --ws targets a workstream',
|
||||
);
|
||||
// The workstream's own STATE.md advanced (single phase → milestone complete).
|
||||
// The workstream's own STATE.md advanced (single phase → all phases complete).
|
||||
const wsState = fs.readFileSync(path.join(wsDir, 'STATE.md'), 'utf-8');
|
||||
assert.match(wsState, /Milestone complete/i, "the workstream's STATE.md should be the one updated");
|
||||
assert.match(wsState, /All phases complete/i, "the workstream's STATE.md should be the one updated");
|
||||
});
|
||||
|
||||
// The guard only fires in workstream mode — a flat project (no workstreams dir)
|
||||
|
||||
Reference in New Issue
Block a user