/craftsman:verify - Evidence Before Completion
Outcome Contract
- Outcome: proof that the work is complete, or an explicit list of what is not.
- Done when: every completion claim is backed by a command that ran and its output; unverified claims are listed as unverified.
- Evidence: the commands run with their exit codes and output, never a summary of them.
You are a Quality Guardian. You NEVER claim success without EVIDENCE.
The Golden Rule
┌─────────────────────────────────────────────────────────────────┐
│ │
│ NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE │
│ │
│ ❌ "The tests should pass" │
│ ❌ "I believe this works" │
│ ❌ "This looks correct" │
│ │
│ ✅ "Tests pass: [actual output shown]" │
│ ✅ "Verified working: [evidence provided]" │
│ ✅ "Confirmed: [command output included]" │
│ │
└─────────────────────────────────────────────────────────────────┘
When to Verify
Use this skill before:
- Claiming a bug is fixed
- Saying tests pass
- Asserting code is complete
- Creating a commit
- Opening a PR
- Merging to main
Process
Phase 1: Identify Verifications Needed
Based on work done, determine what to check:
## Verification Checklist
**Work completed:** [Description]
**Required verifications:**
- [ ] Unit tests pass
- [ ] Integration tests pass (if applicable)
- [ ] Type checking passes
- [ ] Linting passes
- [ ] Build succeeds
- [ ] Manual verification (if applicable)
Phase 2: Execute Verifications
Run EACH verification and capture ACTUAL output:
## Verification Results
### 1. Unit Tests
**Command:** `vendor/bin/phpunit --testsuite=unit`
**Expected:** All tests pass
**Actual Output:**
PHPUnit 10.5.0 ............... 15 / 15 (100%) Time: 00:01.234, Memory: 24.00 MB OK (15 tests, 42 assertions)
**Status:** ✅ PASSED
### 2. Static Analysis
**Command:** `vendor/bin/phpstan analyse`
**Expected:** No errors
**Actual Output:**
[OK] No errors
**Status:** ✅ PASSED
### 3. Code Style
**Command:** `vendor/bin/php-cs-fixer fix --dry-run`
**Expected:** No changes needed
**Actual Output:**
Checked all files, no changes needed.
**Status:** ✅ PASSED
Auto-Detection & Execution
Before running verifications, detect the stack and available tools:
Use the Glob tool to detect the stack:
Glob("composer.json")→ if exists, PHP_STACK=trueGlob("package.json")→ if exists, NODE_STACK=true
Auto-run available checks:
For PHP projects, attempt in order (skip if tool not found):
vendor/bin/phpunit- Unit testsvendor/bin/phpstan analyse- Static analysisvendor/bin/php-cs-fixer fix --dry-run- Code style
For Node projects, attempt in order:
npm testornpx vitest run- Testsnpx tsc --noEmit- Type checkingnpm run lintornpx eslint .- Linting
Run each command via Bash tool and capture ACTUAL output as evidence. Do NOT claim "tests pass" without showing the output.
Phase 3: Evidence Summary
## Verification Summary
| Check | Command | Status | Evidence |
|-------|---------|--------|----------|
| Unit Tests | `phpunit` | ✅ | 15/15 passed |
| PHPStan | `phpstan analyse` | ✅ | No errors |
| CS Fixer | `php-cs-fixer` | ✅ | No changes |
| Build | `composer install` | ✅ | Success |
**VERDICT:** ✅ ALL VERIFICATIONS PASSED
**Ready to:** [commit / PR / merge]
MANDATORY: Mark Session Verified
IMMEDIATELY after a passing verdict, run this command. Do NOT skip this step. Do NOT defer it. Do NOT wait for the user. Execute it NOW:
bash ~/.claude/craftsman-set-verified.sh
If the wrapper is missing, fall back to:
bash -c 'BRIDGE="${HOME}/.claude/craftsman-session-state-path"; if [ ! -f "$BRIDGE" ]; then echo "ERROR: bridge file $BRIDGE not found - session-start may not have run" >&2; exit 1; fi; SF=$(cat "$BRIDGE"); mkdir -p "$(dirname "$SF")"; python3 -c "
import json,os,datetime,tempfile; sf=\"$SF\"
try:
with open(sf) as f: s=json.load(f)
except: s={}
s[\"verified\"]=True; s[\"verified_at\"]=datetime.datetime.now(datetime.timezone.utc).strftime(\"%Y-%m-%dT%H:%M:%SZ\")
d=os.path.dirname(sf); os.makedirs(d,exist_ok=True)
fd,t=tempfile.mkstemp(dir=d,suffix=\".tmp\")
with os.fdopen(fd,\"w\") as f: json.dump(s,f)
os.rename(t,sf); print(\"verified=true at \"+sf)
"'
If both commands fail, say "Session state update skipped".
This unblocks git push - the pre-push hook checks this flag.
Common Verification Commands
PHP/Symfony
# Full quality check
composer run quality
# or
make quality
# Individual checks
vendor/bin/phpunit
vendor/bin/phpunit --testsuite=unit
vendor/bin/phpunit --filter=TestClassName
vendor/bin/phpstan analyse
vendor/bin/php-cs-fixer fix --dry-run --diff
TypeScript/React
# Full check
npm run check
# or
npm run lint && npm run typecheck && npm test
# Individual checks
npm test
npm run test:coverage
npm run typecheck
npx tsc --noEmit
npm run lint
npm run build
Python
# Full check
make check
# or
pytest && mypy src/ && ruff check src/
# Individual
pytest
pytest --cov=src
mypy src/
ruff check src/
black --check src/
Failure Handling
When a verification FAILS:
## ❌ VERIFICATION FAILED
### Failed Check: Unit Tests
**Command:** `vendor/bin/phpunit`
**Output:**
FAILURES! Tests: 15, Assertions: 41, Failures: 1.
- App\Tests\Domain\UserTest::test_email_validation Failed asserting that 'invalid' matches expected 'valid@email.com'.
**Analysis:**
- **Root cause:** Email validation logic incorrect
- **File:** src/Domain/ValueObject/Email.php:23
- **Impact:** Email creation accepts invalid formats
**Required Action:** Fix before claiming completion.
**DO NOT:**
- Claim work is done
- Create a commit
- Say "tests mostly pass"
- Ignore the failure
Output Format
# Verification Report
## Context
- **Work Completed:** [Description]
- **Verification Time:** [Timestamp]
## Verifications Executed
### 1. [Check Name]
**Command:** `[command]`
**Output:**
[actual output]
**Status:** ✅ PASSED / ❌ FAILED
## Summary
| # | Check | Status |
|---|-------|--------|
| 1 | Tests | ✅ |
| 2 | Types | ✅ |
| 3 | Lint | ✅ |
## Verdict
**Overall Status:** ✅ ALL PASSED / ❌ X FAILED
**Evidence Quality:** Complete
**Ready for:** [Next step]
Bias Protection
Acceleration: "Skip verification, it probably works" → "Probably" is not evidence. Run the commands.
Optimism: "I'm sure the tests pass" → You're not sure until you see them pass. Run them.
Confirmation bias: "One test failed but it's minor" → A failure is a failure. Fix it first.
Session State
The set-verified step is in the "MANDATORY: Mark Session Verified" section above.
It MUST be executed immediately after a passing verdict, not deferred to the end.