Skip to content
Back to skills

Spec Verify

ASecurity

Verify an implemented screen, component, flow, data model, API or policy against its spec in specs/, rule by rule, write a pass/fail report to specs/_reports/<Name>.md, and hand it to the user to taste. Checks lint, imports, copy, rule-tagged tests, schema and contract, and visual layout via screenshots. Use when asked to "verify against the spec", "/spec-verify AlbumDetail", or at the end of implementing a spec.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
developmentgonodeapi

Works with

  • api

Security analysis

A100/100

Scanned November 7, 2026

npx -y skills add lstebner/recipe-driven-development --skill spec-verify --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Spec Verify?

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

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

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
---
name: spec-verify
description: Verify an implemented screen, component, flow, data model, API or policy against its spec in specs/, rule by rule, write a pass/fail report to specs/_reports/<Name>.md, and hand it to the user to taste. Checks lint, imports, copy, rule-tagged tests, schema and contract, and visual layout via screenshots. Use when asked to "verify against the spec", "/spec-verify AlbumDetail", or at the end of implementing a spec.
---

# Spec verify

You're the cook checking the dish against the recipe before the head chef tastes it. Check the
implementation against the spec **as written**, not against your memory of what was built or
what was discussed along the way. The output is a report the user can read in 30 seconds, so
they don't need to read the code.

## Ground rules

- The spec is the authority. If the code and the spec disagree, it's a ❌. Don't reinterpret
  the rule so it passes, and don't change a test to make it pass.
- If a ❌ suggests the spec itself is wrong, say so in the report and propose an amendment
  (under `## Open amendments`, per `specs/README.md`). Don't edit the spec's protected sections.
- ✅ needs evidence: a file:line, a passing test name, a screenshot, or a request and its response.
- 👤 is for what only a person can judge: whether it looks right, how it feels on a real device,
  whether the copy reads well. Say what to look at. It doesn't block `verified`; it goes on the
  user's list of things to try.
- ⚠️ is for something you could check but didn't (a simulator wouldn't start, a service was
  down). Give the reason. It blocks `verified` until it's checked. Never use 👤 to get past one.
- Only the user can say it tastes right. A clean report earns the right to *ask*; see step 7.

## Steps

If the spec is `deprecated`, stop: there's nothing to verify, and its report stays as it was.
Offer to verify its `replaced_by` instead.

1. `node scripts/spec-lint.mjs`: any errors go at the top of the report.
2. Read the spec, every spec it references, and `specs/tokens.md` (for UI).
3. **Static checks** against the files under the spec's `## Source`:
   - **component:** each state in States can actually be reached the way its "when" says, and
     looks the way it says.
   - **screen / component:** every `uses` component is imported and actually rendered; no
     shared component has been copied in; every `{copy.key}` text appears verbatim
     (placeholders aside); tokens are used rather than raw values wherever Style names them.
   - **model:** the schema, type or migration has every field with the stated type and
     required-ness, and no extra fields the spec doesn't mention; relationships and delete
     behaviour match.
   - **api:** the route matches `endpoint`; request and response shapes match (including the
     referenced models); every row in Errors is produced with that status and code (or, for a
     service that sends no codes, the message the row says); Auth is enforced.
   - **flow:** each step's screen or API exists and links to the next as written, including every branch.
   - **policy:** the decision is made in one place, and every spec in Used by gets its answer
     from there rather than working it out again or adjusting it (filtering, reordering, adding
     cases). A second copy of the logic is a ❌, even if it currently gives the same answer.
4. **Tests:** find tests tagged `[<RuleID>]` (`grep -rn "\[AlbumDetail.R"`), run them, and record
   pass or fail. If a rule has no tagged test, say so. Offer to write one, but don't block the report on it.
5. **Run it:**
   - **screen / component / flow:** run the app using the project's own run or verify skill if
     there is one (e.g. a simulator or e2e skill), otherwise the built-in `run` skill. Screenshot
     every breakpoint and state the spec mentions and compare each against the Layout tree, node
     by node and in order. For a flow, walk every path through Steps, branches included.
   - **api:** call it for the happy path and for each error code; record request and response.
   - **model:** exercise each Rule (invariant) against the real storage layer if you can.
   - **policy:** run the decision for each Examples row and each rule, and record what you
     gave it and what came back. Then check that each spec in Used by shows the same answer for
     the same input (e.g. AlbumDetail, GetAlbum and CreateOrder agree for a listener who owns the
     album, one who doesn't, and one whose order was refunded).
6. Get the fingerprint with `node scripts/spec-lint.mjs --hash <Name>` and the components line with
   `node scripts/spec-lint.mjs --components <Name>`, then write `specs/_reports/<Name>.md`
   (overwrite it) and show the user the summary table. The fingerprint covers the decision of
   every policy the spec relies on, through other policies too, so a verified spec whose policy
   has changed since its report goes stale: re-verify it against the policy as it is now. The
   components line records each shared component the spec is built from as you checked it. When
   one changes later, lint warns that the spec needs auditing. If you're here because of that
   warning, check what the component's change means for this spec in particular.
7. **Status.** If the spec was `approved`, you may set `implemented` once it's built. If every
   rule is ✅ or 👤, no row is ❌ or ⚠️ and lint is clean, propose the taste test:
   - Tell the user in 2–5 bullets **what to try** to taste it themselves: every 👤 row first,
     then the screens, inputs or calls that exercise the rules most likely to be subtly wrong.
   - Then set `status: verified`. The hook re-checks the report and asks the user to confirm. If
     they deny it, ask what's off: it's either a bug to fix or an amendment to the spec.
   - Never set `approved`, and never edit the report to make the hook accept it.

## Report format

```markdown
# <Name>: verification report
Date: YYYY-MM-DD · Spec: 3f9a1c0b7e2d · Status: implemented · Lint: clean
Components: ArtistBadge 8c41d07e9a2f, PrimaryButton 5e0b3a9d1c76

| rule | result | evidence |
|---|---|---|
| AlbumDetail.R1 | ✅ | test "[AlbumDetail.R1] hides buy when owned" passes; page.tsx:42 |
| AlbumDetail.R2 | ❌ | timeline renders oldest first (screenshot md-default.png) |
| AlbumDetail.R3 | 👤 | spinner timing: tap Buy on a slow connection and check it feels immediate |
| Layout | ⚠️ | <md breakpoint not checked: simulator unavailable |
| Copy | ✅ | 4/4 keys found verbatim |
| Shared components | ✅ | ArtistBadge, PrimaryButton imported from packages/ui |

## Failures
- AlbumDetail.R2: <what's wrong, where, and the suggested fix>

## Proposed amendments
- (none)
```

Use one row per rule, with the rule ID alone in the first column, plus rows for the other checks
that apply (Layout, Copy, Shared components, Schema, Contract, Errors, Paths). The hook reads this
table: every rule needs a ✅ or 👤 row, and any ❌ or ⚠️ row blocks `verified`. It also reads the
`Components:` line, exactly as `--components` prints it (`none` if the spec uses no components),
and refuses `verified` if the line leaves out a component the spec uses or records an older one.
For a spec with no components the line is just a record: if it starts using one later, its own
fingerprint changes and the report goes stale anyway.

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.