---
name: ayf:verify-done
description: |
  Verify a task is actually done: diff declared files vs reality, score COMPLETE/PARTIAL/DRIFTED/INCOMPLETE, write a report, tag the board.
allowed-tools:
  - Read
  - Write
  - Edit
  - Bash
  - Glob
  - Grep
  - Agent
---

# /verify-done N -- Did This Task Actually Ship?

Takes a task number `N` and produces an objective completeness score by comparing
what the task **declared** it would deliver against what is **actually on disk and
in git history**. Agents mark tasks DONE without delivering every declared file (or
with extra unplanned files); this command catches that drift before the human trusts
the board.

This command is **read-and-report only** for the codebase. It never edits source
files — it only writes its report and tags `BOARD.md`.

## Input

The human specifies the task number: `/verify-done 79`.
If no number is given, check `.ay/tracking/locks/` for the currently locked task and
ask the human to confirm before proceeding.

## Phase 1: LOCATE

1. Resolve `.ay/` from any worktree:
   ```bash
   AY="$(dirname "$(git rev-parse --git-common-dir 2>/dev/null)")/.ay"
   ```
2. Find the task file. Task files may be named either way:
   ```bash
   TASK_FILE="$(ls "$AY"/tasks/task-${N}.md "$AY"/tasks/${N}-*.md 2>/dev/null | head -1)"
   ```
   If no task file is found, report `NO TASK FILE for #{N}` and stop.
3. Read the task file. Note its declared **Deliverables** / **Files to Create** /
   **Files to Modify** sections and its **Acceptance** criteria.
4. Read the task's status in `.ay/tracking/BOARD.md` (READY / IN PROGRESS / DONE).
   A task scored here does not have to be DONE — verifying an IN PROGRESS task tells
   the human how far along it really is.

## Phase 2: GIT EVIDENCE

Find commits that reference the task, so the report shows *when* and *how* work landed.

```bash
# Commits mentioning the task number, its slug, or a "task 79" / "#79" reference
git log --oneline --all --grep="task[ -]*${N}\b" --grep="#${N}\b" -i -E | head -20
# Files those commits touched (best-effort, for the actual-vs-declared diff)
git log --all --grep="task[ -]*${N}\b" -i -E --name-only --pretty=format: | sort -u
```

Record:
- **commits** — SHAs + subjects that reference the task (may be empty).
- **lock** — whether `.ay/tracking/locks/task-${N}.lock` exists (still in progress) or not.

If there are zero commits AND the task is marked DONE, flag this loudly in the report
— DONE with no traceable commit is itself a drift signal.

## Phase 3: FILE DIFF

Use the existing drift engine `bin/ayf-drift-check` as the source of truth. It parses
the declared files out of the task spec and classifies each as MATCHED / MISSING /
EXTRA against reality.

```bash
# Existence check (declared files that exist on disk right now)
bin/ayf-drift-check "$TASK_FILE" --json

# Optional: compare against the point work started, to surface EXTRA (undeclared)
# files. Pass the base ref the task branched from if known, else use the default branch.
bin/ayf-drift-check "$TASK_FILE" --base main --json
```

Parse the JSON with `jq`:
```bash
DRIFT_JSON="$(bin/ayf-drift-check "$TASK_FILE" --json)"
MATCHED=$(jq '.matched | length' <<< "$DRIFT_JSON")
MISSING=$(jq '.missing | length' <<< "$DRIFT_JSON")
EXTRA=$(jq   '.extra   | length' <<< "$DRIFT_JSON")
DECLARED=$(( MATCHED + MISSING ))
```

If `bin/ayf-drift-check` is unavailable, fall back to reading each declared path and
testing `[[ -e "$path" ]]` yourself, and derive EXTRA from `git status --porcelain`.

## Phase 4: SCORE

Apply this rubric in order — the first matching rule wins:

| Score       | Condition                                                        | Meaning |
|-------------|------------------------------------------------------------------|---------|
| INCOMPLETE  | `MATCHED == 0`, or `MISSING > DECLARED / 2` (majority absent)    | Barely started — most declared files are not there. |
| PARTIAL     | `0 < MISSING <= DECLARED / 2` (some absent, majority present)    | Real progress, gaps remain. |
| DRIFTED     | `MISSING == 0` and `EXTRA > 0` (all declared present, plus extras)| Delivered, but touched undeclared files — review the extras. |
| COMPLETE    | `MISSING == 0` and `EXTRA == 0` (all declared present, no extras)| Clean — matches the spec exactly. |

Edge cases:
- `DECLARED == 0` (task declared no files): score **COMPLETE** only if commits exist and
  Acceptance is met by inspection; otherwise **INCOMPLETE** with a note that the task
  spec listed no verifiable deliverables.
- Never upgrade a score to hide missing files: MISSING always dominates EXTRA.

## Phase 5: REPORT

Write a structured report to `.ay/tracking/verify-done-${N}.md`. Overwrite any prior
report for the same task. Use exactly this template:

```markdown
# Verify-Done Report — Task #{N}

- **Task:** {title}
- **Board status:** {READY | IN PROGRESS | DONE}
- **Verified:** {YYYY-MM-DD HH:MM}
- **Score:** {COMPLETE ✓ | PARTIAL | DRIFTED ⚠ | INCOMPLETE}

## File Diff

| File | Declared? | On disk? | Status |
|------|-----------|----------|--------|
| path/to/a | yes | yes | MATCHED |
| path/to/b | yes | no  | MISSING |
| path/to/c | no  | yes | EXTRA   |

Summary: {MATCHED} matched, {MISSING} missing, {EXTRA} extra ({DECLARED} declared).

## Git Evidence

- {sha} {subject}
- (none found) — DONE with no referencing commit is a drift signal.

## Acceptance Criteria

For each Acceptance checkbox in the task spec, mark:
- [x] met (observed how)
- [ ] not verifiable by this command (needs a run / human check)

## Verdict

{One paragraph: what is done, what is missing or extra, and the single recommended
next action — e.g. "Deliver the 2 MISSING files", "Justify or remove the EXTRA file",
or "Safe to mark DONE".}
```

Read the report back after writing it to confirm it landed (Self-Inventory: verify,
don't assume).

## Phase 6: TAG THE BOARD

Update the task's row in `.ay/tracking/BOARD.md` by appending a tag to the Title cell.
Do **not** change the Status column — verification annotates, it does not decide.

- **COMPLETE** → append ` [verified ✓]`
- **PARTIAL** / **DRIFTED** / **INCOMPLETE** → append ` [DRIFTED ⚠ {SCORE}]`

If a prior `[verified ✓]` or `[DRIFTED ⚠ …]` tag already exists on the row, replace it
rather than stacking a second tag.

## Phase 7: REPORT TO HUMAN

Print a compact summary and stop:

```
Task #{N}: {SCORE}
  {MATCHED} matched · {MISSING} missing · {EXTRA} extra · {commit-count} commits
  Report: .ay/tracking/verify-done-{N}.md
  Next:   {recommended action}
```

## Rules

- Read-only over the codebase — never edit, create, or delete source files.
- MISSING always dominates EXTRA when scoring. Never inflate a score.
- Do not change the task's Status on the board; only append the verification tag.
- Every score must be backed by the file diff and git evidence in the report — no
  score without shown work.
- If `bin/ayf-drift-check` or `jq` is missing, say so in the report and fall back to
  manual existence checks rather than guessing.
