Skip to content
Back to skills

Test Spec

ASecurity

Spec-first testing — derive tests from a plan/spec file WITHOUT reading the implementation, so the spec is the source of truth: a failing test means the CODE is wrong, not the test. For pure-logic units (calculations, transforms, validators, parsers, state reducers, filters). Enforces verbatim spec-quoting, one-test-per-spec-case coverage, and a deviation report. TRIGGER when: a plan/spec (a /prepare plan file, or an inline `f(1.3) → ~1.0` contract) defines pure-logic behaviour to verify — ei...

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 5, 2026
ai-agentsgobashtestingrefactoringgit

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned September 24, 2026

npx -y skills add mik2win/foureyes --skill test-spec --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Test Spec?

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

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

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: test-spec
disable-model-invocation: true
description: >-
  Spec-first testing — derive tests from a plan/spec file WITHOUT reading the implementation,
  so the spec is the source of truth: a failing test means the CODE is wrong, not the test.
  For pure-logic units (calculations, transforms, validators, parsers, state reducers, filters).
  Enforces verbatim spec-quoting, one-test-per-spec-case coverage, and a deviation report.
  TRIGGER when: a plan/spec (a /prepare plan file, or an inline `f(1.3) → ~1.0` contract) defines
  pure-logic behaviour to verify — either test-first before /implement, or as post-implementation
  spec-conformance. DO NOT TRIGGER when: authoring/refactoring tests by READING an existing module
  (use /test), driving new code red-green from scratch (use /tdd), or covering integration wiring,
  IO/handlers/persistence, or anything needing 3+ mocks (use /test).
allowed-tools: Read, Grep, Glob, Bash, Write, Edit
effort: medium
---

# Spec-First Testing: $ARGUMENTS

`$ARGUMENTS` is one of:
- **Plan/spec path** — a `/prepare` plan or spec file under the backlog location.
- **Inline spec** — `"compute_score(1.3, 'gold') → ~1.0, (0.7, 'gold') → ~0.0"`.

If `$ARGUMENTS` is empty, ask which plan or inline spec to test, then stop.

This skill carries only the invariant spec-first discipline. The test/run commands, test layout, and
framework idioms are read at runtime from `.claude/PROJECT.md` and the installed stack rules — never
hardcoded here.

## Core principle

```
Plan (spec) → Tests from spec → Code → Run tests
                                        │
                                  FAIL? → the CODE is wrong (spec = truth)
                                  PASS? → code matches spec ✓
```

**The spec is the source of truth.** Do **NOT** read the implementation while writing tests — tests
come from the spec alone, so they can't be "written to match the bug". A failing test is a finding
about the code, not a prompt to weaken the test.

## Phase 0 — Load profile

1. Read `.claude/PROJECT.md` — **Commands** (`test`, `test:targeted`) and **Architecture** (the test
   layout: where tests, fixtures, and factories live, and the naming convention). If missing or still
   `TEMPLATE`, fall back to the root `CLAUDE.md` (always in context) when it carries the
   commands/test layout — note you're running without a kit profile; only if *neither* has them,
   **STOP** → run `/bootstrap` first.
2. Read `.claude/rules/_generic/testing.md` and every stack testing rule whose `paths` match the
   target module — these carry the concrete framework, assertion style, and factory locations. This
   skill does not restate framework idioms.
3. If `CONTEXT.md` exists, read it so test names use the project's ubiquitous language.

## Phase 1 — Parse the spec (mandatory verbatim quoting)

1. Read the plan at `$ARGUMENTS` (or parse the inline `function(input) → expected_output` form).
   **Do not open the implementation.**
2. **Quote every testable behaviour verbatim** — never paraphrase: function signatures, concrete
   input→output mappings, edge cases, and error conditions, each with its spec line reference.
3. List ALL extracted cases before writing any test, and count them for the coverage check:

```
## Extracted test cases from spec
1. [spec line 45] `compute_score(1.3, "gold") → ~1.0`
2. [spec line 50] `compute_score(bad_input) → raises ValueError`
Total: N cases
```

**Extract only pure logic** — deterministic units with no external boundary: calculations
(`compute_score(x) → number`), transforms/parsers (`parse(raw) → Model`), validators
(`validate(cfg) → bool | raises`), state reducers (`reduce(state, event) → state`), filters
(`check(state) → (factor, blocked)`). **Do NOT extract** integration wiring, network/DB/filesystem
IO, request/message handlers, or anything needing 3+ mocks — those belong to `/test`.

## Phase 2 — Design the test cases

Build a table from the spec — one row per extracted case. Keep the spec's own notation (`~1.0`,
`raises ValueError`); the notation→assertion mapping lives in the reference.

| Function | Input | Expected output | Edge case? |
|----------|-------|-----------------|------------|
| `compute_score` | `(1.0, "gold")` | `~0.5` | boundary |
| `compute_score` | `(bad_input)` | `raises ValueError` | error |

## Phase 3 — Write the tests

**Entering the writing phase → read [`reference/spec-test-patterns.md`](reference/spec-test-patterns.md)**
for the spec-notation→assertion mapping, the table→parametrize pattern, naming, the test skeleton,
missing-implementation handling, and factory/fixture reuse. Render every idiom in **your stack's
framework** (from the installed testing rules), not a hardcoded one.

