fix(#4187): status reader resolves a bare VERIFICATION.md like resolve-file (#4388)

* test(#4187): bare VERIFICATION.md regression matrix for the status surface

Both query verbs must agree on every row: bare file, suffixed variants,
missing file, other-dir placement, and the staleness seam. Row 1 is the
failing-first regression from the issue repro.

* fix(#4187): status reader resolves a bare VERIFICATION.md like resolve-file

readVerificationStatus and its internal staleness check
(findStaleVerificationSummary) called the shared resolver without
allowBare, so a phase whose only report was a bare VERIFICATION.md read
as missing and was told to re-run execute-phase while
verification.resolve-file, determinePhaseStatus, and both init
verification_path projectors all resolved the same file. Both call
sites now pass allowBare: true, matching the other five; tier order
(dashed > bare) is unchanged, so only bare-only directories change
behavior.

* fix(#4187): correct call-site counts in allowBare docblocks

Adversarial review caught the comments claiming five of six call sites
opted in; the current tree has six call sites with four previously
passing allowBare — the two module-internal status-path sites were both
holdouts, not one.

* chore(#4187): changeset for the bare VERIFICATION.md status fix

* chore(#4187): backfill PR number in changeset

---------

Co-authored-by: sim <sim@local>
This commit is contained in:
Tom Boucher
2026-09-06 11:54:03 -04:00
committed by GitHub
parent fd4aac5670
commit 708d9a0b82
3 changed files with 251 additions and 11 deletions

View File

@@ -467,12 +467,25 @@ interface ResolveVerificationFileOptions {
* determinePhaseStatus and two `verification_path` projectors in
* `src/init.cts`) additionally accept a BARE `VERIFICATION.md` — a form
* this module's own two callers (`findStaleVerificationSummary`,
* `readVerificationStatus`) have never accepted, because a bare filename
* `readVerificationStatus`) had never accepted, because a bare filename
* carries no phase token and `.endsWith('-VERIFICATION.md')` structurally
* excludes it. Defaults to `false`, which is byte-for-behavior identical to
* the pre-existing (non-optioned) resolver — no call-site edit required for
* the two callers in THIS module. Set `true` only from a call site whose
* pre-fix behavior already accepted a bare match.
* callers that do not want the bare tier.
*
* #4187: that historical asymmetry was drift, not contract. Six call sites
* grew around the shared resolver and four opted in
* (`cmdVerificationResolveFile`, `determinePhaseStatus`, both init
* `verification_path` projectors) — the two module-internal status-path
* call sites (`readVerificationStatus`, `findStaleVerificationSummary`)
* did not, so `query verification.resolve-file` resolved a bare report in
* a directory where `query verification.status` answered `missing` and
* recommended re-running execute-phase for an already-verified phase.
* Since #4187 those two pass `true` as well: every reader of the report
* set now recognizes the bare name. Tier order is unchanged — a dashed
* candidate (canonical or not, if it belongs to THIS phase) still outranks
* the bare match — so this only changes directories whose SOLE report is
* bare.
*/
allowBare?: boolean;
/**
@@ -720,9 +733,16 @@ function findStaleVerificationSummary(
// or sentinel-numbered canonically-shaped file cannot outrank this
// phase's own (possibly non-canonical) report. #3511: phaseDirName scopes
// the fallback path to this same phase (see resolveVerificationFile docs).
// #4187: allowBare — this staleness seam must see the same report set the
// status reader sees, or a bare report could never read `stale` while its
// dashed twin could (two answers from one verb).
const phaseDirName = path.basename(phaseDir);
const phaseToken = extractPhaseToken(phaseDirName);
const verificationFile = resolveVerificationFile(phaseFiles, { phaseToken, phaseDirName });
const verificationFile = resolveVerificationFile(phaseFiles, {
allowBare: true,
phaseToken,
phaseDirName,
});
if (!verificationFile) return { determined: true, stale: false };
const summaryFiles = (scanPhasePlans(phaseDir) as { summaryFiles: string[] }).summaryFiles
@@ -766,7 +786,9 @@ function findStaleVerificationSummary(
* 1. Find the phase's verification report via `resolveVerificationFile`
* (canonical `<phase-token>-VERIFICATION.md` preferred; falls back to the
* alphabetically-first `*-VERIFICATION.md` that belongs to THIS phase when
* none is canonical — #3357/#3511). If none → status 'missing'.
* none is canonical — #3357/#3511; and, when the directory's only report
* is a bare `VERIFICATION.md`, that file — #4187, matching
* `verification.resolve-file`). If none → status 'missing'.
* 2. Extract `status` from FRONTMATTER ONLY via the shared extractFrontmatter
* parser (DEFECT.FRONTMATTER-SCALAR-BROAD-GREP fix — parser anchors at byte 0).
* If no frontmatter block or no `status` key → status 'missing'.
@@ -814,7 +836,12 @@ function readVerificationStatus(
// sentinel-numbered canonically-shaped file cannot outrank this phase's
// own (possibly non-canonical) report. #3511: baseName also scopes the
// fallback path to this same phase (see resolveVerificationFile docs).
verificationFile = resolveVerificationFile(entries, { phaseToken, phaseDirName: baseName });
// #4187: allowBare — the status reader must recognize a bare
// `VERIFICATION.md` exactly like `verification.resolve-file`,
// `determinePhaseStatus`, and the init verification_path projectors
// already do; without it a verified phase reported `missing` and
// recommended re-running execute-phase.
verificationFile = resolveVerificationFile(entries, { allowBare: true, phaseToken, phaseDirName: baseName });
} catch {
// Directory unreadable → treat as missing
verificationFile = null;