--- name: finish description: Decide what to do with an anvil task that has submitted evidence and is awaiting human review — accept and ship, reject and reopen, or hold for further investigation. Use this skill when one or more tasks are in needs_review and need a final disposition. --- # Finish — Review Evidence and Ship Drive the final leg of the task lifecycle: read the evidence, pick a disposition, call `apply`, and hand off to the project's git workflow for merging. Nothing moves from `needs_review` to `done` (or back to `drafted`) without going through here. --- ## When to Use - Tasks appear in `anvil list --status needs_review`. - Before merging a PR that contains anvil-tracked work — confirm the task has been applied first. - At end-of-day or end-of-iteration when deciding what to ship versus what to reopen. **Do not use this skill to execute work or submit evidence** — that is `/anvil:execute`. Do not use it to inspect queue state without making a decision — use `/anvil:state-ops` for read-only inspection. --- ## Prerequisites One or more tasks in `needs_review`. Confirm before proceeding: ```bash anvil list --status needs_review ``` Require the source/proof freeze, three independent whole-task reviews and observed stopped custody from [Native resume and frozen handoff](../../AGENTS.md#native-resume-and-frozen-handoff). The coordinator validates their bindings and verdicts; it does not repeat the independent source/security reviews. Repair blocking findings and repeat affected checks and reviews before disposition. The table columns are `TaskID`, `Title`, `Status`, `Priority`, `Type`, `Score`, `Feature`. (To see who claimed a task and what the evidence was, run `anvil show TASK_ID` and `anvil apply TASK_ID`; see Step 2.) Commands used in this skill: | Command | What it does | |---|---| | `anvil list --status needs_review` | List tasks awaiting review | | `anvil show TASK_ID` | Print full task detail (criteria, scores, likely files) | | `anvil apply TASK_ID` | Review-only: show the evidence gate without deciding | | `anvil apply TASK_ID --approve` | Approve: needs_review → done | | `anvil apply TASK_ID --reject --reason "..."` | Reject: needs_review → drafted | --- ## Workflow ### Step 1 — List what needs review ```bash anvil list --status needs_review ``` Read every row. Before proceeding to any individual task: - Note tasks that have been in `needs_review` longer than expected — long dwell times may indicate evidence was submitted in a broken state or the reviewer window closed. - Note whether multiple tasks in the list touch the same files — they may need to be applied in dependency order to avoid conflicts on merge. When multiple tasks are ready, apply them in dependency order: tasks with no dependents first, then tasks whose dependencies are already `done`. --- ### Step 2 — Inspect each task's detail and evidence gate Two reads, both layout-aware: ```bash anvil show TASK_ID --json anvil apply TASK_ID ``` The JSON `anvil show TASK_ID --json` response identifies the task's owning PRD at `data.task.prd_id`. Require a non-empty value and retain it as `TASK_PRD_ID` before any hold-flow resolver or PRD edit. `default` means omit `--prd`; pass a named owner unchanged to every scoped resolver and parse below. Do not infer ownership from the task ID, list ordering, or the currently active PRD. The show response also contains the task detail: title, feature, status, the six-dimension `Scores` (with an explanation block), `Dependencies`, `Conflict Groups`, `Acceptance Criteria`, `Verification Commands`, `Likely Files`, `Active Claims`, and `Recent Events` (where `evidence.submitted` confirms evidence was recorded). It does **not** print the evidence contents; for that, read the submission and the gate. `anvil apply TASK_ID` with no `--approve`/`--reject` flag is **review-only**: it reports the task is awaiting review and prints the evidence gate without changing anything: ``` Task 'T012' awaiting review (status: needs_review). Evidence gate: INCOMPLETE — missing items for required_evidence: - `pytest -x` exits 0 Pass --approve to accept or --reject --reason TEXT to reject. ``` An `INCOMPLETE` gate means a required-evidence item was not captured: the agent ran verification commands outside the hook window or the shell-free capture-evidence dispatcher did not fire. Do not approve over an incomplete gate without understanding why; the gap may indicate the verification was never actually run. (The gate is **advisory** by default: `--approve` still proceeds. Run `anvil apply --strict` to refuse approval while the gate is incomplete.) Before approving: - Confirm every acceptance criterion (from `anvil show`) has a corresponding verification command that exited 0. - Confirm the files the agent changed match what the acceptance criteria required: a task that was supposed to modify `src/claims/manager.py` but only touched `tests/` is suspicious. The submitted `--files-changed` list appears in the `anvil submit` output and the task's `evidence.submitted` event. - Bind the independent reports to the actual task/claim/generation, frozen commit, exact commands/results and authoritative evidence. If a PR URL was linked at submit time (`anvil submit --pr-url`), verify it identifies that reviewed source. Changed source or proof attribution needs affected checks and reviews; unchanged green source does not need a duplicate full run. --- ### Step 3 — Pick a disposition (the hard handoff gate) `apply --approve` appends an immutable `task.applied` event and a signed acceptance proof. Require explicit user authority for the actual disposition; tool availability, green checks and a general unattended-build request do not grant it. If existing explicit authority covers this intermediate disposition, use it without asking again. Preserve any human-only or protected gate and the final user project-validation gate. After surfacing the task detail, evidence gate and independent verdicts, act within that authority. If the disposition is unresolved, present these options and ask the user to pick, then run the chosen command: > The evidence for **T012** is summarized above. How should this be dispositioned? > 1. **Accept and ship** — verification exited 0, evidence is complete, diff matches acceptance criteria. > 2. **Reject and reopen** — evidence is incomplete or the implementation does not satisfy acceptance criteria. I will need a reason. > 3. **Hold for investigation** — evidence is submitted but more context is needed before deciding. I will keep the task in `needs_review`. > 4. **Discard the work entirely** — the task direction was wrong and the implementation should not be merged. I will also need a reason. > > Reply with the number (or just "accept" / "reject" / "hold" / "discard"). Based on the answer, drive the corresponding command yourself rather than asking the user to type it. #### On "accept" (1) With explicit approval authority and the required gates satisfied, invoke `anvil apply T012 --approve` (or the equivalent `apply_review_decision` MCP tool). Surface the resulting disposition and signed proof path. Drive Step 4 only when existing publication authority covers it; otherwise keep the reviewed result ready for that decision. Do not ask twice for the same authorization. #### On "reject" (2) Ask for a concrete reason before invoking: > Reject T012 with which reason? Concrete is required — "pytest -x reports 3 failures in test_retry.py" is good; "not done" is not. With a concrete reason and explicit rejection authority, invoke `anvil apply T012 --reject --reason ""`. Surface the result; the original branch and evidence remain audit history. Continue authorized rework through the current readiness/ownership flow, or return the unresolved choice. #### On "hold" (3) #### Required `prd source-name` capability preflight Before any branch that reads, backs up, edits, writes, or parses a PRD, resolve the actual `anvil` executable with the runtime's native lookup and run `anvil prd source-name --help`. Require exit 0; matching version text alone is not capability proof. Then run `anvil prd source-name [--prd ] --json` with the exact retained `TASK_PRD_ID` and require `ok: true` plus a non-empty `data.relative_name`. Retain that validated value as `PRD_RELATIVE`; it is the sole relative path for the selected PRD for the rest of this hold flow. Do not resolve it again unless the selected PRD ID changes, in which case repeat this entire preflight. If the help probe is absent, run `anvil --version` and stop before changing anything. Report: "Cannot safely resolve the selected PRD. `` reports `` but does not expose `anvil prd source-name`, required by this skill and shipped in `anvil-state >=0.6.1`. No PRD file or state was changed. Upgrade with `uv tool upgrade anvil-state`, refresh the Anvil plugin if applicable, restart the harness/MCP process, then verify `anvil --version` and `anvil prd source-name --help`. I will not infer a filename, parse, or delete state." If the capability exists but the resolver returns a typed state-root, identity, or legacy-source migration error, surface that exact error and stop. Do not mislabel it as version skew and do not construct a fallback filename. Do not invoke `apply` at all. Capture the open questions inline: > What context do we need before this can be dispositioned? I will add it to the task notes so the next review has the full picture. Once the user lists the open questions, record them. There is no CLI subcommand for editing a task's notes in place, so capture the context where the next reviewer will see it: join the `Path:` directory from `anvil status` with the preflight's retained `PRD_RELATIVE`, edit that PRD, re-run `anvil prd parse [--prd ]` with the same owning ID, and coordinate with the next reviewer directly; drive that loop yourself. (PRD-level decision markers can be resolved with `anvil prd resolve-decision`; see `/anvil:resolve-decisions`.) Then loop back to Step 2. #### On "discard" (4) Like reject, but with a discard-specific reason — and you also need to clean up the branch. > Discard T012 with which reason? (For the audit log — "approach superseded by T015" is typical.) Invoke `anvil apply T012 --reject --reason "discarded — "` directly. Then ask whether to delete the branch now: > The work is discarded. Delete the branch `agent/t012-` now? (yes / no) On `yes`, run `git branch -D agent/t012-` yourself. On `no`, leave the branch intact and tell the user the audit log retains the `evidence.submitted` and rejection `task.applied` events regardless. **The rule:** the user supplies disposition authority; the agent carries out the authorized command. Ask only when the needed choice or authority is absent. --- ### Step 4 — Ship the merged work For every task that received `--approve`: 1. Merge the `agent/-` branch to the project's main branch: ```bash git checkout main git merge --no-ff agent/t012-add-retry-backoff -m "merge: T012 add-retry-backoff" git push origin main ``` Or open a PR from the branch and merge via the project's PR workflow. Reference the anvil task ID in the PR body for traceability: ``` Closes anvil:T012 — add-retry-backoff ``` 2. After merging, clean up the branch: ```bash git branch -d agent/t012-add-retry-backoff ``` Branches accumulate. After a task is `done` and its branch is merged, delete it. `anvil drift` reports undeleted `agent/-` branches as orphan branches, so they will resurface there until cleaned up. anvil does not auto-merge. The deliberate separation between `apply` (state transition) and `merge` (git operation) means the reviewer controls the merge strategy, PR template, and commit message — without the tool imposing a workflow. --- ### Step 5 — Sync to external tracker (optional) If the project has an external sync provider configured and the task is now at `status=done`, run `anvil sync github` to push the disposition to GitHub Issues (use `--task TASK_ID` to scope to one task); see [`docs/github-sync.md`](../../docs/github-sync.md) for the full CLI surface and conflict-resolution strategies. Otherwise, skip this step; anvil's local state store is the canonical record. --- ## Common Pitfalls - **Applying without reading the evidence.** The `Evidence gate` shown by `anvil apply TASK_ID` is a presence check against the task's `required_evidence`: it checks that items were captured, not that they are correct. The diff and the submitted commands are the ground truth. Read them. - **Rejecting without a `--reason`.** The flag is required. A rejection without a reason leaves the next agent (or the next session of the same agent) without context for why the task failed review. Concrete reasons prevent duplicate mistakes. - **Applying out of dependency order.** If T013 depends on T012, apply T012 first and merge it before applying T013. Applying in the wrong order creates a branch that cannot be cleanly merged until its dependency lands on main. - **Forgetting to delete merged branches.** Agent branches accumulate. After merge, `git branch -d` the branch. `anvil drift` will report stale `agent/-` branches as orphans, but it is easier to clean up immediately. - **Hand-editing the state store to change a task status.** Use `anvil apply` so the disposition and status transition are recorded as a `task.applied` event in the append-only log. Direct edits to the database produce state that cannot be replayed or audited. For **decision-presentation discipline** — how to surface multi-option dispositions (accept/reject/hold/discard) as structured Q&A — see the canonical description in `/anvil:resolve-decisions`. The same pattern applies to any choice this skill surfaces. --- ## Composition with Other Skills | Position | Skill | |---|---| | Before this skill | `/anvil:execute` — evidence must be submitted; task must be in `needs_review` | | For read-only inspection before deciding | `/anvil:state-ops` — inspect queue without making a disposition | | After reject + redraft | `/anvil:plan` — if the task needs re-scoping; `/anvil:execute` — to re-claim and re-attempt | | After accept + merge | The project's normal PR + deploy workflow; anvil does not drive deployment | Before invoking `anvil apply`, you may dispatch the plugin-local `sentinel` agent (if available in this session) against the task's evidence bundle. Its recommendation supplements the required independent reviews and never grants immutable disposition authority. ### Tier-aware review depth The work packet (and `anvil show` / `anvil next`) carries a derived **review tier** — `light`, `standard`, or `max` — computed from the task's six-dimension score plus its risk-confirmation flags. Read the packet's `Review tier:` line (or the `review_tier` JSON key) and dispatch review effort at the matching depth. Every tier still requires the shared workflow's three independent adversarial whole-task reviews; tiers vary depth, not count: | Review tier | What the reviewer runs before `apply` | |---|---| | `light` | Evidence gate and focused whole-task diff review against acceptance criteria, with three distinct adversarial angles. | | `standard` | Evidence gate and whole-task diff review, tracing relevant callers and regression risks. | | `max` | Evidence gate, diff read, **and** an adversarial pass — dispatch the plugin-local `critic` agent (or the session's strongest reviewer) to actively refute the change. For a task that declares an **evidence contract** (named `claims` / `Artifact assertions`), also dispatch the `sentinel` agent in its **evidence-critic** mode to return a `PROVEN`/`UNPROVEN` verdict per claim (treating diagnostic-category evidence as non-completion) before you approve. High or unconfirmed risk; an unscored task always lands here. | When the task declares an evidence contract, `anvil apply` prints a **claim-grouped verdict** (human `Claim : PROVEN/FAILED/...` lines, or the `claim_verdict` JSON key) and, for a task declaring a contract, an approval **refuses** with exit 1 / error code `claim_unproven` while any enforceable claim is unproven — the task stays in `needs_review`. Read that block first: it tells you exactly which claim the mechanical gate could not prove, so the `max`-tier evidence critic can focus its semantic pass there. An advisory `Intent check` block (`intent_warnings`) flags intents the contract never bound — a prompt to ask whether an artifact assertion is missing. The tier is advisory routing for review *effort* — explicit user authority for `apply --approve` is unchanged at every tier, and a reviewer may always choose a deeper pass than the tier suggests (never a shallower one for `max`). --- ## Scope and limitations The review-and-ship commands are all available: - `anvil list --status needs_review`, `anvil show TASK_ID`, and `anvil apply TASK_ID` (review-only, `--approve`, `--reject --reason "..."`). - GitHub Issues sync is available via `anvil sync github` (see Step 5). - A pass/fail review recommendation is available via the plugin-local `sentinel` agent (see "Composition with Other Skills"); it supplements, never replaces, the human decision. What `finish` deliberately does **not** do: - **No auto-merge and no auto-PR.** `apply` is a state transition only; the git merge / PR is yours to drive (Step 4). This separation is intentional: the reviewer owns the merge strategy and commit message. - **No in-place task-note editor.** To attach context on a "hold" disposition, edit the PRD and re-parse (Step 3, "hold").