A phase whose human UAT passed could loop forever between execute-phase and verify-work. A gap-closure plan, or a later phase editing a covered file, made the report stale; verify-work only recorded a passed UAT against human_needed; the re-run verifier could not see the UAT file and re-emitted human_needed; and execute-phase's human_needed branch then rewrote *-UAT.md with every row back to [pending]. - verification.uat-evidence: recorded UAT rows plus the covered implementation files changed since the UAT. The verifier (Step 8b) treats a row that passed on unchanged code as verified and re-lists only affected rows, each with a retest_reason. - verification.seed-uat: merges the report's human items into *-UAT.md. Existing rows are kept byte-for-byte; a pass is reset only with a recorded reason. - verification.canonicalize-uat: the single human_needed -> passed flip, gated on the uat-only row predicate; refuses a stale report. - verification.status reports stale_reason; init.progress lists every executed-but-stale phase in reverify_phases. - verify-work and progress re-run the verifier themselves for a stale report instead of routing to execute-phase; execute-phase records a gap-closure checkpoint's UAT answers before dispatching the verifier. Fail-closed properties are unchanged: a malformed fingerprint and a real change to covered code still read stale. Emitted-Drift-Ack-Growth: execute-phase.md — verify_phase_goal gains the gap-closure uat-record pointer and the merge-not-overwrite human_needed branch; the uat template it replaced moved into verification.seed-uat Emitted-Drift-Ack-Growth: msd-verifier.md — new step 8b pointer to the lazily loaded verifier-uat-evidence reference plus the human_verified frontmatter key Emitted-Drift-Ack-Growth: progress.md — route v.stale now re-verifies every stale executed phase in place instead of naming one command
73 lines
3.1 KiB
TypeScript
73 lines
3.1 KiB
TypeScript
/**
|
|
* Verification-status subcommand router.
|
|
* Routes `verification.status <phaseDir>` to verification.cmdVerificationStatus.
|
|
*
|
|
* Note: `verification` (reads verifier-emitted status) is distinct from `verify`
|
|
* (runs verification checks like plan-structure/artifacts). Keep them separate.
|
|
*
|
|
* ADR-457 build-at-publish: source in src/verification-command-router.cts,
|
|
* compiled to msd-core/bin/lib/verification-command-router.cjs (gitignored).
|
|
*/
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
import cjsCommandRouterAdapter = require('./cjs-command-router-adapter.cjs');
|
|
const { routeCjsCommandFamily } = cjsCommandRouterAdapter;
|
|
|
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
|
|
interface VerificationModule {
|
|
cmdVerificationStatus(cwd: string, phaseDirArg: string | undefined, raw: boolean): void;
|
|
cmdVerificationResolveFile(cwd: string, phaseDirArg: string | undefined, raw: boolean): void;
|
|
cmdVerificationFingerprint(cwd: string, phaseDirArg: string | undefined, files: string[], raw: boolean): void;
|
|
}
|
|
|
|
/** UAT-evidence verbs live in their own module (it imports `verification`, not the reverse). */
|
|
interface UatEvidenceModule {
|
|
cmdVerificationUatEvidence(cwd: string, phaseDirArg: string | undefined, files: string[], raw: boolean): void;
|
|
cmdVerificationSeedUat(cwd: string, phaseDirArg: string | undefined, raw: boolean): void;
|
|
cmdVerificationCanonicalizeUat(cwd: string, phaseDirArg: string | undefined, raw: boolean): void;
|
|
}
|
|
|
|
interface RouteVerificationCommandOptions {
|
|
verification: VerificationModule;
|
|
uatEvidence: UatEvidenceModule;
|
|
args: string[];
|
|
cwd: string;
|
|
raw: boolean;
|
|
error: (message: string) => void;
|
|
}
|
|
|
|
// ─── Implementation ───────────────────────────────────────────────────────────
|
|
|
|
const VERIFICATION_SUBCOMMANDS = ['status', 'resolve-file', 'fingerprint', 'uat-evidence', 'seed-uat', 'canonicalize-uat'];
|
|
|
|
function routeVerificationCommand({
|
|
verification,
|
|
uatEvidence,
|
|
args,
|
|
cwd,
|
|
raw,
|
|
error,
|
|
}: RouteVerificationCommandOptions): void {
|
|
routeCjsCommandFamily({
|
|
args,
|
|
subcommands: VERIFICATION_SUBCOMMANDS,
|
|
unsupported: {},
|
|
error,
|
|
unknownMessage: (_subcommand: string, available: string[]) =>
|
|
`Unknown verification subcommand. Available: ${available.join(', ')}`,
|
|
handlers: {
|
|
status: () => verification.cmdVerificationStatus(cwd, args[2], raw),
|
|
'resolve-file': () => verification.cmdVerificationResolveFile(cwd, args[2], raw),
|
|
fingerprint: () => verification.cmdVerificationFingerprint(cwd, args[2], args.slice(3), raw),
|
|
'uat-evidence': () => uatEvidence.cmdVerificationUatEvidence(cwd, args[2], args.slice(3), raw),
|
|
'seed-uat': () => uatEvidence.cmdVerificationSeedUat(cwd, args[2], raw),
|
|
'canonicalize-uat': () => uatEvidence.cmdVerificationCanonicalizeUat(cwd, args[2], raw),
|
|
},
|
|
});
|
|
}
|
|
|
|
export = {
|
|
routeVerificationCommand,
|
|
};
|