* test(#2703): failing-first coverage for CRLF frontmatter strip in SUMMARY.md Drives the exported buildPlanningArtifacts seam. Rows for CRLF/LF parity, stacked blocks and a leading BOM fail against the current hand-rolled regex; the negative-space rows pin behavior that must not change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(#2703): strip GSD-2 frontmatter with the canonical parser buildSummaryMd matched the closing delimiter with a hardcoded bare \n, so a CRLF-authored task summary never matched and fell through to the raw-passthrough branch. The function then prepended its own block, emitting a SUMMARY.md with two stacked frontmatter blocks and no warning. Delegates to stripFrontmatter from frontmatter.cts -- the canonical, line-ending tolerant primitive this repo already deduplicated once (#2143) -- instead of adding another hand-rolled variant. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(#2703): strip only the first frontmatter block in gsd2 import Adversarial review caught a regression in the first cut: stripFrontmatter loops by design, so a summary body opening with a thematic-break-delimited section (--- / heading / ---) had that section silently deleted. The old pre-#2703 regex preserved it, so shipping the loop would have traded one silent corruption for another. Adds an explicit { once } option to the canonical primitive -- default behavior and the two existing callers are unchanged -- and has buildSummaryMd opt in. A GSD-2 summary is an arbitrary user document, not a GSD artifact with a known doubling failure mode, so a second block there is body content. This also makes the acceptance criterion exact: CRLF now produces the same result LF already produced, rather than a new result for both. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(#2703): backfill changeset pr number Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,8 +28,11 @@ import { realClock } from './clock.cjs';
|
||||
import coreUtilsMod = require('./core-utils.cjs');
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
import ioMod = require('./io.cjs');
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports -- frontmatter.cjs is an export= CommonJS module
|
||||
import frontmatterMod = require('./frontmatter.cjs');
|
||||
const { output } = ioMod;
|
||||
const { transliterateForSlug } = coreUtilsMod;
|
||||
const { stripFrontmatter } = frontmatterMod;
|
||||
|
||||
// ─── Types ───────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -290,9 +293,22 @@ function buildPlanMd(task: TaskInfo, phasePrefix: string, planPrefix: string, ph
|
||||
*/
|
||||
function buildSummaryMd(task: TaskInfo, phasePrefix: string, planPrefix: string): string {
|
||||
const raw = task.summary || '';
|
||||
// Strip GSD-2 frontmatter block (--- ... ---) if present
|
||||
const bodyMatch = raw.match(/^---[\s\S]*?---\n+([\s\S]*)$/);
|
||||
const body = bodyMatch ? bodyMatch[1].trim() : raw.trim();
|
||||
// Strip the GSD-2 frontmatter block via the canonical primitive (#2703). The
|
||||
// previous local regex required a bare `\n` after the closing `---`, so a
|
||||
// CRLF-authored summary never matched, fell through to the untouched-raw
|
||||
// branch, and had its frontmatter emitted a second time inside the body of
|
||||
// the document this function then wrapped in a fresh v1 block.
|
||||
//
|
||||
// `extractFrontmatter` — which the issue names — returns only the parsed
|
||||
// object and never the body, so it cannot serve this call site;
|
||||
// `stripFrontmatter` is the same module's canonical body primitive.
|
||||
//
|
||||
// `once` is load-bearing. A GSD-2 summary is an arbitrary user-authored
|
||||
// document, not a GSD artefact with a known frontmatter-doubling failure
|
||||
// mode, so a body opening with a thematic-break-delimited section
|
||||
// (`---` / heading / `---`) is far likelier than a corrupt second header —
|
||||
// and the default greedy loop would delete it without a trace.
|
||||
const body = stripFrontmatter(raw, { once: true }).trim();
|
||||
|
||||
return [
|
||||
'---',
|
||||
|
||||
Reference in New Issue
Block a user