Skip to content
Testing
Skill

/opsx-tdd

Adds a TDD gate to OpenSpec — write REAL failing tests from specs/**/*.md before implementation. Run after opsx:propose and before opsx:apply. Solid RED — never expect.fail placeholders.

From plugin
openspec-tdd
61 skill
Install
$ npx -y skills add yuritoledo/openspec-tdd --skill opsx-tdd --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/opsx-tdd

Context preview

The summary Claude sees to decide when to auto-load this skill.

Adds a TDD gate to OpenSpec — write REAL failing tests from specs/**/*.md before implementation. Run after opsx:propose and before opsx:apply. Solid RED — never expect.fail placeholders.

SKILL.md

opsx-tdd.SKILL.md
name: opsx:tdd
description: Adds a TDD gate to OpenSpec — write REAL failing tests from specs/**/*.md before implementation. Run after opsx:propose and before opsx:apply. Solid RED — never expect.fail placeholders.
license: MIT
compatibility: Requires the openspec CLI and a JS/TS test runner (vitest, jest, bun test, or node:test).
metadata:
  author: yuritoledo
  version: "3.0"

Write **real failing test files** from a change's `specs/**/*.md` and confirm a *solid* RED — tests that exercise the true contract and fail only because the implementation is missing — before implementation begins. Never `expect.fail("TDD Red Phase")` placeholders: those prove nothing, get fully rewritten at green, and leave the feature with zero real coverage.

Reads directly from the canonical spec files. Re-run after a spec change and tests regenerate from source-of-truth automatically.

**Input**: Optionally specify a change name. If omitted, infer from conversation context or prompt.

**Steps**

0. **Discover project conventions** (do this first — never hardcode)

This plugin runs in *any* repo. Detect the host project's testing setup, in priority order:

1. **`.openspec-tdd.json`** at repo root, if present. Fields (all optional):

      {
        "testRunner": "npx vitest run",   // command to run a single test file
        "renderHelper": "@/test/render",  // import path for the project's custom render()
        "canonicalTest": "src/example.test.tsx", // a file to mirror for imports/mocks
        "testIdQuery": "getByTestId",     // how the project queries test ids
        "testFileGlob": "**/*.test.{ts,tsx}"
      }

2. **`CLAUDE.md` / `AGENTS.md` / `.cursorrules`** — read for testing conventions, the canonical-reference test file, render-helper location, and selector rules. 3. **`package.json`** — infer the runner from `scripts.test` / `devDependencies` (`vitest` → `npx vitest run`; `jest` → `npx jest`; `bun` → `bun test`; otherwise `node --test`). Infer React/Testing-Library presence from deps. 4. **Fallback defaults** — bare `vitest`, plain testing-library render, `getByTestId`.

Announce what you detected: "Runner: <cmd> · Render helper: <path|none> · Canonical ref: <file|none>". See `references/frameworks.md` for per-runner commands and `references/non-react.md` / `references/e2e.md` for non-component scenarios.

1. **Select the change**

If a name is provided, use it. Otherwise:

  • Infer from conversation context
  • Auto-select if only one active change exists
  • If ambiguous, run `openspec list --json` and use **AskUserQuestion tool** to let user select

Announce: "Using change: <name>"

2. **Parse scenarios from specs**

   find openspec/changes/<name>/specs -name "*.md" 2>/dev/null

If no `specs/**/*.md` exist in the change, stop: > "No spec files found in `openspec/changes/<name>/specs/`. Run `opsx:propose` first to > generate specs with `#### Scenario:` blocks, then re-run `/opsx:tdd`."

Parse every `specs/**/*.md` file. For each file:

a. **Extract scenarios** — collect every `#### Scenario:` block with its parent `### Requirement:` name and its WHEN / THEN lines.

If a file has no `#### Scenario:` blocks, skip it and note it in the report.

b. **Derive the test file path** — in priority order:

1. **design.md Implementation Map**: scan `openspec/changes/<name>/design.md` for a markdown table with columns `Capability`, `File` (or similar). Extract the row matching this capability's name.

         | `ability-card` | `features/combat/AbilityCard.tsx` | `AbilityCard` |

2. **design.md file path mentions**: scan design.md for any `.ts` / `.tsx` paths that mention the capability name or a related module. 3. **Project search**: `find . \( -name "*.ts" -o -name "*.tsx" \) | grep -v node_modules | grep -i <capability>` — pick the best match (non-test file). 4. **Prompt the user**: use **AskUserQuestion** with the capability name and a list of candidate paths found. Never guess silently.

Test file = implementation file path with `.test.ts` / `.test.tsx` extension, same directory. Example: `features/combat/AbilityCard.tsx` → `features/combat/AbilityCard.test.tsx`.

c. **Translate THEN clauses to assertion intent** — for each scenario, capture the full THEN text. Step 4 uses this to write the real assertion body.

THEN clauses that are too vague to assert (e.g. "THEN the UI updates") are flagged as spec quality gaps in the step-7 report — never padded with a stub assertion.

Announce: "<N> specs · <M> scenarios · source-of-truth: specs/**/*.md"

3. **Write REAL failing tests** (solid RED, not forced fail)

The goal is a *genuine* red: the test renders the real component, calls the real function, queries real `data-testid`s, and fails because the implementation does not exist yet. NOT `expect.fail(...)` placeholders.

Before writing, gather concrete selectors and behavior from:

  • `openspec/changes/<name>/proposal.md` and `design.md` — component names, props,

`data-testid` values, expected copy/values, API/mutation shapes.

  • The WHEN/THEN clauses extracted in step 2 — translate THEN directly into assertions.
  • Existing sibling code and the **canonical test reference** discovered in step 0.

For each scenario group:

  • If the target file does not exist, create it using the conventions from step 0: the

project's custom render/wrapper helper, `screen`, `userEvent`, and the shared mock setup — never a bare testing-library render for component tests when the project has a helper.

  • If a `describe` block with the exact name already exists, append `it()`s inside it.

Otherwise append a new `describe` block.

  • For each scenario, write a **real test body**:
  • Render the component / invoke the unit u
Read more
Ships withopenspec-tdd

Adds a TDD gate to OpenSpec — a new opsx:tdd command that sits between opsx:propose and opsx:apply. OpenSpec generates BDD scenarios in specs/**/*.md but jumps straight to implementation — no test-first step exists. This plugin adds one.

Get the whole plugin
Stats
6
Stars
0
Forks
Maintained
Maintenance
JavaScript
Language
MIT
License
2mo ago
Last commit
3mo ago
Created

Repo: yuritoledo/openspec-tdd