| name | guild-verify-done |
| description | Final gate before task close, and the home of Guild's verify-the-claim discipline — no completion language before an independent VCS diff confirms the change exists on disk. Runs five checks: (1) tests the spec defined, (2) scope — every changed file traces to a lane's diff, (3) success-criteria match, (4) no open blocker `followups:`, (5) assumptions reviewed. Writes `.guild/runs/<run-id>/verify.md` (pass/fail + run summary); on pass hands off to `guild:reflect`. TRIGGER: "is this done", "final check", "verify the task is complete", "run the done-gate", "ready to hand off — confirm the diff", "tests pass, are we done". DO NOT TRIGGER for: reviewing individual handoffs (guild:review), or starting new work. |
guild:verify-done
The final checkpoint before a Guild task is allowed to close. Runs after guild:review has produced .guild/runs/<run-id>/review.md with no outstanding Stage 1 or Stage 2 blockers. Enforces the planning contract's success-criteria rule: every criterion from the spec must be demonstrably met. It is a gate, not a re-implementation: it reads artifacts and says pass or fail, it does not re-do specialist work.
Input
Three required inputs, all already on disk by the time this skill fires:
.guild/runs/<run-id>/review.md — the per-lane pass/fail table from guild:review. If any lane is ✗, verify-done should never have been called; abort and return control to review.
- The spec's
success_criteria — from the source spec at .guild/spec/<slug>.md (written by guild:brainstorm) or a user-provided spec path referenced by the plan frontmatter. §8.1 requires these be verifiable; this skill is where they are verified.
.guild/runs/<run-id>/assumptions.md — the assumption log aggregated from per-lane receipts by guild:execute-plan (see its Receipt collection section). If specialists flagged no assumptions during the run, this file may be empty or absent — treat an empty/missing file as "no assumptions to review" and skip check #5's user-acknowledgement step. Only hard-fail the gate if the file exists with unresolved disputed entries.
Secondary inputs (re-read for the scope check): .guild/runs/<run-id>/handoffs/*.md for the full set of changed_files across lanes, and the approved plan for each lane's declared scope. When a receipt is consumed, the embedded ```guild.handoff.v2 JSON block is the machine truth a consumer reads; the guild.handoff_receipt.v1 YAML frontmatter is human-review context only (see §"Handoff contract" of the communication format policy). Read the changed_files / evidence / followups fields from that one embedded envelope — a frontmatter-only receipt with no embedded v2 block is not a valid machine receipt. Also, when present, .guild/runs/<run-id>/diff-learn.json (guild.diff_understanding.v1 — bound by pointer to the implementation contract map, row 13; produced at P2 by guild:plan's plan-impact step) for the brownfield blast-radius cross-check in check #2. The two dispatch-integrity sinks (logs/backend-degradation.jsonl, logs/tier-dispatch.jsonl) feed check #6 and are read only through the audit-run-sinks.ts CLI — never hand-parsed here.
Six checks
All six must pass. Stop at the first failure and record which check failed.
- Tests pass. Run whatever command the spec defined as the acceptance test.
§8.1 makes success criteria a planning-contract precondition, so there is a command to run — if there isn't, that is itself a fail (the spec was approved without a verifiable criterion). Capture exit code and tail of output into the verify report.
- Scope boundary. Union the
changed_files across every lane's handoff receipt. For each file, confirm it falls inside the scope of the lane that produced it. Any file not traceable to a lane's scope is a boundary violation even if review missed it — verify-done is the last place to catch scope creep before merge. Brownfield cross-check (P3 scope-check, when diff-learn.json is present): the spec's plug point P3 (from the codebase-understanding spec §"Where it sits") feeds this check. Treat its untraced_files (changed files no graph node explains) and any changed_files whose affected_layers exceed what the plan's lanes declared as scope-creep signals — a changed file that the plan-impact step flagged as untraced and that no lane scope covers fails this check, not just a soft warning. Do not re-derive the diff here; consume the P2 artifact.
- Success criteria match. Walk the spec's
success_criteria list item by item and tick each off against the receipts' evidence: fields and the test output from check 1. A criterion with no corroborating evidence fails here even if tests pass, because §8.1 makes criteria the contract, not the test suite.
- No open blocker
followups:. Scan the union of followups: across receipts and review.md. A followup tagged or phrased as blocking (e.g., "must fix before merge", "blocks release") fails this check. Deferred/nice-to-have follow-ups are allowed through and carried into the run summary.
- Assumptions reviewed. Surface
.guild/runs/<run-id>/assumptions.md to the user. The user either acknowledges the log (pass) or disputes a specific assumption (fail — that assumption becomes a blocker to resolve before re-running verify-done). Per §8.1, review at the end is the whole point of the assumption log; do not skip it just because review.md was clean.
- Dispatch integrity (sink audit). Read the run's two dedicated dispatch-integrity sinks —
logs/backend-degradation.jsonl (guild.backend_degradation.v1 — a lane that ran on a DOWNGRADED backend) and logs/tier-dispatch.jsonl (guild.tier_dispatch.v1 — a dispatch that violated the tier contract, including an un-tiered missing_model). These sinks are deliberately separate from logs/v1.4-events.jsonl and are surfaced NOWHERE else, so a silent backend downgrade or an un-tiered dispatch would otherwise pass this gate unseen — a run that is auditable-by-path only must instead be VISIBLY dirty. Do not eyeball the jsonl; run the deterministic audit and gate on its exit code:
npx tsx ${GUILD_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-$HOME/.local/share/guild/dist/claude-code}}/scripts/audit-run-sinks.ts --run-id <run-id> --cwd <repo-root>
Exit 0 → clean (no degradations, no tier violations), this check passes; exit 1 → the run is dispatch-dirty and this check FAILS — paste the printed ## Sink audit block as the check-6 evidence line. The only way a dirty run passes is if the operator explicitly accepts each named degradation/violation as a documented entry in the assumption log (check #5); an unreviewed degradation is a hard fail.
Output
Write a single artifact:
.guild/runs/<run-id>/verify.md
It contains, in order:
- Overall status —
pass or fail.
- Per-check result — the six checks above, each with ✓/✗ and the evidence line (test command + exit code, scope-creep file list if any, criterion-to-evidence mapping, blocker-followup list, assumption-review outcome, and the dispatch-integrity Sink audit block +
audit-run-sinks.ts exit code).
- Run summary — what shipped (changed files grouped by lane), which assumptions were acknowledged, open non-blocking follow-ups to carry forward into the next task's plan.
Keep it terse and grep-friendly; downstream (guild:reflect) reads this file, not the specialist transcripts.
Verify the claim against an independent diff (absorbed)
This skill is the home of Guild's per-claim verification discipline: no
completion or success language before an independent VCS diff confirms the
change exists on disk. A completion claim is a hypothesis until the diff
proves it — a lane that writes "done — all changes applied" over an empty tree,
a partial edit, or changes in the wrong path poisons the receipt that
guild:review then trusts, and the gap surfaces a lane later, far from its
origin.
One principle, two scopes:
-
Per-claim precondition (every lane, before its receipt). Before writing
any of "done", "complete", "finished", "ready to hand off", or "tests pass",
the specialist takes an independent diff and gates the claim on it — never
trusting its own running narrative of what it changed:
git status --porcelain
git diff --stat HEAD
git diff HEAD -- <expected-paths>
For every scoped file, confirm a diff hunk exists; for every diff hunk,
confirm it traces to the lane scope (an out-of-scope edit is a finding, not a
pass). Run the spec's defined checks and capture the actual exit status —
never infer pass from "it should pass". Diff present + in-scope + checks green
→ the claim is evidence-backed, proceed to the receipt; otherwise do not
claim completion — report the gap with the diff output as evidence and
continue the work.
-
Final multi-lane gate (the five checks above). They re-apply the same
diff-is-the-evidence rule across the whole run: check 2 unions the
changed_files across every receipt and re-confirms each traces to its
lane's scope, and check 1 re-runs the spec's test rather than trusting a
receipt's word.
The VCS diff — not the narrative — is the single source of truth for what
changed, at both scopes.
Handoff
On pass, hand off to guild:reflect (lands in P5 — for P1 this is a forward reference and acceptable; if guild:reflect is not yet installed, stop here and return the verify.md path to the user). The handoff payload:
run_id — the run directory name.
verify_path — absolute path to .guild/runs/<run-id>/verify.md.
review_path — carried forward from guild:review.
assumptions_path — .guild/runs/<run-id>/assumptions.md, acknowledged.
followups — the non-blocking follow-up list from the run summary, for the next task's plan.
Failure mode
On fail, annotate verify.md with the specific failing check and the evidence that failed it (test output, scope-creep file, unmet criterion, blocker followup, or disputed assumption). Do not auto-rollback, do not re-dispatch specialists, do not amend receipts. Return control to the user with the path to verify.md and a one-line summary of what failed. The user decides whether to loop back to guild:execute-plan (for check-1/3/4 failures), to guild:plan (for check-2 scope violations that imply the plan was wrong), or to live with an assumption dispute (check 5). Verify-done is a gate; gates report, they do not self-heal.