verify-completion-evidence
When to use
- Just before claiming a task, feature, fix, or refactor is complete
- Just before proposing
/commit, /create-pr, or pushing
- Before answering "is it ready?", "can I merge?", "does it work?"
- After a sequence of edits, when next step would be reporting to the user
- Whenever the wording "should work", "looks good", "probably fine" is
about to appear in a reply
Do NOT use when:
- Still actively editing — run targeted tests, not the full gate
- Pure documentation changes with no executable impact
- The user explicitly asks for a draft / exploration, not a final answer
Goal
Make every completion claim traceable to captured output from this
message. No claim survives unless the command that proves it was run
and its output was read inside the current turn.
The Iron Law
NO COMPLETION CLAIMS WITHOUT FRESH EVIDENCE IN THIS MESSAGE.
"I already ran it earlier in the conversation" does not count. Earlier
runs are stale the moment another edit lands.
Procedure
1. Identify the claim you are about to make
Examples: "all tests pass", "this is ready for PR", "the refactor
is done", "the bug is fixed".
Each claim maps to a specific verification command. Write down the
mapping before running anything:
| Claim |
Evidence command |
| "tests pass" |
full or targeted test suite |
| "no static errors" |
project's type-checker on changed scope (PHPStan, tsc --noEmit, mypy / pyright, go vet, cargo check) |
| "style is clean" |
project's linter + formatter (ECS / Prettier / ESLint / Ruff / Black / gofmt / rustfmt) |
| "no automated refactor pending" |
project's auto-refactor dry-run if one exists (Rector for PHP — otherwise skip this row) |
| "endpoint works" |
curl / Postman / integration test output |
| "UI renders" |
Playwright snapshot or manual browser check |
| "bug is fixed" |
regression test passes |
2. Run the command fresh
- Run against the current working tree, not a cached summary.
- If the project runs commands inside a container or VM (Docker, Devcontainer, Vagrant), run them there — not on the host. See
docker and /tests:execute.
- Use targeted runs during iteration (
--filter=, --testNamePattern).
Run the full suite only in the final verification pass.
3. Read the full output
- Check the exit code.
- Count failures, errors, warnings.
- Do not rely on the last line — scroll through the output for
deprecations, skipped tests, silent retries.
4. Match output against the claim
Ask: "Does this output actually support what I am about to say?"
- 248/250 tests passed with 2 skipped → do not say "all green"; name the skips.
- Type-checker exit 0 but only analyzed one file → do not say "no static
errors"; name the scope that was checked.
curl returned 200 → check the body, not just the status.
5. Only then make the claim
Reference the evidence: "Tests: 250/250 passed. Type-checker: 0 errors
on the changed scope." — not "everything looks good".
The end-of-work sequence
Gate zero: when quality.local_auto_run is false or missing (the
default), steps 2–3 below do NOT run — the user runs quality tools
manually and remote CI is the authoritative gate (see
quality-tools § Execution policy). Run
only the narrowest probe the claim needs (step 1, a curl, a syntax
check on the edited file) and close with "quality gates delegated to
remote CI" — never a pass claim for tools that did not run. The full
sequence applies when local_auto_run: true:
- Targeted tests — the test(s) covering the changed code pass.
- Full test suite — only after targeted pass is green.
- Static analysis pipeline — run the project's type-checker → auto-refactor dry-run (if any) → linter / formatter → type-checker (second pass catches issues the refactor / formatter may have introduced).
- Fix any output from steps 1–3 and restart the sequence.
- Only then: claim completion or suggest
/commit, push, or PR.
Do not run the full quality pipeline between intermediate edits — it burns time and tokens. Use it once, at the end.
→ For the exact PHP commands (PHPStan → Rector → ECS → PHPStan): see quality-tools.
→ For TS / JS, Python, Go, Rust pipelines: the project's Taskfile.yml / package.json scripts / Makefile is the source of truth — read it before improvising.
Minimum evidence per task type
| Task type |
Required evidence |
| Code change (logic) |
Targeted tests + project's type-checker on changed scope |
| New feature |
Tests (new + suite) + type-checker + smoke check (curl / UI / integration probe) |
| Bug fix |
Regression test (RED → GREEN) + full suite |
| Refactoring |
Full suite + type-checker + auto-refactor dry-run if available |
| Config / env change |
Relevant command or service output (not just file diff) |
| Migration |
Migration run output + rollback dry-run + tests |
| API endpoint |
HTTP response body + status + content-type |
| Frontend component |
Rendered state (Playwright or manual) + unit tests |
| Documentation only |
No verification needed |
Never accept as proof: "should work", "looks correct", "the logic
is sound", "compiles" (unless compilation itself is the contract).
Output format
When reporting completion to the user:
- What was changed — one line summary per changed file / component
- Verification run — the exact command and its exit code
- Result — numeric breakdown (tests passed/failed/skipped, errors,
warnings)
- Caveats — anything the output flagged but you chose to accept
- Untracked files — if
git status --short shows any untracked
files in the working tree, list them verbatim in the report. This
prevents silently-shipped artefacts (logs, scratch scripts, ad-hoc
notes) from disappearing into a future commit. Empty list means
omit the section.
- Next step — e.g. "Ready for
/commit" or "Awaiting review"
Gotchas
- A "no output" result from a linter is not proof it ran — check the
exit code and the analyzed-file count.
- Silencing a warning with
@phpstan-ignore-next-line, // @ts-expect-error, # type: ignore, or //nolint
without a reason code passes the linter but defers the real problem.
- Running tests with
--stop-on-failure then reporting "passed" — it
only ran until the first failure; the green streak after it is
unexamined.
- Cached static-analysis results (
--cache directories) can report
clean after you have broken something; clear the cache when the
change is large.
- Running the test suite on the wrong branch (forgot to switch or
rebase) — verify
git status and git log -1 before the final gate.
- A previously green static-analysis run in the same conversation is stale as
soon as any edit lands. Run it again.
Red flags — STOP and run the gate
- About to write "done", "ready", "works", "passes" without a
command-output reference in the same message
- About to suggest
/commit / push / PR without a verification block
- Relying on an earlier-in-conversation test run
- Partial evidence (tests green, type-checker / linter not run — or vice versa)
- "The failing test is unrelated, let me skip it" — verify first, then
decide
- Reporting a green run by paraphrasing instead of quoting exit code
and counts
Do NOT
- Do NOT claim completion without running the mapping command in this
message
- Do NOT trust a summary written earlier in the conversation
- Do NOT suppress warnings or skip tests to pass the gate
- Do NOT report only the last line of output — read the whole thing
- Do NOT run the full quality pipeline between intermediate edits —
and under
quality.local_auto_run: false (the default) do NOT run
it at the end either; remote CI is the gate
When to hand over to another skill
Validation checklist
Before sending a completion message:
1---2name: verify-completion-evidence3description: Use when claiming 'done', suggesting a commit, push, or PR — runs the evidence gate so completion claims come from fresh output in this message, not memory or earlier runs.4---56# verify-completion-evidence78## When to use910* Just before claiming a task, feature, fix, or refactor is complete11* Just before proposing `/commit`, `/create-pr`, or pushing12* Before answering "is it ready?", "can I merge?", "does it work?"13* After a sequence of edits, when next step would be reporting to the user14* Whenever the wording "should work", "looks good", "probably fine" is15 about to appear in a reply1617Do NOT use when:1819* Still actively editing — run targeted tests, not the full gate20* Pure documentation changes with no executable impact21* The user explicitly asks for a draft / exploration, not a final answer2223## Goal2425Make every completion claim **traceable to captured output from this26message**. No claim survives unless the command that proves it was run27and its output was read inside the current turn.2829## The Iron Law3031```32NO COMPLETION CLAIMS WITHOUT FRESH EVIDENCE IN THIS MESSAGE.33```3435"I already ran it earlier in the conversation" does not count. Earlier36runs are stale the moment another edit lands.3738## Procedure3940### 1. Identify the claim you are about to make4142Examples: *"all tests pass"*, *"this is ready for PR"*, *"the refactor43is done"*, *"the bug is fixed"*.4445Each claim maps to a specific verification command. Write down the46mapping before running anything:4748| Claim | Evidence command |49|---|---|50| "tests pass" | full or targeted test suite |51| "no static errors" | project's type-checker on changed scope (PHPStan, `tsc --noEmit`, mypy / pyright, `go vet`, `cargo check`) |52| "style is clean" | project's linter + formatter (ECS / Prettier / ESLint / Ruff / Black / gofmt / rustfmt) |53| "no automated refactor pending" | project's auto-refactor dry-run if one exists (Rector for PHP — otherwise skip this row) |54| "endpoint works" | curl / Postman / integration test output |55| "UI renders" | Playwright snapshot or manual browser check |56| "bug is fixed" | regression test passes |5758### 2. Run the command fresh5960* Run against the current working tree, not a cached summary.61* If the project runs commands inside a container or VM (Docker, Devcontainer, Vagrant), run them there — not on the host. See [`docker`](../docker/SKILL.md) and [`/tests:execute`](../../domains/engineering-base/tests/execute/command.md).62* Use targeted runs during iteration (`--filter=`, `--testNamePattern`).63 Run the full suite only in the final verification pass.6465### 3. Read the full output6667* Check the exit code.68* Count failures, errors, warnings.69* Do not rely on the last line — scroll through the output for70 deprecations, skipped tests, silent retries.7172### 4. Match output against the claim7374Ask: *"Does this output actually support what I am about to say?"*7576* 248/250 tests passed with 2 skipped → do not say "all green"; name the skips.77* Type-checker exit 0 but only analyzed one file → do not say "no static78 errors"; name the scope that was checked.79* `curl` returned 200 → check the body, not just the status.8081### 5. Only then make the claim8283Reference the evidence: *"Tests: 250/250 passed. Type-checker: 0 errors84on the changed scope."* — not *"everything looks good"*.8586## The end-of-work sequence8788**Gate zero:** when `quality.local_auto_run` is `false` or missing (the89default), steps 2–3 below do NOT run — the user runs quality tools90manually and remote CI is the authoritative gate (see91[`quality-tools` § Execution policy](../quality-tools/SKILL.md)). Run92only the narrowest probe the claim needs (step 1, a `curl`, a syntax93check on the edited file) and close with *"quality gates delegated to94remote CI"* — never a pass claim for tools that did not run. The full95sequence applies when `local_auto_run: true`:96971. **Targeted tests** — the test(s) covering the changed code pass.982. **Full test suite** — only after targeted pass is green.993. **Static analysis pipeline** — run the project's type-checker → auto-refactor dry-run (if any) → linter / formatter → type-checker (second pass catches issues the refactor / formatter may have introduced).1004. Fix any output from steps 1–3 and restart the sequence.1015. Only then: claim completion or suggest `/commit`, push, or PR.102103Do not run the full quality pipeline between intermediate edits — it burns time and tokens. Use it once, at the end.104105→ For the **exact PHP commands** (PHPStan → Rector → ECS → PHPStan): see [`quality-tools`](../quality-tools/SKILL.md).106→ For TS / JS, Python, Go, Rust pipelines: the project's `Taskfile.yml` / `package.json scripts` / `Makefile` is the source of truth — read it before improvising.107108## Minimum evidence per task type109110| Task type | Required evidence |111|---|---|112| Code change (logic) | Targeted tests + project's type-checker on changed scope |113| New feature | Tests (new + suite) + type-checker + smoke check (curl / UI / integration probe) |114| Bug fix | Regression test (RED → GREEN) + full suite |115| Refactoring | Full suite + type-checker + auto-refactor dry-run if available |116| Config / env change | Relevant command or service output (not just file diff) |117| Migration | Migration run output + rollback dry-run + tests |118| API endpoint | HTTP response body + status + content-type |119| Frontend component | Rendered state (Playwright or manual) + unit tests |120| Documentation only | No verification needed |121122**Never accept** as proof: "should work", "looks correct", "the logic123is sound", "compiles" (unless compilation itself is the contract).124125## Output format126127When reporting completion to the user:1281291. **What was changed** — one line summary per changed file / component1302. **Verification run** — the exact command and its exit code1313. **Result** — numeric breakdown (tests passed/failed/skipped, errors,132 warnings)1334. **Caveats** — anything the output flagged but you chose to accept1345. **Untracked files** — if `git status --short` shows any untracked135 files in the working tree, list them verbatim in the report. This136 prevents silently-shipped artefacts (logs, scratch scripts, ad-hoc137 notes) from disappearing into a future commit. Empty list means138 omit the section.1396. **Next step** — e.g. "Ready for `/commit`" or "Awaiting review"140141## Gotchas142143* A "no output" result from a linter is not proof it ran — check the144 exit code and the analyzed-file count.145* Silencing a warning with `@phpstan-ignore-next-line`, `// @ts-expect-error`, `# type: ignore`, or `//nolint`146 without a reason code passes the linter but defers the real problem.147* Running tests with `--stop-on-failure` then reporting "passed" — it148 only ran until the first failure; the green streak after it is149 unexamined.150* Cached static-analysis results (`--cache` directories) can report151 clean after you have broken something; clear the cache when the152 change is large.153* Running the test suite on the wrong branch (forgot to switch or154 rebase) — verify `git status` and `git log -1` before the final gate.155* A previously green static-analysis run in the same conversation is stale as156 soon as any edit lands. Run it again.157158## Red flags — STOP and run the gate159160* About to write "done", "ready", "works", "passes" without a161 command-output reference in the same message162* About to suggest `/commit` / push / PR without a verification block163* Relying on an earlier-in-conversation test run164* Partial evidence (tests green, type-checker / linter not run — or vice versa)165* "The failing test is unrelated, let me skip it" — verify first, then166 decide167* Reporting a green run by paraphrasing instead of quoting exit code168 and counts169170## Do NOT171172* Do NOT claim completion without running the mapping command in this173 message174* Do NOT trust a summary written earlier in the conversation175* Do NOT suppress warnings or skip tests to pass the gate176* Do NOT report only the last line of output — read the whole thing177* Do NOT run the full quality pipeline between intermediate edits —178 and under `quality.local_auto_run: false` (the default) do NOT run179 it at the end either; remote CI is the gate180181## When to hand over to another skill182183* Exact PHP quality commands (PHPStan / Rector / ECS) → [`quality-tools`](../quality-tools/SKILL.md)184* Running tests inside a container / VM → [`/tests:execute`](../../domains/engineering-base/tests/execute/command.md)185* Writing the regression test that the gate requires →186 [`test-driven-development`](../test-driven-development/SKILL.md)187* Diagnosing why the gate failed → [`systematic-debugging`](../systematic-debugging/SKILL.md)188* Committing once the gate is green → [`git-workflow`](../git-workflow/SKILL.md)189190## Validation checklist191192Before sending a completion message:193194* [ ] Every claim in the message maps to a command run in this turn195* [ ] Exit code of each command is read and matches the claim196* [ ] Output is quoted with numeric counts, not paraphrased197* [ ] No warnings or skips are hidden198* [ ] Targeted tests green → full suite green → quality pipeline clean199 (`local_auto_run: true` only; under the default `false` state200 "quality gates delegated to remote CI" instead)201* [ ] `git status` reflects only the intended change set202* [ ] If `git status --short` shows untracked files, the report lists203 them verbatim under "Untracked files"