fix: align planner plan contract with phase index (#3436)

* test: add planner/query contract regressions (#3430)

* fix: align planner plan contract with phase index (#3430)

* docs: add changeset for #3430

* fix: keep planner contract docs within size budget (#3430)

* docs: set changeset pr for #3430
This commit is contained in:
Tom Boucher
2026-05-12 18:50:35 -04:00
committed by GitHub
parent 29b3e470f0
commit a4f94c87aa
5 changed files with 83 additions and 2 deletions

View File

@@ -505,4 +505,27 @@ describe('phasePlanIndex', () => {
expect(planA!.depends_on).toEqual([]);
expect(planB!.depends_on).toEqual(['15-01']);
});
it('#3430: native phase-plan-index warns about noncanonical plan-shaped files it cannot index', async () => {
const phase16 = join(tmpDir, '.planning', 'phases', '16-warning');
await mkdir(phase16, { recursive: true });
await writeFile(join(phase16, '16-PLAN-01-eval-harness.md'), [
'---',
'phase: 16-warning',
'plan: 01',
'wave: 1',
'autonomous: true',
'depends_on: []',
'---',
'<objective>Noncanonical plan filename.</objective>',
].join('\n'));
const result = await phasePlanIndex(['16'], tmpDir);
const data = result.data as Record<string, unknown>;
expect(data.plans).toEqual([]);
expect(data.warnings).toEqual([
'Ignored noncanonical plan files: 16-PLAN-01-eval-harness.md',
]);
});
});

View File

@@ -278,6 +278,11 @@ export const phasePlanIndex: QueryHandler = async (args, projectDir, workstream)
const phaseFiles = await readdir(phaseDir);
const planFiles = phaseFiles.filter(f => f.endsWith('-PLAN.md') || f === 'PLAN.md').sort();
const summaryFiles = phaseFiles.filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md');
const nonCanonicalPlanFiles = phaseFiles.filter((f) => (
f.toLowerCase().endsWith('.md')
&& /(^|-)plan(-|\.)/i.test(f)
&& !(f.endsWith('-PLAN.md') || f === 'PLAN.md')
)).sort();
// Build set of plan IDs with summaries — match the planId derivation logic
const completedPlanIds = new Set(
@@ -433,6 +438,10 @@ export const phasePlanIndex: QueryHandler = async (args, projectDir, workstream)
let hasCheckpoints = false;
const warnings: string[] = [];
if (nonCanonicalPlanFiles.length > 0) {
warnings.push(`Ignored noncanonical plan files: ${nonCanonicalPlanFiles.join(', ')}`);
}
for (const raw of rawPlans) {
if (!raw.autonomous) {
hasCheckpoints = true;