From 1fe316477d7e0804df1dc35f6f67e85957c6b758 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Sun, 5 Apr 2026 01:04:46 -0400 Subject: [PATCH] fix(preferences): warn on silent parse failure for non-frontmatter files (#3310) * fix(preferences): warn when preferences file lacks YAML frontmatter delimiters parsePreferencesMarkdown() silently returned null for non-frontmatter content, causing all preferences (including git.isolation: none) to be ignored. The system fell back to default worktree isolation with no indication to the user. Now emits a stderr warning when a non-empty preferences file cannot be parsed due to missing --- fences, so users know their file was skipped. Fixes #2036 Co-Authored-By: Claude Opus 4.6 (1M context) * ci: retrigger after perf test fix * fix(test): update preferences test to expect heading+list parser result The heading+list fallback parser (parseHeadingListFormat) now handles non-frontmatter markdown content like "## Git\n- isolation: none", so the test should expect a parsed object instead of null. Co-Authored-By: Claude Opus 4.6 (1M context) * chore: retrigger CI * fix: add missing closing brace for test block in preferences.test.ts The 'unrecognized format warning' test block was missing its closing }); after the finally clause, causing TS1005 syntax error at line 535. Co-Authored-By: Claude Sonnet 4.6 * fix(preferences): include 'unrecognized format' in warn-once message (#2373) The test filters for "unrecognized format" but the message only said "does not use YAML frontmatter delimiters". Add the phrase so the warn-once regression test can find its expected output. Co-Authored-By: Claude Sonnet 4.6 --------- Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: trek-e --- src/resources/extensions/gsd/preferences.ts | 8 ++++++-- src/resources/extensions/gsd/tests/preferences.test.ts | 10 ++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/resources/extensions/gsd/preferences.ts b/src/resources/extensions/gsd/preferences.ts index 93ac6f1f0..4d88f0271 100644 --- a/src/resources/extensions/gsd/preferences.ts +++ b/src/resources/extensions/gsd/preferences.ts @@ -224,9 +224,13 @@ export function parsePreferencesMarkdown(content: string): GSDPreferences | null return parseHeadingListFormat(content); } - if (!_warnedUnrecognizedFormat) { + // Warn when a non-empty file exists but lacks frontmatter delimiters (#2036). + if (content.trim().length > 0 && !_warnedUnrecognizedFormat) { _warnedUnrecognizedFormat = true; - console.warn("[parsePreferencesMarkdown] preferences.md exists but uses an unrecognized format — skipping."); + console.warn( + "[GSD] Warning: preferences file has unrecognized format — content does not use YAML frontmatter delimiters (---). " + + "Wrap your preferences in --- fences. See https://github.com/gsd-build/gsd-2/issues/2036", + ); } return null; } diff --git a/src/resources/extensions/gsd/tests/preferences.test.ts b/src/resources/extensions/gsd/tests/preferences.test.ts index 7c263ef26..79aa04ef3 100644 --- a/src/resources/extensions/gsd/tests/preferences.test.ts +++ b/src/resources/extensions/gsd/tests/preferences.test.ts @@ -412,6 +412,16 @@ test("unrecognized format warning is emitted at most once (#2373)", () => { } }); +test("parsePreferencesMarkdown parses heading+list format without frontmatter (#2036)", () => { + // A GSD agent recovery session wrote preferences in markdown heading+list + // format instead of YAML frontmatter. Since the heading+list fallback parser + // was added, this format is now handled gracefully. + const content = "## Git\n\n- isolation: none\n"; + const result = parsePreferencesMarkdown(content); + assert.notEqual(result, null, "heading+list content should be parsed"); + assert.deepStrictEqual(result!.git, { isolation: "none" }); +}); + // ── Experimental preferences ───────────────────────────────────────────────── test("experimental.rtk: true is accepted and stored", () => {