fix(#2400): warn on planned-phase no-op + sync progress.total_plans (#2552)

* fix(#2400): warn on planned-phase no-op + sync progress.total_plans

Bug A: When STATE.md Current Position has no recognized labels (narrative
prose), emit a warning field so the workflow detects the no-op instead
of continuing with stale state.

Bug B: Sync progress.total_plans in the YAML frontmatter when a plan
count is provided, preventing contradictory state between frontmatter
(0) and body (actual count). This writes the explicitly-provided count,
not a re-derivation from disk (#500 safe).

Closes #2400

* docs(#2400): backfill changeset PR number (2552)
This commit is contained in:
Tom Boucher
2026-07-23 07:38:21 -04:00
committed by GitHub
parent 1482dc5ce0
commit 2bcfaa2e27
3 changed files with 22 additions and 1 deletions

View File

@@ -1117,6 +1117,19 @@ function plannedPhaseCore(
);
if (body !== beforePos) updated.push('Current Position');
// #2400 Bug B: sync progress.total_plans to the frontmatter when a plan count
// is given. This writes the explicitly-provided count — it is NOT a re-derivation
// from disk (#500 RC1 is about deriving from a half-planned snapshot, not about
// refusing to write an explicitly-passed argument).
if (intent.planCount !== null && intent.planCount !== undefined && hasFrontmatter) {
const fmProgress = (existingFm['progress'] as Record<string, unknown> | undefined) || {};
if (fmProgress['total_plans'] !== intent.planCount) {
fmProgress['total_plans'] = intent.planCount;
existingFm['progress'] = fmProgress;
updated.push('progress.total_plans');
}
}
return { content: reassemble(body), updated };
}

View File

@@ -2529,7 +2529,10 @@ function cmdStatePlannedPhase(cwd: string, phaseNumber: string | number, planCou
return result.content;
}, cwd, { resync: false, deriveProgressKeys: true });
output({ updated, phase: phaseNumber, plan_count: planCount }, raw, updated.length > 0 ? 'true' : 'false');
const result = updated.length === 0
? { updated, phase: phaseNumber, plan_count: planCount, warning: 'STATE.md Current Position has no recognized labels — transition was a no-op. Verify STATE.md uses the canonical labeled format (Status:, Total Plans in Phase:, etc.).' }
: { updated, phase: phaseNumber, plan_count: planCount };
output(result, raw, updated.length > 0 ? 'true' : 'false');
}
/**