Verification Before Completion
Evidence-before-claims methodology. Requires fresh execution of verification commands, reading full output, and confirming results before asserting completion.
Tags
README
verification
Evidence-before-claims methodology for task completion.
What It Does
Installs a rules file that requires fresh proof before asserting anything works:
- Identify the command that proves the claim
- Execute it fresh (no cached results)
- Read the full output including exit codes
- Verify the output actually supports the claim
- Report with evidence attached
Covers tests, linting, builds, bug fixes, deployments, and type checking. Prevents common failures like proxy claims, stale results, and partial verification.
Manual Installation
# Global (all projects)
mkdir -p ~/.claude/rules
cp rules/verification.md ~/.claude/rules/verification.md
cp rules/evidence-tier-gate.md ~/.claude/rules/evidence-tier-gate.md
cp rules/config-change-detection.md ~/.claude/rules/config-change-detection.md
# Project-level
mkdir -p .claude/rules
cp rules/verification.md .claude/rules/verification.md
cp rules/evidence-tier-gate.md .claude/rules/evidence-tier-gate.md
cp rules/config-change-detection.md .claude/rules/config-change-detection.md
Files
| File | Description |
|---|---|
rules/verification.md |
5-step verification process with evidence requirements table |
rules/evidence-tier-gate.md |
Mechanical L1/L2/L3 evidence gate: a completion claim with no fresh L1 artifact auto-fails |
rules/config-change-detection.md |
Hash-of-config pattern for re-verifying expensive automation when config drifts |
Will install
| Path | Action | Target | Type |
|---|---|---|---|
rules/verification.md | → | rules/verification.md | rule |
rules/evidence-tier-gate.md | → | rules/evidence-tier-gate.md | rule |
rules/config-change-detection.md | → | rules/config-change-detection.md | rule |
Dependencies
No dependencies.
Required by
No other module depends on this one.
Included in presets
Install this module
Agent prompt
Recommended for agent users -- hands the whole install off to your assistant.
Fetch https://cd23a9be.ccgm-site.pages.dev/modules/verification.md and install this module into my Claude Code setup.
Native plugin marketplace
One command via the native plugin marketplace -- additive, does not merge settings.json.
claude plugin install verification@ccgm
The marketplace path is additive, not a replacement: it installs commands, agents, and skills as native plugin components, but it does not perform the bash installer's deep settings.json merge, and it does not write the always-loaded global CLAUDE.md context. Rules are only injected via an opt-in SessionStart hook rather than being auto-loaded. Use the bash installer when those pieces matter to you.
Files
rule (3)
rules/verification.md
# Verification Before Completion **Iron Law:** NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE. Violating the letter of this rule is violating the spirit of this rule. Never assert that something works, passes, or is fixed without fresh proof. **Announce at start:** "I'm using the verification discipline. Running fresh checks before claiming completion." ## The 5-Step Verification Process Before claiming any task is complete: 1. **Identify** the specific command or action that proves the claim 2. **Execute** the command fresh (do not rely on cached or prior results) 3. **Read** the full output, including exit codes and failure counts 4. **Verify** the output actually supports the claim you are making 5. **Report** with evidence attached (not just "tests pass") ## What Counts as Evidence | Claim | Required Evidence | |-------|-------------------| | "Tests pass" | Fresh test run output showing pass count and 0 failures | | "Lint is clean" | Fresh linter output showing 0 errors, 0 warnings | | "Build succeeds" | Build command output with exit code 0 | | "Bug is fixed" | Reproduction steps that previously failed now succeed | | "No regressions" | Full test suite output, not just the new tests | | "Types check" | Type checker output showing 0 errors | | "Deployed successfully" | Deployment URL responding with expected content | | "Agent completed" | The actual diff, test run, or artifact the subagent claims to have produced - never the subagent's self-report alone | ## Rules ### Run Fresh - Do NOT rely on previous runs, even from earlier in the same session - Do NOT assume passing one check means another also passes (lint passing does not mean types check) - Do NOT trust partial output (10/12 tests passing means 2 are failing) ### Read Fully - Check the exit code, not just the visible output - Scroll through the full output, not just the summary line - Look for warnings that might indicate problems even if the overall status is "pass" ### Report Honestly - If 1 out of 100 tests fails, do not say "tests pass" - If the build succeeds with warnings, mention the warnings - If you could not run a verification step, say so explicitly ## Common Verification Failures - **Proxy claims**: "Lint passed so the code must be correct" - wrong, they check different things - **Stale results**: "Tests passed earlier" - they might not pass now after your changes - **Partial verification**: Running a subset of tests instead of the full suite - **Assumed verification**: "This change is trivial, it can't break anything" - run the checks anyway - **Trusting subagent self-reports**: A subagent saying "DONE" is not evidence. Read the diff, run the tests yourself, or inspect the artifact ## Rationalizations That Mean You Are About to Claim Completion Without Evidence | You are about to say... | The reality is... | |-------------------------|-------------------| | "The change is too small to bother re-running" | Small changes break builds all the time. Run it. | | "I ran it earlier in this session" | State has changed since then. Run it again. | | "Type check passed, that's good enough" | Type check is not a test run. It is not a lint run. They catch different classes of problems. | | "The subagent said it succeeded" | A subagent's summary describes what it intended. Read the diff. | | "CI will catch anything I miss" | CI is a last-resort. Local checks are faster and cheaper. Do not ship the blame. | | "I can see from the code it will work" | Reading is not running. If you did not see exit code 0, you did not verify. | ## Red Flags Stop and run the check if you catch yourself: - Saying "tests pass" without having just run them - Claiming a fix works before seeing fresh output - Trusting an earlier-in-session result after editing code - Reporting a subagent's result as your own without verifying the artifact - Summarizing what you did instead of showing the output - "I'll just do one more thing before I run the full suite" ## When to Verify - Before every commit - Before claiming a bug is fixed - Before reporting a task as complete - After any refactoring, no matter how minor - After resolving merge conflicts
rules/evidence-tier-gate.md
# Evidence-Tier Gate **Iron Law:** A COMPLETION CLAIM WITH NO L1 EVIDENCE AUTO-FAILS. PASTE THE FRESH OUTPUT OR DO NOT CLAIM DONE. This is the mechanical enforcement layer for `verification.md`. That rule says "evidence before claims." This rule defines what counts as evidence, ranks it, and makes a missing-evidence claim a hard failure rather than a judgment call. There is no "I'm confident enough to skip the paste" — the gate is binary. **Announce at start:** "Applying the evidence-tier gate. Every completion claim ships its L1 artifact or it does not ship." ## The Three Tiers | Tier | What it is | Counts as proof? | |------|-----------|------------------| | **L1** | Fresh artifact captured **this session**: command output with exit code, test run summary, screenshot, log line, HTTP response. The machine produced it, just now. | **Yes.** This is the only tier that satisfies a claim. | | **L2** | Reasoned argument: "the diff is small," "the types line up," "this can't break X because Y." | **No.** A hypothesis, not a result. | | **L3** | Bare assertion: "tests pass," "it works," "fixed." No artifact, no argument. | **No.** This is the failure the gate exists to catch. | L2 and L3 are not evidence. They are the *claim*. The gate requires the claim be backed by L1. ## The Gate Before emitting any of these phrases — "done," "complete," "it works," "tests pass," "fixed," "deployed," "passing," "green," "verified" — run the gate: 1. **Is there an L1 artifact for this exact claim, captured this session?** - No → **AUTO-FAIL.** Do not make the claim. Run the proving command, or downgrade the language to "I changed X; have not yet verified." - Yes → continue. 2. **Does the artifact actually show success** (exit code 0, 0 failures, expected content), not just "ran without crashing"? - No → the artifact contradicts the claim. Report the failure, do not claim done. - Yes → **paste the artifact alongside the claim.** A claim without its pasted L1 artifact is treated as L3. An unpasted L1 artifact is not L1 to the reader. If you ran it but did not show it, you asserted — that is L3. Paste it. ## Pre-Claim Checklist For each claim type, the L1 artifact that proves it. This extends the evidence table in `verification.md` — that table names the evidence; this one is the gate you run before speaking. | Before you say... | Paste this L1 artifact | |-------------------|------------------------| | "tests pass" | Fresh test-run tail: pass count + "0 failed" + exit code | | "lint is clean" | Linter output: "0 errors, 0 warnings" + exit code 0 | | "build succeeds" | Build command tail + exit code 0 | | "types check" | Type-checker output: "0 errors" + exit code 0 | | "bug is fixed" | The repro command's output now succeeding (and ideally the failing run before) | | "no regressions" | **Full** suite output, not the new tests alone | | "deployed" | `curl -I` / HTTP response showing the new behavior live | | "UI renders correctly" | A screenshot captured this session | | "subagent completed" | The subagent's actual diff / test run / artifact — never its self-report | If the row you need is not here, the rule still holds: name the command, run it fresh, paste the output. ## Rationalizations That Mean You Are About to Ship an L3 Claim | You are about to say... | The reality is... | |-------------------------|-------------------| | "I'm confident the tests pass" | Confidence is L2. The gate takes L1. Run it and paste it. | | "Re-running just to paste it is wasteful" | The paste *is* the deliverable. An unverifiable claim is worth less than no claim. | | "I described the output, that's enough" | Describing output is L3. Show the lines, including the exit code. | | "It passed, I just didn't copy the output" | Then to the reader it is L3. Re-run and paste, or do not claim. | | "The reasoning is airtight" | Airtight reasoning is still L2. Reasoning predicts; artifacts prove. | | "I'll paste it if asked" | The gate fires before the claim, not after a challenge. Paste now. | ## Red Flags Stop and capture L1 if you catch yourself: - Typing "done" / "passing" / "works" with no command output above it - Pasting an artifact from earlier in the session and calling it fresh - Summarizing what an artifact *would* show instead of showing it - Treating a green exit code you remember but did not re-run as current - Forwarding a subagent's "DONE" as your own completion without its artifact
rules/config-change-detection.md
# Config Change Detection
**Iron Law:** RE-VERIFY WHEN CONFIG CHANGES. NEVER ASSUME LAST RUN'S RESULT STILL APPLIES.
This is a specialization of the verification discipline for automation that is dangerous on first run or after configuration drift. Deploys, migrations, new integrations, any workflow that depends on an external contract (env file, deploy config, wrangler.toml, CI secrets) must re-verify when that contract changes. A green run last week does not prove anything about today's config.
## The Hash-of-Config Pattern
Record a hash of the relevant config section. Before running the automation, compare the stored hash against a fresh hash. If they differ (or no hash exists), the automation is in one of two states:
- **FIRST_RUN**: no marker file exists. The config has never been verified on this machine.
- **CONFIG_CHANGED**: marker file exists, but the current hash does not match. The config drifted since last verification.
In either state, run a dry-run or full verification step before the destructive/expensive action, then update the marker.
### Minimal Implementation
```bash
# Hash the relevant config slice (section of CLAUDE.md, plus workflow files)
CONFIG_HASH=$(
{
sed -n '/^## Deploy/,/^## /p' CLAUDE.md
cat .github/workflows/deploy.yml 2>/dev/null
cat wrangler.toml 2>/dev/null
} | shasum -a 256 | awk '{print $1}'
)
MARKER="$HOME/.claude/projects/${PROJECT_SLUG}/deploy-confirmed"
if [ ! -f "$MARKER" ] || [ "$(cat "$MARKER")" != "$CONFIG_HASH" ]; then
# FIRST_RUN or CONFIG_CHANGED - run dry-run / verification
echo "Config changed or never verified. Running dry-run..."
run_dry_run_and_await_user_confirmation
echo "$CONFIG_HASH" > "$MARKER"
fi
# Config verified, proceed with automation
run_real_action
```
## Marker File Strategy
Store markers under `~/.claude/projects/{slug}/{operation}-confirmed`, one file per operation. The file contains the hash only. The directory structure isolates per-project state without polluting the repo.
Rules:
- **One marker per operation**, not per project. `deploy-confirmed`, `migrate-confirmed`, `env-sync-confirmed` are separate.
- **Never commit markers to git**. They are local trust state, not shared truth.
- **Include every file that affects the outcome** in the hash input. Missing a file (a new workflow step, a new env var) defeats the pattern.
- **Order the hash input deterministically**. Sort file paths or use a fixed concatenation order so identical configs always produce identical hashes.
## What to Hash
For each operation type, define the config surface that actually controls behavior:
| Operation | Hash Input |
|-----------|-----------|
| Deploy | Deploy section of CLAUDE.md + deploy workflow files + platform config (wrangler.toml, vercel.json, fly.toml) |
| Migration | Schema directory + `package.json` db scripts + migration runner config |
| Integration | `.env.example` + integration config file + any setup scripts |
| Package rebuild | `package.json` + lockfile + build script section of CLAUDE.md |
If you cannot name the files that control the operation, the operation is not safe to automate yet. Identify them first.
## When to Apply
Use this pattern when all of the following are true:
- The automation is **expensive** (deploy, migration, large build) or **destructive** (drops data, rewrites history, charges money)
- The automation depends on **external config** that a human might edit without re-running
- A **dry-run or verification step** exists that costs less than the real run
Do not apply it to:
- Idempotent read-only checks (tests, lints, type checks) - these are already cheap to re-run fresh
- One-off scripts that never run twice
- Operations where the config IS the command (e.g., `rm -rf dist` has no external config to drift)
## Integration with the Verification Discipline
The base verification rule ("Evidence before claims") requires fresh proof every time. Config change detection is the mechanism that tells you **when fresh proof needs to include a full dry-run**, not just a check of the last run's artifact.
Without this mechanism, agents default to one of two failure modes:
- **Always dry-run** (slow, defeats automation) or
- **Never dry-run after first success** (ships config drift to production)
The hash marker resolves the tradeoff: automated when safe, interactive when the contract changed.
## Rationalizations That Mean You Are About to Skip Re-Verification
| You are about to say... | The reality is... |
|-------------------------|-------------------|
| "The deploy worked last week, it will work now" | Config can drift without you touching the deploy script. A new env var, a changed secret, a workflow edit. |
| "I only changed docs, not the deploy config" | If the hash input includes only deploy files, the docs change will not trigger re-verify. If it does include CLAUDE.md, re-verify anyway - you might have documented a change that is not yet reflected in code. |
| "The dry-run is annoying" | Annoying is cheaper than a broken production deploy or a migration run against the wrong database. |
| "I can eyeball the config diff" | Eyeballing is not verification. A hash comparison is deterministic; human memory is not. |
## Red Flags
Stop and re-run the dry-run if you catch yourself:
- Running a deploy command without checking whether the marker exists
- Copying a hash check from another project without updating the hash input list for this operation
- Deleting or ignoring a marker file to "just get past it"
- Committing a marker file to the repo (it is local trust state, not shared truth)