Files
msd-core/src/verification-command-router.cts
Jakub Zych aa03fc7fa9 fix(verification): keep passed uat results across verifier re-runs
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
2026-10-11 01:44:30 +02:00

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,
};