Spec Kit, Kiro, OpenSpec, BMAD, specdx: what each one actually checks
Two of the five tools check a spec with code. I wrote one password-reset spec with eight seeded defects and ran each tool’s own command on it. OpenSpec 1.14.3 caught three and specdx 0.5.0 caught five. Spec Kit 1.2.3 and BMAD 6.12.4 give spec review to the agent, so they caught none without one. I did not run Kiro.
What each spec-driven development tool checks without an agent
Every one of these tools gives an agent a workflow: write requirements, then a design, then tasks, then code. They differ in what happens to a spec when no model reads it. Some ship a validator that runs in CI. Others ship prompts that ask the agent to review the spec, and those checks run only when an agent runs them, in a different way each time.
| Tool | Version | Check that runs as code | Exit code on the seeded spec | Seeded defects caught |
|---|---|---|---|---|
| Spec Kit | 1.2.3 | check-prerequisites.sh: the files exist and plan.md is not empty |
0 | 0 of 8 |
| Kiro | — | Requirements analysis: an LLM writes logic, an SMT solver checks it | not run | not measured |
| OpenSpec | 1.14.3 | openspec validate |
1 | 3 of 8 |
| BMAD | 6.12.4 | None for the PRD; lint_spine.py for the architecture document |
— | 0 of 8 |
| specdx | 0.5.0 | specdx lint and specdx check |
1 | 5 of 8 |
I maintain specdx, and I chose the eight defects. Both facts lean the test toward the checks I think matter, so the next section gives the result for each defect and you can weigh them yourself. All four installs and runs are from 2026-10-23.
Eight seeded defects and which tool caught each one
The feature is a password reset by email: six requirements, written once in each tool’s own format. Each copy carries the same eight defects.
| Defect | Spec Kit | OpenSpec | BMAD (PRD) | specdx |
|---|---|---|---|---|
| D1. A requirement with no acceptance scenario | — | error | — | warning |
| D2. “The reset page must load fast and be user-friendly” | — | — | — | — |
| D3. A requirement with no MUST or SHALL | — | warning | — | — |
| D4. “The token must expire after TBD minutes” | — | — | — | warning |
| D5. The same requirement written twice | — | error | — | — |
| D6. A reference to a spec that does not exist | — | — | — | error |
| D7. A source file named in the spec that does not exist | — | — | — | error, once approved |
| D8. A section heading with nothing under it | — | — | — | warning |
OpenSpec and specdx overlap on one defect, D1. Between them they catch seven of the eight. One defect, D2, passed every validator I ran.
Read D6 and D7 with care. specdx has frontmatter fields for a reference to another spec and for the files a spec promises, so in the specdx copy those two defects sit in fields a program reads. The other formats have no such fields, so there the same defects are a sentence of prose. Two of the five specdx catches come from that difference in format, not from a stricter check of the same text.
Spec Kit leaves every content check to the agent
specify init for Claude Code wrote 10 skills, about 2,500 lines of
instructions, and 6 shell scripts. The scripts create feature folders and
resolve paths. The only check on a spec is in check-prerequisites.sh: it
fails when the feature folder or plan.md is missing, when plan.md is empty,
and, with flags, when spec.md or tasks.md is missing. On the seeded spec it
printed the feature path and exited 0.
The real review is the /speckit-analyze skill. It tells the agent to run six
detection passes: duplication, ambiguity, underspecification, alignment with
the project constitution, coverage gaps, and inconsistency between the spec,
plan and tasks. The ambiguity pass names the exact words in D2: “Flag vague
adjectives (fast, scalable, secure, intuitive, robust) lacking measurable
criteria”. It also names placeholders such as TODO, and the duplication pass
covers D5. But no code runs them. The skill even asks for “Deterministic results: Rerunning without changes
should produce consistent IDs and counts”, which is an instruction to a model,
not a property of a program.
Kiro checks requirements with an LLM and an SMT solver
Kiro is an IDE and a CLI, not an npm package, so I did not run it; this section
comes from its specs documentation and its 12
May 2026 post on requirements analysis.
A Kiro spec is three files: requirements.md, design.md and tasks.md.
Requirements analysis is an optional step before design. An LLM first rewrites vague criteria into testable ones. A second LLM step translates each criterion into SMT-LIB logic, samples several translations, and groups them by logical equivalence. Where the samples disagree, Kiro asks a question. An SMT solver then checks the model for conflicts and gaps. Each finding becomes a question with two options: keep the requirement, or change it.
That design aims at D2, a requirement at the wrong level of detail, which no validator in my test caught. The solver step is deterministic, but its input is the output of an LLM, so the full pipeline is not. I have no measurement of how often it is right.
OpenSpec validates requirement structure
openspec validate is a 953-line validator. It requires a Purpose and a
Requirements section, at least one #### Scenario: for each requirement, and
unique requirement names. It warns when a requirement has no SHALL or MUST,
when a Purpose is under 50 characters, and when a requirement is over 500
characters. For change proposals it checks that a “Why” section is 50 to 1,000
characters and that no requirement is both added and removed in the same
change.
On the seeded spec it found D1 and D5 as errors and exited 1. D3 is only a
warning: alone, it passes with exit 0, and fails only with --strict. OpenSpec
has no field for the files a spec promises, and its references point at other
OpenSpec stores, not from one spec to another, so D6 and D7 were prose that it
does not read. It also passed the inline “TBD” in D4: its placeholder check
reads only the Purpose section.
BMAD lints the architecture document, not the PRD
The bmad-method CLI has three commands: install, status, uninstall. There is
no validate. The install for Claude Code wrote 29 skills in 235 files. PRD
validation is a rubric for a “validator subagent” with seven dimensions, and it
says so directly: it wants judgement, “not box-ticking”. The seeded PRD had no
code to run against it, so BMAD caught 0 of 8 without an agent. My
walkthrough of the method is linked from the vibe coding post.
BMAD does ship one linter, lint_spine.py, for the architecture spine. Its
docstring is the clearest sentence any of these tools has on the subject: “LLMs
miscount IDs and miss literal placeholders; a grep does not.” It checks four
things: placeholder text, duplicate or out-of-order decision IDs, decisions
missing their Binds, Prevents or Rule field, and stack rows with no version. On
a seeded spine it found the TBD, the reused ID, the missing Rule and the
unpinned version, and it passed a Rule that said the page “must load fast and
be user-friendly”. It also exits 0 every time, by design: the findings go back
to the agent, and the agent decides.
specdx lints references, placeholders and declared files
specdx 0.5.0 has 15 lint rules: 6 for structure and 9 for content. One of
them, clarity/ambiguity-score-ai, calls no model; it prints a pointer to an
AI mode when an API key is set. The other 14 run as code. specdx check then
compares the files and exports a spec declares with the files on disk.
It caught D6 as an error and D4 and D8 as warnings. It caught D1 as an empty
Acceptance Criteria section, because in specdx a story’s scenarios are a
section. It caught D7 only after I set the design spec to approved. While the
spec is a draft, specdx check treats its files as planned, not missing, and
exits 3: nothing checkable.
It missed D3 and D5. Its vague-language list has 13 phrases, among them “TBD”, “as needed” and “simply”, and “fast” is not on it. It also printed three warnings about missing edge cases that I had not seeded. This site pins specdx 0.4.0-alpha.24 in CI; the sdx reference page covers the tool, and what dogfooding changed in specdx covers the fixes this site caused.
No validator caught “load fast”
D2 is the easiest of the eight to write by accident, and it is the one every validator passed. “Fast” and “user-friendly” are well-formed, have a MUST, and have an acceptance scenario. Nothing in the text is wrong in a way a parser can see. The defect is that nobody can write a test for it.
That is the line between the two kinds of tool. Code catches what is missing or broken in the structure: a section, a scenario, a reference, a file. Judging whether a requirement means something takes a reader, and every tool here that tries it, whether Spec Kit’s analyze skill, BMAD’s rubric or Kiro’s solver, puts a model in that seat. I wrote about where the structural checks stop in spec drift detection, and about the practice around them in spec-driven development.
So choose by which half you want code to own. If you want CI to fail on a broken spec, the choice is OpenSpec or specdx, and the per-defect table says which defects each one covers. If you want a review of what the requirements mean, all three of the others give that to the agent, and only Kiro adds a solver.
How to rerun the comparison
The versions were Spec Kit 1.2.3 (uvx --from git+https://github.com/github/spec-kit.git@v1.2.3 specify init --integration claude), OpenSpec 1.14.3, BMAD 6.12.4 with the bmm module, and specdx
0.5.0, on Node.js 24.13.0. The commands were:
bash .specify/scripts/bash/check-prerequisites.sh --json --require-tasks --include-tasks
openspec validate --specs --strict --no-interactive
specdx lint && specdx check
python3 .claude/skills/bmad-architecture/scripts/lint_spine.py --workspace <dir>
Write the eight defects into each tool’s own format, not a shared one: an
OpenSpec requirement needs a #### Scenario: heading, a specdx PRD feature
needs a **F1**: prefix and a user story to cover it, and a Spec Kit
requirement needs an FR-001 ID. A defect written in the wrong format tests
the parser, not the check. A new release can change any row, and this page’s
revision log records each rerun.
Revisions
- Created; one password-reset spec with eight seeded defects, run that day through Spec Kit 1.2.3, OpenSpec 1.14.3, BMAD 6.12.4 and specdx 0.5.0. Kiro described from its documentation, not run.
- Published, on its slot; auto-drafted by the publishing run because no draft existed for the row.