From a01df1f1105a8267f4408cbec993adcf630074e7 Mon Sep 17 00:00:00 2001 From: Jeremy McSpadden Date: Mon, 16 Mar 2026 14:52:17 -0500 Subject: [PATCH] fix: coerce completedAt to String in visualizer changelog sort YAML frontmatter parsers can return Date objects for ISO date strings instead of plain strings. This caused a TypeError when calling .localeCompare() on a Date object in the changelog sort. Wrap completedAt with String() at both assignment and sort to handle both native and JS parser paths safely. --- .../extensions/gsd/tests/visualizer-data.test.ts | 11 +++++++++++ src/resources/extensions/gsd/visualizer-data.ts | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/resources/extensions/gsd/tests/visualizer-data.test.ts b/src/resources/extensions/gsd/tests/visualizer-data.test.ts index 3aec834e1..85f740db7 100644 --- a/src/resources/extensions/gsd/tests/visualizer-data.test.ts +++ b/src/resources/extensions/gsd/tests/visualizer-data.test.ts @@ -165,6 +165,17 @@ assertTrue( "VisualizerData has changelog field", ); +// completedAt must be coerced to String() to handle YAML Date objects (issue #644) +assertTrue( + dataSrc.includes("String(summary.frontmatter.completed_at"), + "completedAt assignment coerces to String() for YAML Date safety", +); + +assertTrue( + dataSrc.includes("String(b.completedAt") && dataSrc.includes("String(a.completedAt"), + "changelog sort coerces completedAt to String() for YAML Date safety", +); + // Verify overlay source exists and imports data module const overlayPath = join(__dirname, "..", "visualizer-overlay.ts"); const overlaySrc = readFileSync(overlayPath, "utf-8"); diff --git a/src/resources/extensions/gsd/visualizer-data.ts b/src/resources/extensions/gsd/visualizer-data.ts index 5abf82e01..3e0c75182 100644 --- a/src/resources/extensions/gsd/visualizer-data.ts +++ b/src/resources/extensions/gsd/visualizer-data.ts @@ -379,7 +379,7 @@ async function loadChangelog(basePath: string, milestones: VisualizerMilestone[] path: f.path, description: f.description, })), - completedAt: summary.frontmatter.completed_at ?? '', + completedAt: String(summary.frontmatter.completed_at ?? ''), }; changelogCache.set(cacheKey, { mtime, entry }); @@ -388,7 +388,7 @@ async function loadChangelog(basePath: string, milestones: VisualizerMilestone[] } // Sort by completedAt descending - entries.sort((a, b) => (b.completedAt || '').localeCompare(a.completedAt || '')); + entries.sort((a, b) => String(b.completedAt || '').localeCompare(String(a.completedAt || ''))); return { entries }; }