The scenario can fail
Gherkin keywords are case-sensitive, so GIVEN is prose and
the scenario runs zero steps — and passes. The gate refuses a scenario
Cucumber would see as empty.
Zero runtime dependencies Any agent CLI MIT
Requirements, Gherkin scenarios and a traceability matrix that your CI enforces — plus a harness that drives any coding agent through them, one requirement at a time, in its own git worktree. It never merges.
npx create-spec-driven-app@latest init
Get started Read the docs Worked example →
Most teams already write requirements. They live in a wiki, drift from the code within a sprint, and nothing fails when they disagree. Point a coding agent at that and you have automated the drift.
This tool makes the specification a thing that can be wrong:
every requirement has a scenario, every scenario has a file, and
csda validate fails when the link is broken. Then — and only
then — an agent is worth pointing at it.
One row of the traceability matrix, read left to right, is a sentence about your system. Every link in it resolves to a real file, and the build fails when one does not.
csda validate fails — which is what stops a specification from
quietly becoming false.
This requirement is demonstrated by this scenario, in this feature file, realised by this use case, which dispatches this command against this aggregate, emitting this event, implemented here, proven by this test, currently in this state.
An event emitted by an aggregate nobody declared is a build failure, the same way an undefined symbol is. That is the whole idea, and everything else on this page exists to keep it true. How a matrix row is written →
Four commands. The same four whether a person or an agent is driving.
csda initScaffold a project: spec, scenarios, traceability matrix, and the execution contract an agent reads before it writes a line.
csda planWhat still needs a test, an implementation or a status change — ordered by dependency, with a fix on every blocker.
csda harness runHand one requirement to an agent in its own worktree, gate the result, and stop if the gate says no.
csda validateThe gate itself: structure, traceability, Gherkin that Cucumber can actually run, and TDD order. This is what CI runs.
Step three in detail. One requirement, one branch, one gate — and the branch is yours to merge or throw away.
Not a mockup. This is stdout from a real run — scaffold, pack, plan,
harness, gate — captured by npm run docs:terminal and committed
to the repository. A test checks it was recorded from this version of the CLI.
~ $ cat smart-parking.yaml
PROJECT_NAME: Smart Parking
PROJECT_SLUG: smart-parking
PROJECT_TYPE: backend
DOMAIN: parking operations
STACK: Node 20, Express, PostgreSQL
API_STYLE: REST with DTO boundaries
TESTING: node:test
~ $ csda init --config ./smart-parking.yaml --out . --no-git
ℹ️ [INFO] 🧩 Rendering base template
ℹ️ [INFO] 🛠️ Applying project type template: backend
ℹ️ [INFO] 🧩 No optional modules selected. Generating base + project-type features only.
ℹ️ [INFO] 📋 Summary
ℹ️ [INFO] - Project: Smart Parking
ℹ️ [INFO] - Slug: smart-parking
ℹ️ [INFO] - Type: backend
ℹ️ [INFO] - Domain: parking operations
ℹ️ [INFO] - Output: ~/smart-parking
ℹ️ [INFO] - Dry-run: false
ℹ️ [INFO] - Git: skipped
ℹ️ [INFO] ✅ Generation completed
~/smart-parking $ csda specops add --pack-root ~/packs --pack multi-tenant/backend --var "PROJECT_NAME=Smart Parking" --var PROJECT_SLUG=smart-parking --var "DOMAIN=parking operations"
ℹ Adding multi-tenant/backend to ~/smart-parking
ℹ️ [INFO] Using pack: ~/packs/multi-tenant/backend/pack.yaml
ℹ️ [INFO] Generated 3 scenario file(s).
ℹ️ [INFO] Domain pack expansion completed.
~/smart-parking $ csda plan
Needs Feature + Test + Code
REQ-002 SCN-001
✓ feature: `features/multi-tenant/provisioning_a_tenant_emits_tenantprovisioned.feature`
REQ-001 SCN-002
✓ feature: `features/multi-tenant/cross_tenant_read_attempts_are_rejected.feature`
REQ-003 SCN-003
✓ feature: `features/multi-tenant/exceeding_a_quota_emits_quotaexceeded.feature`
Needs Implementation
REQ-000 SCN-000
✓ feature: `features/core/health.feature`
· code: `API /health`, smoke test
~/smart-parking $ cat harness.config.yaml
harness_version: 1
agent: 'csda-stub-agent {prompt_file}'
test_cmd: 'node --test test/*.test.js'
max_attempts: 1
~/smart-parking $ csda harness run --req REQ-000
ℹ️ [harness] Processing 1 requirement(s) from base HEAD.
⚠️ [harness] REQ-000: the row declares no test artifact, so the agent chooses where the test goes and the matrix will point somewhere else. [requirement_declares_no_test_artifact]
⚠️ [harness] fix: csda req link REQ-000 --test <path>
ℹ️ [harness] REQ-000: attempt 1/1
── harness report ──
✅ REQ-000 pass (1 attempt) → harness/REQ-000
1 passed · 0 failed · 0 skipped
Review and merge the harness/* branches you trust.
ℹ️ [harness] Run recorded in .harness/runs/2026-01-01T00-00-00-000Z.json — `csda harness report`
~/smart-parking $ git diff --stat main harness/REQ-000
.../REQ-000-2026-01-01T00-00-00-000Z-attempt-1-agent.md | 83 +++++++++++++++++++++++++
docs/specs/traceability.md | 2 +-
src/health.js | 9 +++
test/health.test.js | 10 +++
4 files changed, 103 insertions(+), 1 deletion(-)
~/smart-parking $ git switch -q harness/REQ-000 && node --test test/*.test.js
✔ GET /health reports the service as UP (12ms)
ℹ tests 1
ℹ suites 0
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 1.234
~/smart-parking $ csda validate .
ℹ️ [INFO] ✅ Validation passed
ℹ️ [INFO] - Features detected: 4
ℹ️ [INFO] - Base SDD structure: complete
ℹ️ [INFO] - Traceability mode: rich
No agent runtime, no SDK dependency. The agent is any shell command
containing {prompt_file} — the harness writes the prompt,
substitutes the path, and reads the exit code plus the gate result.
csda harness run --agent "claude -p < {prompt_file}"
csda harness run --agent "aider --yes --message-file {prompt_file}"
csda harness run --agent "./my-wrapper.sh {prompt_file}" # anything else
Commit the commands your team uses in .harness/profiles.yaml
and pick one by name — or let a profile select itself per requirement, so
an infrastructure task and a domain task get different tools and different
allowances in the same run.
Wired from one definition:
claudeclaude-plugincursorcopilotwindsurfaidergeminiclinecodexantigravity
Choosing your agent → How the harness works → Wiring agent tools →
Every item here exists because the loop was caught approving work it had not checked. They are not features; they are scars.
GIVEN parsed
as prose — no error, no warning, and an empty scenario passes. The
in-house linter matched case-insensitively and called all 28 fine.
Given nothing / Then nothing is asserted and got
1 passed · 0 failed. The branch was published and the
requirement closed.
pack lint --strict. A schema that declares authority it does
not exercise is worse than none.
All three were found by running the tool against a real project and reading the output, not by reasoning about it. Each is now a test that fails when the guard is removed.
Gherkin keywords are case-sensitive, so GIVEN is prose and
the scenario runs zero steps — and passes. The gate refuses a scenario
Cucumber would see as empty.
A filter that matches nothing exits 0. When the runner is Cucumber, the gate reads its message protocol instead of its exit code.
An agent that cannot pass a scenario can relax the scenario. Touching
spec.md, features/** or the rules fails the
attempt, with the diff fed back.
The matrix names a test and a production artifact. A green diff that
never goes near them is reported, and fails under
--strict-artifacts.
No feature file, unmet dependencies, or a status of
Needs Clarification — checked before an agent is paid for,
not after.
--budget-seconds and --max-requirements. Running
out is not an error: the run stops cleanly and still reports what it
did.
OpenSpec is the closest thing and the one worth reading first. The honest summary: it is smaller, and smaller is a feature.
| csda | OpenSpec | Spec Kit | A wiki page | |
|---|---|---|---|---|
| Change lifecycle: propose → review → archive | yes | yes — their idea first | no | no |
| Traceability matrix that CI enforces | yes | no | basic | no |
| Gherkin a real runner executes | yes | prose scenarios | via templates | no |
| Versioned, installable domain packs | yes | no | no | no |
| Unattended harness with a gate | yes | no | no | no |
| MCP server and language server | both shipped | no | no | no |
| Concepts you have to learn | more | fewer | fewer | none |
| Shortest path to a working loop today | no | yes | — | yes |
If you want the change loop and nothing else, or your specs are prose rather than a domain model, OpenSpec is the better tool and the table says so. We took the delta format and the audited agent contract from their design. The full comparison, including where we lose →
The specification layer is the interesting half. This is the other half — the answers that decide whether it survives contact with a real organisation.
csda ci init writes the workflow for GitHub Actions,
GitLab, Azure Pipelines, CircleCI or Jenkins. The gate is one command
with a JSON mode, so it fits whatever you already have.
csda alm sync pushes requirements to Jira, Azure Boards,
GitHub or GitLab and csda alm pull brings status back. The
board is a mirror; it never gets to define what a requirement
is.
Zero runtime dependencies. A CycloneDX SBOM is generated on every push
and kept 90 days, the licence policy is a build gate, and every domain
pack is pinned by content digest in .specops.lock.
A language server and an MCP server ship in this repository, along with VS Code and IntelliJ extensions — built and installable from source, not yet on either marketplace.
No. Patterns are optional; principles are not (ADR-0022). Pick an architecture profile and you get only the vocabulary it declares — a static site is not handed six documents describing aggregates it does not have.
Yes — csda adopt installs the specification layer without
touching your code. Checks that a legacy repository cannot pass yet are
flags, and csda doctor reports them as advisories with a
fix, so adoption is a door rather than a wall.
None in particular, and none at all if you do not want one. Every command works by hand. The harness takes any shell command; there is no SDK and no vendor lock.
Never. It produces a branch per requirement and stops. Merging is a judgement, and a tool that merges its own work removes the only place a person was still reviewing it.
Whatever your agent costs — the harness measures wall-clock, because an agent is any shell command and only the agent knows its token spend. A profile may declare a cost hint, and the report multiplies it out and says it is declared rather than measured.
Mostly in what happens after the specification is written. See the comparison, which is honest about where the others are the better choice.
npx create-spec-driven-app@latest init
cd my-spec-driven-app
csda validate .