* enhance(#2572): run the verify-summary artifact check against phase SUMMARYs (W025) The artifact<->git check has existed since the beginning but was only ever pointed at .planning/research/SUMMARY.md (new-project.md:1145, new-milestone.md:425). Phase summaries -- the ones that actually claim "I created these files" -- were never checked. - extract verifySummaryCore from cmdVerifySummary: same checks, lifted out of the output() wrapper so callers consume {passed, checks, errors} directly instead of shelling out and re-parsing JSON; cmdVerifySummary is now a thin adapter over it - validate.health gains advisory W025 per phase SUMMARY with missing files Advisory only: appends to warnings[], never touches status escalation beyond the channel's own warning semantics, the repair set, or readVerificationStatus. Resolves both open questions from triage: (a) commits_exist is deliberately NOT surfaced -- its hash pattern matches any hex-shaped token in prose, too loose to show a user; (b) a phase carries N per-plan summaries plus a legacy bare SUMMARY.md, so all of them are checked via the repo-wide filter. * chore(#2572): add changeset fragment * feat(#2572): move the SUMMARY artifact check to phase completion Responds to the #2685 review. Three substantive changes. Seam (Blocker 2). The check now runs in cmdPhaseComplete, the seam the issue body cited (src/phase.cts:~1745), not validate.health. That channel does exist: cmdPhaseComplete declares warnings[], populates it from the UAT/VERIFICATION pre-scan, and emits it. The cycle objection raised against the earlier deviation holds for state.cts only -- verify.cts has no transitive import path to phase.cts, so phase.cts -> verify.cjs adds no cycle (verified over every src/*.cts). Moving it also retires the retroactive firing across all historical phases: this fires once, at completion, for the phase being completed. Extraction (Blocker 1). Pattern 2 now excludes [ and ] from its path class. The SUMMARY templates prescribe a YAML flow sequence (key-files.created: [a.ts, b.ts]) and the label matches case-insensitively, so the class previously captured the literal [ and produced a candidate that can never exist on disk -- firing on healthy projects built from the templates GSD itself ships. Stripping frontmatter was the other offered remedy; measured across all three shipped templates it is a no-op on top of the exclusion, so it is not carried. Consequence named in-code: the key-files block still is not read, which needs a real frontmatter parse. Also narrowed to the noise classes confirmed in review -- globs, bare hostnames, and paths resolving outside the project are skipped rather than reported, and the containment guard the old comment claimed now actually exists. Budget (Majors 1 and 3). verifySummaryCore takes a checkCommits option; phase completion passes false, so the discarded git cat-file probes are not spawned at all. It also passes Infinity, so every referenced file is reported instead of the first two -- a summary listing twelve files of which nine are missing now says nine, not zero. The verb keeps its historical 2-file default. Tests (Major 2). The vacuous fixtures are gone with the health block. The replacements use /-bearing paths that genuinely extract, and each fix was mutation-checked: un-anchoring pattern 2, dropping the glob, hostname or containment filter, forcing commit checking on, and re-capping at 2 each fail at least one test. * docs(#2572): describe the phase-completion SUMMARY artifact check The W025 text under /gsd-health is withdrawn with the health seam; the check is documented where it now runs, under `phase complete` in docs/CLI-TOOLS.md. Both the docs and the changeset previously overclaimed: they said a referenced file not on disk is warned about, while at most two candidates per SUMMARY were ever examined. The cap is gone at this seam, so the claim now holds -- and the text states the limits that remain, rather than leaving them to be discovered: the key-files frontmatter block is not read, commit hashes are not resolved, and globs, URLs, bare hostnames and out-of-project paths are skipped rather than reported. --------- Co-authored-by: CI Rebase Check <ci@gsd-redux>
This commit is contained in:
@@ -62,6 +62,11 @@ import uatPredicate = require('./uat-predicate.cjs');
|
||||
const { evaluateUatPassed } = uatPredicate;
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports -- verification.cjs is an export= CommonJS module
|
||||
import verificationMod = require('./verification.cjs');
|
||||
// #2572: the artifact↔disk core behind the `verify-summary` verb. `verify.cts`
|
||||
// has no transitive import path back to `phase.cts`, so this edge introduces no
|
||||
// cycle (the reverse edge, `state.cts → verify.cjs`, would).
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports -- verify.cjs is an export= CommonJS module
|
||||
import verifyMod = require('./verify.cjs');
|
||||
const { readVerificationStatus } = verificationMod;
|
||||
|
||||
const { planningDir, withPlanningLock, listAvailableWorkstreams, getActiveWorkstream } =
|
||||
@@ -1757,6 +1762,50 @@ function cmdPhaseComplete(cwd: string, phaseNum: string, raw: boolean): void {
|
||||
* warnings are surfaced this run, not a blocked or corrupted completion. */
|
||||
}
|
||||
|
||||
// #2572: artifact↔disk advisory for the SUMMARYs of the phase being completed.
|
||||
//
|
||||
// A SUMMARY asserts "I created these files". Nothing checked that claim for
|
||||
// phase summaries — the `verify-summary` verb has existed since the beginning
|
||||
// but was only ever pointed at `.planning/research/SUMMARY.md`. An interrupted
|
||||
// or over-reported phase therefore counted toward 100% silently.
|
||||
//
|
||||
// Joins the same ADVISORY channel as the pre-scan above: findings land in
|
||||
// `warnings[]` (rendered by execute-phase.md's "If has_warnings is true"
|
||||
// step), never in the completion GATE (readVerificationStatus below).
|
||||
// Completion is never blocked.
|
||||
//
|
||||
// `checkCommits: false` — only the file-existence half is surfaced here, so
|
||||
// the `git cat-file` probes would be spawned and their result discarded. The
|
||||
// hash pattern is a loose `\b[0-9a-f]{7,40}\b` that matches any hex-shaped
|
||||
// token in prose, too noisy to put in front of a user even as a warning.
|
||||
//
|
||||
// `Infinity` — report every referenced file, not the CLI verb's default first
|
||||
// two, so a phase that lists twelve files and landed three says so. The verb
|
||||
// keeps its 2-file default; only this caller opts out of the cap.
|
||||
try {
|
||||
const phaseDirRel = phaseInfo['directory'] as string;
|
||||
// `summaries` arrives pre-sorted from the phase locator, so warning order is
|
||||
// deterministic across platforms rather than readdir-dependent.
|
||||
const summaryNames = (phaseInfo['summaries'] as string[] | undefined) || [];
|
||||
for (const summaryName of summaryNames) {
|
||||
const v = verifyMod.verifySummaryCore(
|
||||
cwd,
|
||||
`${phaseDirRel}/${summaryName}`,
|
||||
Infinity,
|
||||
{ checkCommits: false },
|
||||
);
|
||||
const missing = v.checks.files_created.missing;
|
||||
if (missing.length > 0) {
|
||||
warnings.push(
|
||||
`${summaryName}: references ${missing.length} file(s) not on disk: ${missing.join(', ')}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* best-effort, same posture as the #2245 pre-scan above: an unreadable
|
||||
* SUMMARY means one fewer advisory this run, never a blocked completion. */
|
||||
}
|
||||
|
||||
let nextPhaseNum: string | null = null;
|
||||
let nextPhaseName: string | null = null;
|
||||
let isLastPhase = true;
|
||||
|
||||
Reference in New Issue
Block a user