fix(3266): preserve wave 0 and bucket plans by depends_on DAG in phase-plan-index (#3276)
* fix(3266): preserve wave 0 and bucket plans by depends_on DAG in phase-plan-index Fixes two cooperating bugs in the phase-plan-index builder: 1. Wave 0 collapse: `parseInt(...) || 1` coerced parsed value `0` to `1` due to JS falsy default. Fixed with `Number.isNaN` guard. 2. depends_on ignored: wave-bucketing used only the `wave:` frontmatter field. Now replaced with Kahn's topological-level algorithm over `depends_on`: source nodes (no in-phase deps) → lowest level; each plan's level = max(deps' levels) + 1. Declared `wave:` that disagrees with computed level emits a non-fatal warning on the result. Cycle detection throws GSDError. `PlanInfo` gains `depends_on: string[]`. `PhasePlanIndex` gains `warnings?: string[]`. Both TS (`sdk/src/query/phase.ts`) and CJS twin (`get-shit-done/bin/lib/phase.cjs`) fixed identically. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add changeset for #3276 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(phase): resolve depends_on against canonical plan id (#3276 CR) Build a secondary `canonicalToId` index alongside `planMap` so that a dependency declared as '03-01' resolves to a descriptive plan stored under '03-01-auth-hardening', preventing silent wave-ordering failures. Applied at both DAG construction sites in phase.cjs and the SDK's phase.ts (k014 parity). Regression test added. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -129,7 +129,7 @@ describe('execute-phase docs: user-facing wave flag', () => {
|
||||
});
|
||||
|
||||
describe('phase-plan-index: wave grouping behavior', () => {
|
||||
test('phase-plan-index groups plans by wave frontmatter field', () => {
|
||||
test('phase-plan-index groups plans by wave (DAG-bucketing: P002 depends on P001)', () => {
|
||||
// allow-test-rule: behavioral — calls gsd-tools and asserts structured output
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
@@ -138,12 +138,13 @@ describe('phase-plan-index: wave grouping behavior', () => {
|
||||
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-alpha');
|
||||
fs.mkdirSync(phaseDir, { recursive: true });
|
||||
|
||||
// Wave 1 plan
|
||||
// Wave 1 plan — no dependencies
|
||||
fs.writeFileSync(path.join(phaseDir, 'P001-PLAN.md'), [
|
||||
'---',
|
||||
'wave: 1',
|
||||
'objective: First wave task',
|
||||
'autonomous: true',
|
||||
'depends_on: []',
|
||||
'---',
|
||||
'',
|
||||
'# Plan 001',
|
||||
@@ -153,12 +154,14 @@ describe('phase-plan-index: wave grouping behavior', () => {
|
||||
'<task>Do the thing</task>',
|
||||
].join('\n'));
|
||||
|
||||
// Wave 2 plan
|
||||
// Wave 2 plan — depends on P001 so DAG places it in level 1 → wave 2
|
||||
fs.writeFileSync(path.join(phaseDir, 'P002-PLAN.md'), [
|
||||
'---',
|
||||
'wave: 2',
|
||||
'objective: Second wave task',
|
||||
'autonomous: true',
|
||||
'depends_on:',
|
||||
' - P001',
|
||||
'---',
|
||||
'',
|
||||
'# Plan 002',
|
||||
@@ -185,6 +188,8 @@ describe('phase-plan-index: wave grouping behavior', () => {
|
||||
assert.ok(p002, 'P002 should be in plans array');
|
||||
assert.equal(p001.wave, 1, 'P001 should have wave=1');
|
||||
assert.equal(p002.wave, 2, 'P002 should have wave=2');
|
||||
// No mismatch warning: declared wave 2 matches topo level 2
|
||||
assert.strictEqual(data.warnings, undefined, 'no warnings when declared wave matches DAG');
|
||||
} finally {
|
||||
cleanup(tmpDir);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user