fix(#4806): unparseable VERIFICATION.md frontmatter reports a distinct parse error, never status missing (#4896)
* test(#4806): failing-first — unparseable VERIFICATION.md frontmatter is a parse error, not status missing / Field not found * fix(#4806): unparseable VERIFICATION.md frontmatter reports a distinct parse error — never 'missing' or 'Field not found' * test(#4806): census pin 66→67 — cmdFrontmatterGet's unparseable-frontmatter error is a new output({error}) call site * chore(#4806): backfill changeset PR number (4896) --------- Co-authored-by: sim <sim@local>
This commit is contained in:
@@ -1378,6 +1378,14 @@ function cmdFrontmatterGet(cwd: string, filePath: string, field: string | undefi
|
||||
// Pass the resolved path so a truncated file is named in the diagnostic and deduplicated
|
||||
// per file rather than per content digest (#1882, ADR-1411 wiring clause).
|
||||
const fm = extractFrontmatter(content, fullPath);
|
||||
// #4806: an unparseable frontmatter block is a distinct outcome — the file
|
||||
// HAS a frontmatter block but its YAML failed to parse. Reporting
|
||||
// "Field not found" tells the caller the key is absent, which is
|
||||
// indistinguishable from a file that genuinely lacks it.
|
||||
if ((fm as unknown as Record<symbol, unknown>)[FRONTMATTER_UNPARSEABLE] === true) {
|
||||
output({ error: 'Frontmatter is not parseable YAML — fix the syntax error in the frontmatter block', path: filePath }, raw, undefined);
|
||||
return;
|
||||
}
|
||||
if (field) {
|
||||
const value = fm[field];
|
||||
if (value === undefined) { output({ error: 'Field not found', field }, raw, undefined); return; }
|
||||
|
||||
@@ -49,7 +49,7 @@ import { isContainedIn } from './security.cjs';
|
||||
|
||||
const { output, error } = io;
|
||||
const { extractPhaseToken, scopeToPhase } = phaseId;
|
||||
const { extractFrontmatter } = frontmatterMod;
|
||||
const { extractFrontmatter, FRONTMATTER_UNPARSEABLE } = frontmatterMod;
|
||||
const { normalizeLineEndings } = coreUtilsMod;
|
||||
const { SCOPE } = planningScopeMod;
|
||||
type Scope = planningScopeMod.Scope;
|
||||
@@ -128,6 +128,15 @@ const VERIFICATION_ROUTING_TABLE: Record<string, VerificationRoute> = {
|
||||
next_action: 'No verification report found — the verify step never completed. Running execute-phase is safe here: it resumes at the verification gates and does not re-run plans that already have a SUMMARY.md (see #2868).',
|
||||
next_command: 'execute-phase',
|
||||
},
|
||||
// #4806: the report EXISTS but its frontmatter is not parseable YAML —
|
||||
// fundamentally different from "missing" (the verify step DID run; re-running
|
||||
// execute-phase cannot fix a YAML typo). Consumers treat any non-'passed'
|
||||
// status as blocking, so this value fails safe while telling the truth.
|
||||
unparseable: {
|
||||
status: 'unparseable',
|
||||
next_action: "The *-VERIFICATION.md frontmatter is not parseable YAML — fix the syntax error in the report itself. Re-running execute-phase cannot fix a YAML typo in an existing report.",
|
||||
next_command: '',
|
||||
},
|
||||
// INTERNAL SENTINEL: constructed when the file has a status value not in
|
||||
// VERIFIER_STATUSES. Never emitted by the verifier.
|
||||
unknown: {
|
||||
@@ -1028,6 +1037,16 @@ function readVerificationStatus(
|
||||
// same root cause as the false-clean class fixed elsewhere in #3707-CR.
|
||||
const content = normalizeLineEndings(fsImpl.readFileSync(filePath, 'utf-8'));
|
||||
fm = extractFrontmatter(content, filePath);
|
||||
// #4806: an unparseable frontmatter block is NOT "missing" — the file
|
||||
// exists and verification ran. Report a distinct status so the caller is
|
||||
// sent to fix the YAML, not to re-run execute-phase.
|
||||
if ((fm as unknown as Record<symbol, unknown>)[FRONTMATTER_UNPARSEABLE] === true) {
|
||||
return {
|
||||
status: 'unparseable',
|
||||
next_action: "The *-VERIFICATION.md frontmatter is not parseable YAML — fix the syntax error in the report itself. Re-running execute-phase cannot fix a YAML typo in an existing report.",
|
||||
next_command: '',
|
||||
};
|
||||
}
|
||||
const statusVal = fm['status'];
|
||||
// status is always a scalar string in a well-formed VERIFICATION.md frontmatter;
|
||||
// only accept string values — arrays and objects are not valid status values.
|
||||
|
||||
Reference in New Issue
Block a user