src/core-skills/bmad-review/references/lens-verification-gap.md
Goal: Find changed behavior that could break without reliable verification catching it. Ask one question — "if the behavior this change is supposed to produce broke where it's actually used, would verification fail?" Do not hunt for correctness bugs, but report genuine problems you notice while tracing verification.
The main verification gap shapes are:
Before applying the non-behavioral stop to a test-only change, check whether it removes or weakens verification of deterministic behavior. If so, continue to Step 2; it is eligible for a broken-verification gap even though production behavior is unchanged.
If the change is non-behavioral, stop here and return zero findings ([]); when the output format includes a markdown report, note there that the change is non-behavioral (a caller's exact zero-findings output contract wins over this note). Call it non-behavioral only when the changed code does not alter return values, thrown errors, caller-visible side effects, or observable state (including iteration order and emitted messages). After the changed code meets that test, stop; do not inspect callers or tests for extra confirmation.
Common non-behavioral examples: formatting, comments, whitespace; pure renames; trivial getters/setters and pass-throughs; type-only or compiler-enforced changes with no runtime effect; etc.
Identify what behavior changed compared to the previous version: output, side effect, branch, error path, schema/event shape, config default, validation/authorization rule, external contract, etc. If the change affects more than one behavior, handle each separately.
Treat broad-impact changes as behavioral even when no single changed line looks important: dependency, toolchain, build/config, data-file, etc.
Seek verification of behavior, not the literal text of implementation or documentation artifacts. Tests may assert exact content or structure when they execute deterministic construction or transformation and inspect its output, including generated prompts and request payloads. Do not seek phrase-existence assertions over hand-authored prompts, skills, documents, or source files.
For LLM-backed behavior, stop at the inference boundary: do not require invoking a model or judging its semantic response. Deterministic request construction and response handling remain eligible without live inference; existing inference tests are not precedent for more.
Trace the changed behavior to the places that observe it. Start with direct callers and registered entry points (routes, commands, DI), contract consumers (schemas, events, APIs, database readers), and reverse-dependency info if already available.
Follow a path only while the changed behavior is reachable and unverified. Stop when a test at that boundary would fail, the consumer does not observe the changed behavior, or the next hop is guesswork (dynamic dispatch, reflection, outside-repo consumers, etc.). Prefer the nearest observable boundary, often one to three hops away, especially across contract, integration, or service edges. If there are more than five similar consumers, group obvious repeats and check representative paths; expand only when a consumer observes the behavior differently.
For each consumer, name the smallest realistic regression this consumer would observe: invert the branch, drop the default, omit the field, return the old error code, skip the integration call, etc. This is the Demonstration. If no such regression exists, drop the path; untested downstream code is not a finding.
A Missing-adoption gap qualifies not by the adoption failure alone but by a supersession signal: the change gives clear evidence the new behavior is meant to replace the local one — PR intent, naming or docs, a replaced sibling site, deleted duplicate logic, or a test defining the new rule — and the local site shares the same observable contract. Without a supersession signal and a shared observable contract, it is a refactor suggestion, not a verification-gap finding. Once both hold, check whether any test for that site would flag the non-adoption; missing coverage of the non-adoption is the gap itself, not a disqualifier.
Find and read the relevant test. Ask whether the Demonstration would make an assertion fail.
Regression gap or Broken-verification gap.Missing-adoption gap.A test counts only if it runs normally and an assertion observes the changed output, branch, or contract. These do not count: no execution; success/no-throw/snapshot-only checks; mock/log-call checks; human-only checks; tests that mock away the integration; e2e tests that pass through without checking the changed output; stale assertions or fixtures.
For example, expect(x ?? DEFAULT).toBe(DEFAULT) passes when x is missing.
Common patterns:
Before writing a finding, re-open the specific tests or search results the finding relies on. Verify the Demonstration would not make any test you checked fail, or that the absence claim is backed by the symbol/import-reference search. Do not claim more than you verified; drop any finding you cannot ground.
Explain why the test misses the bug using what the test sets up and checks.
Do not report: compiler/type-checker-enforced cases; behavior already verified by an integration, contract, or e2e test; implementation-detail or mock-only tests; low coverage or a missing test file by itself; legacy untested code the change did not affect.
Report genuine problems you noticed while tracing verification, even if they are not verification gaps — emit them as findings with gap_shape: "other". This permits reporting what you already reached, not extra hunting.
Emit each gap with the canonical fields plus this lens's extras:
location — the changed surface: the exact behavior or contract that changed, file:linetrigger_condition — the gap, in one lineguard_snippet — the missing verification: the precise assertion or check that's absent, optionally with the test shape that would close it, fit to the repo's own way of verifying — don't impose a generic test pyramidpotential_consequence — the concrete thing that ships wrong: the regression the checked evidence would not catch, or the site that should use the new behavior and doesn't, with why the tests you checked would not failgap_shape — "regression-gap", "missing-adoption-gap", "broken-verification-gap", or "other"consumer — the impacted consumer or site, named concretely with file:line (e.g. "the createInvoice mutation used by the billing dashboard at billing/dashboard.ts:88", not "callers of this function")evidence — what you actually checked: what the relevant test asserts with file:line; or, if none, the symbol/import-reference searches run and their result; for a broken-verification gap, the apparent test and why it does not countFor gap_shape: "other" findings the four canonical fields suffice (description only); consumer and evidence are optional. An empty array is valid when the change is non-behavioral or every changed behavior is verified. When this lens comes up clean and a markdown report is presented, its clean statement for this lens is exactly: No verification gaps found.