Merge pull request #1261 from j2h4u/fix/frontmatter-status-preservation

fix(state): preserve frontmatter status when body Status field is missing
This commit is contained in:
Tom Boucher
2026-03-20 22:14:49 -04:00
committed by GitHub
2 changed files with 37 additions and 2 deletions

View File

@@ -733,9 +733,20 @@ function stripFrontmatter(content) {
}
function syncStateFrontmatter(content, cwd) {
// Read existing frontmatter BEFORE stripping — it may contain values
// that the body no longer has (e.g., Status field removed by an agent).
const existingFm = extractFrontmatter(content);
const body = stripFrontmatter(content);
const fm = buildStateFrontmatter(body, cwd);
const yamlStr = reconstructFrontmatter(fm);
const derivedFm = buildStateFrontmatter(body, cwd);
// Preserve existing frontmatter status when body-derived status is 'unknown'.
// This prevents a missing Status: field in the body from overwriting a
// previously valid status (e.g., 'executing' → 'unknown').
if (derivedFm.status === 'unknown' && existingFm.status && existingFm.status !== 'unknown') {
derivedFm.status = existingFm.status;
}
const yamlStr = reconstructFrontmatter(derivedFm);
return `---\n${yamlStr}\n---\n\n${body}`;
}

View File

@@ -475,6 +475,30 @@ describe('STATE.md frontmatter sync', () => {
assert.ok(content.includes('status: paused'), 'frontmatter should reflect latest status');
});
test('preserves frontmatter status when body Status field is missing', () => {
// Simulate: frontmatter has status: executing, but body lost Status: field
fs.writeFileSync(
path.join(tmpDir, '.planning', 'STATE.md'),
`---
status: executing
milestone: v1.0
---
# Project State
**Current Phase:** 03
**Current Plan:** 03-02
`
);
// Any writeStateMd triggers syncStateFrontmatter — use state update on a field that exists
runGsdTools('state update "Current Plan" "03-03"', tmpDir);
const content = fs.readFileSync(path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
assert.ok(content.includes('status: executing'), 'should preserve existing status, not overwrite with unknown');
assert.ok(!content.includes('status: unknown'), 'should not contain unknown status');
});
test('round-trip: write then read via state json', () => {
fs.writeFileSync(
path.join(tmpDir, '.planning', 'STATE.md'),