Files
msd-core/gsd-core/references/verifier-phase-gates.md
Tom Boucher d30c99bc92 chore(#3421): delete orphan verify-phase workflow, migrate live gates to verifier (#3422)
* chore(#1892): delete orphan verify-phase workflow, migrate live gates to verifier reference

* test(#1892): retarget structural suites from verify-phase.md to verifier-phase-gates.md

* chore(#1892): reword retired-workflow mentions for removed-but-needed lint

* test(#1892): correct stale surface labels in retargeted suites

* docs(#1892): add verifier-phase-gates row to locale inventories

* chore(#3421): backfill changeset pr number

---------

Co-authored-by: sim <sim@local>
2026-08-13 21:22:03 -04:00

9.8 KiB

Verifier Phase Gates

Loaded eagerly by agents/gsd-verifier.md (<required_reading>). Carries the three verification-time gates that lived in the retired verify-phase workflow (#1892 / epic #1891 F7): decision-coverage validation (#2492), the test-quality audit, and infrastructure-phase human-verification scoping (#2504). Run each at its named agent step; gsd_run is the launcher shim defined in the agent's own Step 1 block.

verify_decisions — Decision Coverage Gate (run after Step 6, requirements coverage)

**Decision coverage validation gate (issue #2492).**

After requirements coverage, also check that each trackable CONTEXT.md <decisions> entry shows up somewhere in the shipped artifacts (plans, SUMMARY.md, files modified by the phase, or recent commit subjects on the phase branch).

This gate is non-blocking / warning only by deliberate asymmetry with the plan-phase translation gate. The plan-phase gate already blocked at translation time, so by the time verification runs every decision has either been translated or explicitly deferred. This gate's job is to surface decisions that were translated but vanished during execution — that's a soft signal because "honors a decision" is a fuzzy substring heuristic, and we don't want a paraphrase miss to fail an otherwise good phase.

Skip if workflow.context_coverage_gate is explicitly set to false (absent key = enabled). Also skip cleanly when CONTEXT.md is missing or has no <decisions> block.

GATE_CFG=$(gsd_run query config-get workflow.context_coverage_gate 2>/dev/null || echo "true")
if [ "$GATE_CFG" != "false" ]; then
  CONTEXT_PATH=$(ls "${PHASE_DIR}"/*-CONTEXT.md 2>/dev/null | head -1)  # #2962: not a for-glob (zsh aborts)
  DECISION_RESULT=$(gsd_run query check.decision-coverage-verify "${PHASE_DIR}" "${CONTEXT_PATH}")
fi

The handler returns JSON { skipped, blocking: false, total, honored, not_honored: [...], message }.

Reporting: Append the handler's message (a ### Decision Coverage section) to VERIFICATION.md regardless of outcome — even when all decisions are honored, recording the count helps reviewers spot drift over time. Set decision_coverage in the verification result to {honored, total, not_honored: [...]} so downstream tooling can read it.

Status impact: none. The decision gate does NOT influence the gaps_found / human_needed / passed decision tree in Step 9. Its findings are warnings the user reviews and may act on by re-opening the phase or by acknowledging the decision was abandoned intentionally.

audit_test_quality (run after Step 7b, alongside anti-patterns)

**Verify that tests PROVE what they claim to prove.**

This step catches test-level deceptions that pass all prior checks: files exist, are substantive, are wired, and tests pass — but the tests don't actually validate the requirement.

1. Identify requirement-linked test files

From PLAN and SUMMARY files, map each requirement to the test files that are supposed to prove it.

2. Disabled test scan

For ALL test files linked to requirements, search for disabled/skipped patterns:

grep -rn -E "it\.skip|describe\.skip|test\.skip|xit\(|xdescribe\(|xtest\(|@pytest\.mark\.skip|@unittest\.skip|#\[ignore\]|\.pending|it\.todo|test\.todo" "$TEST_FILE"

Rule: A disabled test linked to a requirement = requirement NOT tested.

  • 🛑 BLOCKER if the disabled test is the only test proving that requirement
  • ⚠️ WARNING if other active tests also cover the requirement

3. Circular test detection

Search for scripts/utilities that generate expected values by running the system under test:

grep -rn -E "writeFileSync|writeFile|fs\.write|open\(.*w\)" "$TEST_DIRS"

For each match, check if it also imports the system/service/module being tested. If a script both imports the system-under-test AND writes expected output values → CIRCULAR.

Circular test indicators:

  • Script imports a service AND writes to fixture files
  • Expected values have comments like "computed from engine", "captured from baseline"
  • Script filename contains "capture", "baseline", "generate", "snapshot" in test context
  • Expected values were added in the same commit as the test assertions

Rule: A test comparing system output against values generated by the same system is circular. It proves consistency, not correctness.

4. Expected value provenance (for comparison/parity/migration requirements)

When a requirement demands comparison with an external source ("identical to X", "matches Y", "same output as Z"):

  • Is the external source actually invoked or referenced in the test pipeline?
  • Do fixture files contain data sourced from the external system?
  • Or do all expected values come from the new system itself or from mathematical formulas?

Provenance classification:

  • VALID: Expected value from external/legacy system output, manual capture, or independent oracle
  • PARTIAL: Expected value from mathematical derivation (proves formula, not system match)
  • CIRCULAR: Expected value from the system being tested
  • UNKNOWN: No provenance information — treat as SUSPECT

5. Assertion strength

For each test linked to a requirement, classify the strongest assertion:

Level Examples Proves
Existence toBeDefined(), != null Something returned
Type typeof x === 'number' Correct shape
Status code === 200 No error
Value toEqual(expected), toBeCloseTo(x) Specific value
Behavioral Multi-step workflow assertions End-to-end correctness

If a requirement demands value-level or behavioral-level proof and the test only has existence/type/status assertions → INSUFFICIENT.

6. Coverage quantity

If a requirement specifies a quantity of test cases (e.g., "30 calculations"), check if the actual number of active (non-skipped) test cases meets the requirement.

Reporting — add to VERIFICATION.md:

### Test Quality Audit

| Test File | Linked Req | Active | Skipped | Circular | Assertion Level | Verdict |
|-----------|-----------|--------|---------|----------|-----------------|---------|

**Disabled tests on requirements:** {N} → {BLOCKER if any req has ONLY disabled tests}
**Circular patterns detected:** {N} → {BLOCKER if any}
**Insufficient assertions:** {N} → {WARNING}

Impact on status: Any BLOCKER from test quality audit → overall status = gaps_found (Step 9 rule 1), regardless of other checks passing.

identify_human_verification — infrastructure/foundation scoping (apply at Step 8)

First: determine if this is an infrastructure/foundation phase.

Infrastructure and foundation phases — code foundations, database schema, internal APIs, data models, build tooling, CI/CD, internal service integrations — have no user-facing elements by definition. For these phases:

  • Do NOT invent artificial manual steps (e.g., "manually run git commits", "manually invoke methods", "manually check database state").
  • Mark human verification as N/A with rationale: "Infrastructure/foundation phase — no user-facing elements to test manually."
  • Set human_verification: [] and do not produce a human_needed status solely due to lack of user-facing features.
  • Only add human verification items if the phase goal or success criteria explicitly describe something a user would interact with (UI, CLI command output visible to end users, external service UX).
  • Exception — behavior-unverified truths still count. A truth marked ⚠️ PRESENT_BEHAVIOR_UNVERIFIED (a state transition or a cancellation/cleanup/ordering invariant with no test exercising it) is a behavioral-evidence gap, not an artificial user-facing step. Record it in behavior_unverified_items and emit a human-verification item for it even on an infrastructure/foundation phase — these invariants are exactly where infra phases hide runtime state leaks. Such a truth drives human_needed; the auto-pass-UAT shortcut applies only to the absence of user-facing UX, never to a behavior-unverified invariant.

How to determine if a phase is infrastructure/foundation:

  • Phase goal or name contains: "foundation", "infrastructure", "schema", "database", "internal API", "data model", "scaffolding", "pipeline", "tooling", "CI", "migrations", "service layer", "backend", "core library"
  • Phase success criteria describe only technical artifacts (files exist, tests pass, schema is valid) with no user interaction required
  • There is no UI, CLI output visible to end users, or real-time behavior to observe

If the phase IS infrastructure/foundation: auto-pass UAT — skip the human verification items list entirely, except any ⚠️ PRESENT_BEHAVIOR_UNVERIFIED truth (see exception above), which still emits a human-verification item and drives human_needed. Log:

## Human Verification

N/A — Infrastructure/foundation phase with no user-facing elements.
All acceptance criteria are verifiable programmatically.

If the phase IS user-facing: only flag items that genuinely require a human — per the Step 8 always/uncertain lists already in the agent. Do not invent steps.

Lazy references

  • Per-stack verification patterns: before Step 4 (artifact verification) on an unfamiliar stack, Read ~/.claude/gsd-core/references/verification-patterns.md — the grep catalog for React/Next.js components, API routes, database schema, and the universal stub patterns. Read it lazily (only the sections for the stack under verification); it is too large to load wholesale on every run.
  • Canonical report shape: the emitted VERIFICATION.md follows @~/.claude/gsd-core/templates/verification-report.md — the template whose Guidelines and row shapes src/uat.cts treats as canonical when consuming verification output.