Enforces fresh verification evidence before any completion claim. Use when about to claim "tests pass", "bug fixed", "done", "ready to merge", handing off wo...
---
name: ia-verification-before-completion
class: discipline
description: >-
Enforces fresh verification evidence before any completion claim. Use when
about to claim "tests pass", "bug fixed", "done", "ready to merge", handing
off work, or before editing when a request has ambiguous scope.
---
# Verification Before Completion
## The Rule
No completion claims without fresh verification evidence. If the verification command has not been run **immediately before the claim**, the claim cannot be made.
"Should pass", "probably works", and "looks correct" are not verification. Only command output confirming the claim counts (typically exit code 0). Pre-existing failures causing non-zero exits unrelated to the current changes: see "When Verification Fails" below.
## Pre-Verification Check
Before running verification, check the working tree state: `git status --porcelain`. If there are uncommitted changes unrelated to the current task, handle them first (commit, stash, or acknowledge) -- verification commits on top of a dirty tree create tangled history.
**Dirty tree + shared-module change → local green is not evidence.** Reproduce on a clean base ([isolated-verification.md](./references/isolated-verification.md)).
For delegated work: never trust the implementer subagent's own report -- spec compliance and quality are separate concerns, verify both. Confirm via the VCS diff that changes were actually made, then run the verification command directly; never relay the subagent's claim.
## Scope Confirmation (Pre-Edit Gate)
This gate fires at task start, before the first edit. When a request uses ambiguous spatial scope -- "migrate my project", "refactor the codebase", "update everywhere", "fix this across the app", "my code/repo/project" -- confirm the concrete scope before any Write or Edit. Imperative phrasing is not defined scope.
Run a breakdown command to surface the real blast radius:
```bash
rg -l 'pattern' | cut -d/ -f1 | sort | uniq -c | sort -rn # files per top-level dir
rg -l 'pattern' | xargs dirname | sort -u # affected directories
```
Present the result -- "This touches N files across M subsystems" -- with scope options: (a) everything, (b) just <subset>, (c) pick specific files. Ask via AskUserQuestion (Claude Code; load with ToolSearch `select:AskUserQuestion` if not loaded) or request_user_input (Codex); fall back to numbered options in chat. Do not start editing until the user commits to one option.
**When this applies**: any request whose scope could plausibly span more than one directory AND where the user has not enumerated files. For a request with explicit file paths, skip this gate.
## Gate Function
Before any success claim, run through these five steps:
| Step | Action | Example |
|------|--------|---------|
| **1. Identify** | What command proves this claim? The full chain -- build -> typecheck -> lint -> test -> security scan -> diff review, **stop on first failure** -- applies to ship-level claims (commit/push/PR-ready); for a single claim, run the proof command from the Common Claims table below. | `pytest tests/`, `npm test`, `curl -s localhost:3000/health` |
| **2. Run** | **Run it now, in this same message.** Output from an earlier turn is stale and does not count. | "I ran it earlier" fails this step |
| **3. Read** | Read the complete output, check exit code | Don't scan for "passed" -- read failure counts, warnings, errors |
| **4. Verify** | Does the output actually confirm the claim? | "42 passed, 0 failed" confirms "tests pass". "41 passed, 1 failed" does not. |
| **5. Claim** | Only now make the statement | "All 42 tests pass" with the evidence visible |
## Verification Strategies by Change Type
Type-check and unit tests are the universal baseline — not sufficient proof on their own. Match the strategy to the change:
| Change type | Required verification |
|-------------|----------------------|
| Frontend (component, page, form) | Start the dev server, exercise the feature in a browser, check the console; test the happy path AND one failure path |
| Backend handler / endpoint | `curl` the endpoint, check response shape and status code, hit at least one error path (invalid input, missing auth) |
| CLI tool | Run the binary with real inputs; check stdout, stderr, exit code. Run from `/tmp` to catch "only works from source" bugs |
| Infra / IaC (Terraform, Dockerfile, k8s) | `terraform plan` / `docker build` / `kubectl apply --dry-run=server`; review the diff before applying |
| Database migration | Run migration up, down, then up again against production-shape data |
| Refactoring (no behavior change) | Full test suite passes unchanged; public API surface diff shows no breakage (`grep` exported identifiers) |
| Library / package update | Run the consumer's test suite against the new version; check for deprecation warnings |
| Schema change | Old consumers parse the new shape (forward compat); new consumers handle old data still present (backward compat) |
| Documentation / prose | Read the rendered output; confirm links, formatting, and content match intent |
| Config with no validator | Validate syntax where possible (`jq .`, `yamllint`); otherwise read the file and confirm it matches the intended change |
| Non-runnable changes | `git diff`, confirm the diff matches intent, and state explicitly: "No automated verification available — verified by reading the diff." |
Reading code is not a strategy. If the table has no row for the change, fall back to the Non-runnable row. The principle holds even when no test suite applies: state what was checked and how.
## Adversarial Probes
For any change that touches production logic, include at least one adversarial probe in the verification. Pick the most relevant from:
- **Boundary value**: 0, -1, empty string, empty array, `null`, `undefined`, `MAX_INT`, 1-char unicode combining mark
- **Concurrency**: two parallel requests with the same identifier (for state changes, races, double-spend classes)
- **Idempotency**: run the same mutation twice; the second should either no-op or error cleanly, not corrupt state
- **Orphan op**: delete/update/get a nonexistent ID — does it 404/return-null as expected, or throw an internal error?
Exempt: docs changes, trivial typo fixes, pure rename refactors. Everything else: one probe minimum -- a report with zero adversarial probes is a happy-path confirmation, not verification.
## Review Staleness
Before shipping, check whether prior reviews (agent or human) are still valid. If commits landed after the last review (`git log --oneline <review-commit>..HEAD`), verify the new changes don't invalidate its conclusions: previously flagged issues are still fixed, and no new code contradicts the review's approval.
## When This Applies
- About to claim "tests pass", "build succeeds", or "bug fixed"
- About to commit, push, create a PR, or mark a task complete
- After completing each plan step / before starting the next file
- Reporting results to the user
- A subagent reports success on delegated work
## Red Flags
**Fantasy assessment auto-fail.** "Zero issues found" on a first implementation pass is a red flag, not a green light -- first implementations typically need 2-3 revision cycles, so "perfect on the first try" more likely means incomplete verification. Re-verify with a broader scope.
**Negative confirmation at signoff.** State what defect classes were checked and NOT found, not just what passed: "tests pass, no type errors, no lint warnings, no security flags in the changed files" proves the scope of verification; "tests pass" alone does not.
## Requirements vs Tests
"Tests pass" and "requirements met" are different claims: re-read the plan or requirements, create a line-by-line checklist, verify each item against the implementation, then report gaps or confirm completion. Passing tests prove the code works, not that the right code was written.
## Common Claims and Their Proof
| Claim | Required Proof |
|-------|---------------|
| "Tests pass" | Test runner output showing 0 failures, exit code 0 |
| "Build succeeds" | Build command output with exit code 0 |
| "Bug is fixed" | Original reproduction case now passes |
| "Feature complete" | All acceptance criteria verified individually |
| "No regressions" | Full test suite passes, not just new tests |
| "Regression test works" | Red-green cycle: test passes, revert fix, test fails, restore fix, test passes |
| "Linting clean" | Linter output showing 0 errors/warnings |
## When Verification Fails
If the output does not confirm the claim:
1. **Do not claim completion.** Report the actual failure output to the user.
2. **Do not retry the same verification** hoping for a different result.
3. **Return to implementation.** Fix the issue, then re-run from Step 1 of the Gate Function.
4. **Failure unrelated to the current changes** (pre-existing flaky test, environment issue)? State it explicitly with evidence: show the failure also occurs on the base branch or is a known issue.
## Pre-Commit Hook Failures
A failing pre-commit hook is a verification checkpoint, not an obstacle to route around. **`git commit --no-verify` is forbidden when the current session's changes caused the failure -- fix the root cause.** Permitted only when: (1) the failure reproduces on the base branch (show it), and (2) the user saw the failure first. A `--no-verify` the user never saw is a defeated check -- the same failure mode as claiming completion without evidence.
## Rationalization Prevention
Reasoning about the outcome instead of running the command means the Gate is not satisfied. "Should work", "trivial change", "just a refactor", "new tests pass" (not "all tests pass"), "CI will catch it" -- all the same failure mode: substituting confidence for evidence. Any satisfaction expression ("looks good", "seems correct", "that should do it") or any positive statement about completion -- including paraphrases and synonyms -- triggers the Gate: spirit over letter, rephrasing a claim to avoid the trigger words does not exempt it from verification.
## Completion Report Format
After verification passes, produce a structured report rather than an open-ended summary:
```
## Completion report
**Status**: DONE / DONE_WITH_CONCERNS / BLOCKED / NEEDS_CONTEXT
**Changes made**
- path/to/file.ts: [one-line description of what changed and why]
- path/to/other.ts: [one-line description]
**Things I didn't touch (intentionally)**
- [thing noticed but out of scope, with one-line reason]
- [adjacent issue deferred, with one-line reason]
**Potential concerns**
- [any risk, uncertainty, or open question the reviewer should know about]
- [or "none"]
**Verification evidence**
- [command]: [exit code / result summary]
```
DONE_WITH_CONCERNS makes the `Potential concerns` section mandatory; BLOCKED and NEEDS_CONTEXT must name the blocker or the missing information. The `Things I didn't touch` section is not optional -- if nothing was noticed, write "nothing noticed"; the goal is to prove scope was considered, not to pad the report.
## References
- [System-Wide Test Check](./references/system-wide-test-check.md) -- blast-radius verification for task completion (callbacks, integration, orphaned state)
## Integration
Referenced by `/ia-work` (before task completion, shipping, and merge/PR creation), `ia-receiving-code-review` (verify each fix before marking resolved), `ia-debugging` (before claiming a bug fixed), `ia-writing-tests` (tests as primary evidence), the `ia-design-iterator` and `ia-figma-design-sync` agents (verify rendering / Figma fidelity), and `/ia-verify` (full pre-PR verification pipeline).
don't have the plugin yet? install it then click "run inline in claude" again.
extracted implicit decision logic into explicit if-else branches for scope gate and pre-commit hooks, formalized inputs with specific tool names and access requirements, structured procedure into numbered steps with clear inputs/outputs, added edge cases for dirty trees and delegated work verification, created comprehensive decision points section covering when to run each gate and what rationalization phrases trigger the skill, and added output contract with mandatory completion report template and defect-class summary requirement.
this skill enforces a hard gate: no completion claim gets made without fresh verification evidence run immediately before the claim. "should pass", "probably works", and "looks correct" don't count. only command output with exit code 0 or explicit pass/fail metrics counts as evidence. use this before claiming tests pass, bugs fixed, builds succeed, features complete, or readiness to merge. also use this at task start when a request has vague scope (like "refactor everywhere") to confirm what actually gets touched before you write a line.
git status --porcelain output to detect uncommitted changesgit log, git diff to review what changed and check review stalenessidentify the verification command
pytest tests/, npm test, npm run build && npm run lint && npm test)run verification now, in this session
read the complete output
verify the output matches the claim
make the claim only if step 4 is yes
check working tree state
git status --porcelain; if there are uncommitted changes unrelated to the current task, handle them first (commit, stash, or acknowledge); if dirty tree + shared-module change exists, verification on dirty tree does not count as evidencefor delegated work: do not trust implementer subagent report
detect ambiguous scope
run blast-radius breakdown
rg -l 'pattern' | cut -d/ -f1 | sort | uniq -c | sort -rn # files per top-level dir
rg -l 'pattern' | xargs dirname | sort -u # affected directories
present scope options to user
identify change type
execute verification strategy
check if change touches production logic
select probe type
run probe and capture result
check for commits since last review
git log --oneline <review-commit>..HEAD; if commits exist, proceed to step 2; if empty, review is currentverify new changes don't invalidate review
structure the report
include negative confirmation at signoff
if ambiguous scope is present AND no explicit file list was provided: run breakdown, show results, ask user to pick, wait for answer. Do not edit until user commits to a scope option.
if scope is explicit (user said "update src/auth.ts and src/session.ts"): skip the gate, proceed directly to editing.
if scope is implicit but single-directory (user said "fix the API handler" and context makes clear which handler): skip the gate, proceed directly.
| Claim | Required Proof | Command |
|---|---|---|
| "Tests pass" | Test runner showing 0 failures, exit code 0 | pytest tests/, npm test, cargo test, repo-specific test runner |
| "Build succeeds" | Build command with exit code 0 | npm run build, cargo build, make build |
| "Bug is fixed" | Original reproduction case now passes | run the exact repro steps; must now succeed where it previously failed |
| "Feature complete" | All acceptance criteria verified individually | create line-by-line checklist from requirements/plan; verify each item |
| "No regressions" | Full test suite passes (not just new tests) | npm test or equivalent (full suite, not filtered) |
| "Regression test works" | Red-green cycle: test passes, revert fix, test fails, restore fix, test passes | run test, revert, re-run test, restore, re-run test; show all outputs |
| "Linting clean" | Linter output showing 0 errors and 0 warnings in changed files | npm run lint, eslint src/, pylint on changed files |
| "Type-check passes" | Typecheck runner with exit code 0, no errors in changed files | tsc, mypy, go vet on changed files |
| "Security scan clean" | Security tool output showing 0 critical/high issues in changed code | npm audit, cargo audit, snyk test on changed files |
git log --oneline <base>..HEAD); do not ignore it or ship without flagging itgit commit --no-verify is forbiddenif you catch yourself using any of these phrases, the gate is not satisfied: "should work", "trivial change", "just a refactor", "new tests pass" (not "all tests pass"), "CI will catch it", "looks good", "seems correct", "that should do it", "pretty sure", "can't imagine it breaking".
any positive statement about completion (or paraphrases/synonyms of completion claims) triggers the gate. rephrasing a claim to avoid the trigger words does not exempt it.
completion report (mandatory, after gate passes):
## completion report
**status**: DONE / DONE_WITH_CONCERNS / BLOCKED / NEEDS_CONTEXT
**changes made**
- path/to/file.ts: [one-line description of what changed and why]
- path/to/other.ts: [one-line description]
**things i didn't touch (intentionally)**
- [thing noticed but out of scope, with one-line reason]
- [adjacent issue deferred, with one-line reason]
- (or "nothing noticed")
**potential concerns**
- [any risk, uncertainty, or open question the reviewer should know about]
- (or "none")
**verification evidence**
- [command]: [exit code / result summary]
- [adversarial probe type]: [result]
- [defect-class summary]: "tests pass, no type errors, no lint warnings, no security flags in changed files"
if BLOCKED or NEEDS_CONTEXT: mandatory name-the-blocker or missing-information section.
if DONE_WITH_CONCERNS: mandatory Potential concerns section with at least one item.
verification strategy table (reference, used in procedure step 2.2):
| Change type | Required verification |
|---|---|
| Frontend (component, page, form) | Start dev server, exercise feature in browser, check console; test happy path + one failure path |
| Backend handler / endpoint | curl the endpoint, check response shape and status code; hit at least one error path (invalid input, missing auth) |
| CLI tool | Run binary with real inputs; check stdout/stderr/exit code; run from /tmp to catch "only works from source" bugs |
| Infra / IaC (Terraform, Dockerfile, k8s) | terraform plan / docker build / kubectl apply --dry-run=server; review diff before applying |
| Database migration | Run migration up, down, then up again against production-shape data |
| Refactoring (no behavior change) | Full test suite passes unchanged; public API surface diff shows no breakage (grep exported identifiers) |
| Library / package update | Run consumer's test suite against new version; check for deprecation warnings |
| Schema change | Old consumers parse new shape (forward compat); new consumers handle old data (backward compat) |
| Documentation / prose | Read rendered output; confirm links, formatting, content match intent |
| Config with no validator | Validate syntax where possible (jq ., yamllint); otherwise read file and confirm it matches intended change |
| Non-runnable changes | git diff, confirm diff matches intent, state explicitly: "No automated verification available - verified by reading the diff." |
you know the skill worked when: