Skip to content
Back to skills

Verify Acceptance

ASecurity

Review a completed bead's implementation against its acceptance_criteria, criterion-by-criterion, and emit a strict-mode `report.json` (schema in `djinn/internal/djinn/report/`). Use after /review but before declaring STATUS COMPLETE. The bead-axis counterpart to code-axis review — answers "did we build what was asked for?" not "is the code well-structured?" Triggers on: verify acceptance, bead coverage check, check acceptance criteria, did the implementation match the bead.

  • 4 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 5, 2026
developmentgobashgit

Works with

  • cli

Security analysis

A100/100

Scanned October 5, 2026

npx -y skills add kurofune/claudit --skill verify-acceptance --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Verify Acceptance?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Verify Acceptance
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/kurofune-verify-acceptance/badge)](https://www.skillsdirectory.com/skills/kurofune-verify-acceptance)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
# Generated by djinn-gen-adapters from theme=djinn. DO NOT EDIT — regenerate via djinn-gen-adapters.
# djinn-compat: 1
name: verify-acceptance
description: >-
  Review a completed bead's implementation against its acceptance_criteria, criterion-by-criterion, and emit a strict-mode `report.json` (schema in `djinn/internal/djinn/report/`). Use after /review but before declaring STATUS COMPLETE. The bead-axis counterpart to code-axis review — answers "did we build what was asked for?" not "is the code well-structured?" Triggers on: verify acceptance, bead coverage check, check acceptance criteria, did the implementation match the bead.
---

<!-- djinn-compat-check: begin -->
**Before anything else:** run `djinn skills check --here` in the repo you are working in. If it exits non-zero, stop: relay its output to the user verbatim — it names the fix — and run no other step of this skill. On exit 0, continue.
<!-- djinn-compat-check: end -->

# Verify Acceptance

## Overview

Bead-axis review: judge every acceptance criterion against the diff plus the evidence artifact and emit a `report.json` that names each criterion's verdict, a non-empty note, and the diff files that satisfy it. This complements `/review`, which reviews code health.

**Scope boundary — read this before anything else.** Findings MUST be grounded in the literal text of `bead.acceptance_criteria`. Do NOT expand scope. If the implementation is missing a capability that seems useful but is not named in the criteria, that is a bead-authoring concern, not a `/verify-acceptance` finding. This skill answers exactly one question: *does the diff implement every criterion the bead names?*

**Strict mode — read this twice.** A `report.json` accepts an iteration only when `complete: true` AND every criterion has `status: "pass"`. Acceptance has exactly two branches: all-pass → `complete: true`, or anything else → `complete: false` with a non-empty `reason`. The harness rolls the iteration back on `complete: false`; the Author then fixes the gap (or files a separate bead for genuinely-out-of-scope work and reduces the parent's criteria) and re-runs. The strict-mode rule is enforced by `report.IsAccept` (`djinn/internal/djinn/report/accept.go`) — emitting `complete: true` with a partial or fail criterion will be rejected by the gate regardless of how persuasive the note reads.

## Inputs

The invoker must provide:

1. **`bead_id`** — e.g. `phoenix-a4os`
2. **Diff base ref** — the commit before this bead's work started. Under DJINN, use the harness `pre_sha` (the fallback below names where to read it). Otherwise `HEAD~N` where N is this iteration's commit count.
3. **Evidence** — the bead's latest evidence comment, read with `bd comments <bead_id> --json` (djinn-mr56i; there is no evidence file)

Only input 1 is guaranteed: in formula mode the harness's verify step passes the bead id and nothing else, and there is no operator in the loop to supply the rest. If an input is missing, derive it from convention and continue — never stop, ask, or wait for a human: the diff base ref is the pre-implement SHA the harness records (`pre_sha`) — read it from the iteration's `[run.N] <bead_id>` child bead with `bd show <run_bead_id> --json | jq -r '.[0].metadata.pre_sha'` (written at run-open time by `internal/djinn/session/iter_beads.go`); when that bead or field is unreadable, fall back to `git merge-base <default-branch> HEAD`, and outside a harness run to `HEAD~N` for this iteration's commit count. The evidence is always the bead's own comment thread — no path to pass. In formula mode the harness records that comment in its `commit-push` step, which runs AFTER verification, so inside a harness run (`DJINN_SESSION=1`) the bead usually carries no evidence comment for this iteration yet: when none is present there, skip the row-count check in Step 4 and judge every criterion from the bead text and the diff alone; in that mode its absence is never a `fail`. Outside a harness run the evidence comment is the author's §6 obligation and is expected to exist: a missing one is a `fail`.

## Process

### Step 1. Read the bead

```bash
bd show {bead_id} --json | jq -r '.[0].acceptance_criteria'
```

Use field name `acceptance_criteria` (NOT `acceptance` — that key is always null).

### Step 2. Split criteria into segments

Acceptance criteria follow a line-per-criterion convention (see the project's bead-authoring rules): each non-blank line is one independently-verifiable criterion. Trim whitespace. Preserve order — index `N` in `report.criteria` corresponds to segment `N` (1-indexed).

Older beads written before the convention may use `;` as a separator. For those, split on `;` and best-effort. If you detect ambiguous splits (semicolons inside parenthetical clauses, e.g. `tooltip shows (heartbeat × 3; story_so_far × 0)`), record the issue under the affected criterion's `note` and continue with your best split.

### Step 3. Read the diff

```bash
git diff <base>..HEAD
git diff <base>..HEAD --stat        # for large diffs, then Read individual files
git diff <base>..HEAD --name-only   # canonical diff-file set; criterion.diff_files MUST be a subset
```

Read it end to end for small diffs; use `--stat` to triage which files to open with `Read` for larger changes.

### Step 4. Read the evidence comment

```bash
bd comments {bead_id} --json     # the whole thread; select by run/iter, not by recency
```

**Select the record by IDENTITY, never by recency.** New evidence comments are JSON objects with `schema: "djinn-evidence"` and top-level `run` (string) and `iter` (integer) fields (contract §6). For legacy YAML comments, read the same identity from the first-line header `# djinn evidence — run <run-id> · iter <n>`.

Comments are append-only: they survive retries, fix iterations and reopened beads, so the newest evidence comment on a bead is frequently an EARLIER attempt's — and in formula mode it always is, because the harness records the current run's comment at `commit-push`, after this verification has already run. Judging an older record as if it were this one produces a false row-count `fail` the moment the criteria changed between attempts.

So: read the `run`/`iter` fields (or legacy header) of each evidence comment and pick the one whose `run`/`iter` match the run and iteration you are verifying. Treat every other evidence comment as history — do not judge against it, do not count its rows. A comment with neither the JSON schema marker nor the legacy header is not an evidence record; skip it.

If no comment matches the current run and iteration:

- **Inside a harness run** (formula mode, `DJINN_SESSION=1`): the harness has not recorded it yet. Skip this step entirely and judge from the diff; do not report the absence as a finding — this is the documented skip, not a gap.
- **Outside a harness run**: a missing evidence record is a `fail` — the author owed it under §6.

When the current record IS present, its row count should match the criterion count from Step 2; a mismatch is a `fail` (the harness gate will also reject, but flag it here so the agent can fix before re-invocation).

Each row carries the author's claim: Status, Implementation (file:line), Verification (command or test), Evidence (artifact). Use these as starting points — then validate.

### Step 5. Judge each criterion

For each segment from Step 2, independently assign one of the three closed-enum statuses (`pass | partial | fail`):

**`pass`** — The diff implements the criterion, the evidence row's Implementation `file:line` points to code that actually does what the criterion describes (open the file and confirm), and Verification is a runnable command or real test path.

**`partial`** — Criterion is addressed but a named sub-aspect is not met. Examples: "remains readable with 500 ticks" but only tested at 87; "tooltip shows X, Y, Z" but tooltip shows X and Y only; criterion demands empirical test and evidence offers algorithmic reasoning only.

**`fail`** — No `file:line` in the diff implements the criterion, OR the claimed `file:line` does not exist, OR the claimed `file:line` exists but does not implement what the criterion describes.

For each criterion, validate the author's claim yourself:

- If Implementation names `path/file.ts:42`, open that file and read around line 42. Confirm it implements what the criterion says.
- If Verification is a `playwright-cli` command, confirm it's a plausible way to exercise the criterion (not a placeholder). You do not need to re-run it.
- If Evidence names a snapshot file, confirm it exists under `.playwright-cli/` (list the directory).
- If Evidence is "algorithmic analysis" for a criterion that named a specific empirical threshold (e.g., "< 100ms at 10k entries"), assign `partial` unless the empirical number is produced.

### Step 6. Decide acceptance (strict mode)

- Every criterion is `pass` → `complete: true`, `reason: null`.
- Any criterion is `partial` or `fail` → `complete: false`, `reason: "<one-line summary naming which criteria gated rejection>"`.

There is no third branch. Acceptance carries no follow-up budget — a bead with one partial criterion is rejected. If the gap is genuinely out of scope, the remediation is to file a follow-up bead and update the parent's criteria, not to ship `complete: true` and promise to come back.

### Step 7. Emit `report.json`

Output a single JSON object — no prose, no markdown, no code fences. The Reviewer subprocess writes the JSON to `.djinn/state/<session>/<bead-id>/iter-N/report.json` via the harness's atomic-write helper; a `/verify-acceptance` Skill-tool invocation by the Author emits the same JSON object inline.

The canonical schema is `Report` / `Criterion` in `djinn/internal/djinn/report/report.go`:

```json
{
  "complete": true,
  "reason": null,
  "criteria": [
    {
      "index": 1,
      "status": "pass",
      "note": "<non-empty evidence string: file:line + what it does>",
      "diff_files": ["<path relative to repo root>", "..."]
    }
  ]
}
```

The accept-path shape above omits `suggested_change` because it has no remediation to record. The rejection example below shows it in use.

Validation rules enforced by `report.Parse` (`djinn/internal/djinn/report/validate.go`):

- `complete` MUST be present and a bool.
- `criteria` MUST be present (an array; may be empty at the Parse seam — the evidence gate enforces the count match against the bead's parsed criteria downstream).
- `reason` MUST be a string or null.
- Each `status` MUST be `"pass"`, `"partial"`, or `"fail"` — any other value, or an absent `status` key, is rejected.
- Each `note` MUST be non-empty.
- `suggested_change` is optional; omit the key when not used.
- Top-level and per-criterion keys outside this schema are rejected (`DisallowUnknownFields`) — defends against a Reviewer regression smuggling a `verdict` shadow field.
- Exactly one JSON value per document — a stray second object or any trailing token after the first JSON value is rejected.

Convention (not enforced by `Parse`, but the harness's rollback path expects it): when `complete: false`, fill `reason` with a non-null one-line summary so `rejection.json` carries useful forensic context.

Cross-cutting structural check enforced by the artifacts gate (not `Parse`): each `diff_files` entry MUST appear in `git diff --name-only <base>..HEAD`.

`report.IsAccept` (`djinn/internal/djinn/report/accept.go`) is the strict-mode helper the harness calls: it returns `(true, nil)` only when `complete=true` AND every `status=="pass"`; otherwise it returns `(false, reasons)` and the iteration is rolled back.

## Example — rejection path

Any non-pass criterion forces `complete: false`:

```json
{
  "complete": false,
  "reason": "criterion 12 partial: scalability untested at the named 500-tick threshold",
  "criteria": [
    {"index": 1, "status": "pass", "note": "tick-timeline-utils.ts:12 implements unique-tick mapping over the cell range", "diff_files": ["src/lib/tick-timeline-utils.ts"]},
    {"index": 12, "status": "partial", "note": "Tested at ~87 ticks / 12 out-of-tick only. 500-tick fixture does not exist; virtualization not implemented.", "suggested_change": "Add testdata/500-ticks.json fixture and assert virtualization activation at >50 entries.", "diff_files": ["src/lib/tick-timeline-utils.ts", "src/components/TickTimelineStrip.svelte"]}
  ]
}
```

## Anti-Patterns

- **Adding criteria the bead does not name.** If the author did not write "keyboard shortcut to reset filters," the absence of that shortcut is not a finding.
- **Judging code quality.** Leave style, readability, architecture to `/review`. This skill judges *coverage of promises*, not code health.
- **"Probably pass."** If you cannot validate the `file:line` claim by opening the file, the status is `partial` or `fail` — not `pass` with a caveat.
- **Marking `complete: true` with mixed-status criteria.** This is the original 3-iteration halt incident's failure mode. Strict mode requires *every* criterion at `pass`; the schema validator and `IsAccept` reject the mismatch and the iteration rolls back.
- **Inventing a fourth status.** The enum is closed: `pass | partial | fail`. `report.Status.UnmarshalJSON` rejects every other value at the JSON boundary.
- **Rubber-stamping the evidence comment.** The evidence comment is the author's claim. Your job is to validate, not quote it back.
- **Empirical-vs-algorithmic laundering.** If the criterion names a concrete threshold (latency, count, scale), evidence needs that concrete number. Algorithmic reasoning is `partial` at best.
- **Wrapping output in code fences or prose.** `report.Parse` reads raw JSON. Markdown decoration trips schema validation and the iteration is rejected as malformed.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.