Propose the test(s) FIRST and get a human to sign off on the behaviour before writing the code — the test analog of /schema-review and /ui-proposal. For hand-written LOGIC in packages/* (apps opt-in, pocs exempt). Renders the proposed failing test as a reviewable contract, holds for lgtm/approve, then you write the code to make it green. Use when an agent is about to implement packages/* logic, or when asked to "write the test first", "TDD this", "agree on the test", run /test-review.
Scanned 9/12/2026
Install to Claude Code
npx -y skills add FriendlyInternet/nuxt-crouton --skill test-review --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Test Review?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/friendlyinternet-test-review)More formats (shields.io, HTML) on the badges page.
---
name: test-review
layer: stage
description: Propose the test(s) FIRST and get a human to sign off on the behaviour before writing the code — the test analog of /schema-review and /ui-proposal. For hand-written LOGIC in packages/* (apps opt-in, pocs exempt). Renders the proposed failing test as a reviewable contract, holds for lgtm/approve, then you write the code to make it green. Use when an agent is about to implement packages/* logic, or when asked to "write the test first", "TDD this", "agree on the test", run /test-review.
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
---
# Test Review — agree on the test before you write the code
Turns "I'm about to write some logic" into a **reviewable contract**: the proposed *failing*
test(s) — the cases being asserted, in plain language plus the actual test code — so a human can
sign off on the **behaviour** *before* any implementation exists. The agreed test is the
definition of done: "done" = that test passes.
This is the third sign-off gate (epic #774), alongside **`schema-review`** (#314, the data model)
and **`ui-proposal`** (#307, the look). Pick the gate by *what the change is*: a data model → schema
gate; how it looks → UI gate; **hand-written logic → here**. It reuses the same revision/approval
loop and signal as the other two (#310/#572).
> **Why test-first?** The test is cheap to change while it's just a proposal and expensive to change
> after the code is built around the wrong assumption. Agreeing on the cases first ("no
> double-booking; adjacent slots OK; buffer respected") means the code has one job — go green —
> instead of being a moving target.
## Step 0 — Self-scope (skip when out of scope). This gate is deliberately narrow.
Decide by **where the code currently lives (#779)**, then by **what kind of change** it is. Skip
loudly (one line saying why + which gate, if any, fits instead) rather than gating everything.
**By location — resolved from the stage model.** Run `node scripts/harness-stages.mjs <path>`
(or `gateMode(path, 'test-first')`) for the verdict (`on` / `opt-in` / `off`) rather than matching
the folder by hand; the canonical source is `harness.config.mjs` (epic #952). The table below is
the **default profile** rendered for humans:
| Current home | Stage | Action |
|---|---|---|
| `packages/*` | `package` | **Proceed** (default — what we maintain; every consuming app inherits its correctness) |
| `apps/*` | `app` | **Skip unless the app has explicitly opted in** (may be another user's app — not ours to impose) |
| `pocs/*` | `poc` | **Skip** — the incubator must stay fast and safe-to-fail; a POC graduating to `packages/*` is the checkpoint to backfill its tests |
**By change kind (only once location is in scope):**
- Collection schema / data model → **skip**, use `/schema-review` (+ the e2e fixture smoke covers generated CRUD).
- `.vue` component / layout / theme — how it *looks* → **skip**, use `/ui-proposal`.
- Deterministic **generated** code (CLI output, templated CRUD) → **skip**, the e2e smoke harness owns it.
- **Hand-written logic** — a composable rule, server util, permission check, calculation, layout-engine transform, a generator's own logic → **proceed.**
> Rule of thumb: `packages/*` *logic* almost always proceeds; everything else almost always skips.
> When unsure whether something is "logic", ask: *can a human just eyeball it for correctness?* If
> yes, it probably doesn't need a test-first contract.
## Step 1 — Pin down the behaviour
Before writing any test, state — in one or two plain sentences — what the logic must do, and
**enumerate the edge cases** that matter. This list IS the thing being agreed on; the test code is
just its executable form. Example (a slot-availability rule):
- a booking that overlaps an existing one is rejected
- two back-to-back bookings (end == next start) are both allowed
- a booking inside another's buffer window is rejected
- times are compared in the resource's timezone, not the server's
## Step 2 — Write the proposed failing test(s)
Author the test next to the code it will cover, following the package's existing test layout
(`*.test.ts` beside the source, or its `__tests__/` dir — match the neighbours; see
`packages/crouton-core` for examples). Vitest is the runner (`pnpm test`).
- One `describe` per unit; one `it` per edge case from Step 1, named in plain language.
- The test must **fail first** — it asserts against code that doesn't exist yet (or asserts the
new behaviour the current code lacks). Red before green is the point; surface that.
- Keep it a **general** contract (all valid inputs), never assertions reverse-engineered to pass a
specific implementation.
## Step 3 — Hand off for sign-off (review happens on the test itself)
**The proposed test is the review surface.** Present both the plain-language case list (Step 1) and
the test code, so the reviewer agrees on *behaviour*, not syntax.
- **Interactive session:** show the case list + test in chat and hold. The reviewer says
`lgtm`/`approve` to proceed, or asks for changes (add a case, fix an expectation) → revise and
re-show.
- **Agent pipeline / PR:** commit the failing test (via `/commit`, scope = the package, e.g.
`test(crouton-core): …`) so it lands in the PR's "Files changed". The reviewer comments inline on
any `it(...)` to change a case. Set the issue to `status:blocked` while holding. Approval is a
**comment** containing `lgtm`/`approve` — **not** a reaction or label (#572) — and it unblocks
implementation.
Iterate Step 1–3 until approved. Do **not** write implementation code before the test is agreed.
## Step 4 — Go green (only after approval)
Write the implementation to make the agreed test pass — and **only** that. Run `pnpm test` (and
`pnpm typecheck`) until green. The agreed test is now the regression guard; it stays in the diff and
ships with the code. Resume the normal workflow (typecheck → update issue → `/commit`).
## Conventions
- The case list (Step 1) is the contract; keep the test code honest to it — never quietly drop or
weaken a case to make implementation easier (that's a re-negotiation → go back to Step 3).
- One concern per test file, mirroring the source it covers.
- Red first, then green. If the proposed test passes *before* you write any code, the behaviour
already exists — say so and skip (nothing to build).
- This is about *order* (test before code), not *enforcement* — the CI `test` job already hard-gates
`pnpm test`.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!