Non-negotiables:
- Place the test at the project's test path/naming for the target module (PROJECT.md → Architecture);
  the module docstring references the spec file.
- **Concrete assertions** tied to the spec value (`assert result >= 0.9`), never `assert result > 0`
  or `is not None`.
- One spec case → one test (or one parametrized row); a spec table → one parametrized test.
- Reuse the project's factories/fixtures for domain objects (check before building inline); seed all
  randomness; freeze/inject time — the spec cases must be repeatable.

## Phase 4 — Run the tests

Run the **targeted** command (PROJECT.md → Commands → `test:targeted`) on the new test file — never
hardcode the runner. If any test fails, do **NOT** assume the test is wrong → go to Phase 5 and
investigate the code first.

## Phase 5 — Failure investigation (spec = truth)

1. **Show the mismatch** — Expected (from the spec, with file + line) vs Actual (from the run).
2. **Locate the implementation** — grep the source tree for the function under test.
3. **Root-cause it** — ask *"why doesn't the code produce what the spec expects?"* Common causes:
   formula wrong, parameters swapped, missing normalization, edge case unhandled.
4. **Suggest a fix direction for the CODE** — do **not** modify the test to make it pass; the spec
   defines correct behaviour. The one exception: if the **spec itself** is contradictory or wrong,
   **STOP and ask** — do not silently pick a winner.

Worked example → `reference/spec-test-patterns.md`.

## Phase 6 — Verification

- Re-run the **targeted** command — the spec tests behave as expected (pass, or fail pointing at a
  real code defect you've reported).
- Run the **full** `test` command (PROJECT.md → Commands) — no regressions elsewhere.
- Optionally check coverage of the target module if the project defines a coverage command.

For spec tests, **Timely** (the T in FIRST) means *written from the spec before implementation* —
the strongest position, because the test cannot have been shaped by the code.

## Spec-compliance rules

**MUST**: write a test for **every** case extracted in Phase 1; quote the spec line in the test
docstring/comment; report coverage — which cases are tested, which are not.

**CANNOT without asking FIRST**: skip any case (even one that looks redundant); merge cases in a way
that loses coverage; declare a case "too hard to test".

**STOP and ask when**: you want to skip a case, a case describes behaviour that seems genuinely
untestable at this layer, or the spec contradicts itself.

## Output

```markdown
## Spec-based tests: <plan/spec name>

### Extracted cases (from spec)
| # | Spec location | Case | Covered? |
|---|---------------|------|----------|
| 1 | line 45 | `compute_score(1.3, "gold") → ~1.0` | yes |
| 2 | line 50 | `compute_score(bad_input) → raises` | no — skipped (reason) |
**Coverage: X/Y spec cases**

### Spec deviation report — ALWAYS include ("No deviations" if all covered)
| # | Spec said | What I did | Reason |
|---|-----------|-----------|--------|
| 2 | invalid input raises | skipped | user approved — input unreachable in production |

### Tests written
- `<test path>` — N cases (M parametrized)

### Test results
- Passed: X · Failed: Y · Skipped: Z (awaiting implementation)

### Failures (if any)
| Test | Expected (spec) | Actual (code) | Root cause | Fix direction |
|------|-----------------|---------------|------------|---------------|
```

## How this fits the pipeline

- **`/prepare` → plan → `/test-spec` → `/implement`** — the recommended TDD flow: derive failing
  spec tests from the plan, then implement until they pass (the plan is the shared spec).
- **Post-implementation** — run `/test-spec <plan>` after `/implement` to prove the code conforms to
  the spec it was built from.

## Cross-reference

- **`/tdd`** — drives *new* code red→green→refactor one slice at a time. `/test-spec` writes the spec
  tests up front from a written plan; use `/tdd` when there is no spec and you're discovering the
  design as you go.
- **`/test`** — code-first: reads an *existing* module to author/refactor tests for coverage,
  fixtures, async, and antipatterns. `/test-spec` deliberately does **not** read the code.
- **`/implement`** — executes the plan `/test-spec` derived tests from; mirrors its **Commit
  Message** pattern for the block below.

## Persist to the artifact

*(Mandatory when `$ARGUMENTS` or the session brief names a plan, card or spec file.)* Follow [`../implement/reference/work-log.md`](../implement/reference/work-log.md). Keep a ledger on disk from the first phase and record deviations the moment they are decided. Before the final reply, append `## Test-Spec Log — <date> — <verdict>` to that file; its `### Result` is the extracted-cases table, the spec deviation report, the results, and the failures with their root-cause direction. Anything you could not observe yourself goes under `Re-check later`, with its command and today's baseline: a post-deploy log grep, a soak count, a UI gesture. The chat output is a copy of the log, never the only record. No planning artifact → skip this section.

## Commit
*(Only if test files were written, and only if tracked files changed)*

Offer a copy-paste `git add <explicit test paths>` + **plus the plan/card file the log was appended to** + `git commit` block — follow the **Commit
Message** pattern in `/implement`: explicit paths only (never `-A` / `.` / a bare directory), text
the user pastes (**never run it**), no `Co-Authored-By` trailer.

Files in this skill

  • SKILL.md9.4 KB
  • reference/spec-test-patterns.md6.6 KB

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.