# sdx: a toolchain for spec-driven development

sdx — published on npm as specdx — is a CLI toolchain for spec-driven development. It gives project specs a formal schema, validates and lints them deterministically with no LLM calls in the core pipeline, packs them into token-budgeted context for agent sessions, and detects spec-to-implementation drift. This is its canonical reference page.

Published: 2026-07-27
Updated: 2026-07-28
Canonical: https://umar.codes/sdx

## Revisions

- 2026-07-27 — Created as a living reference page; outline only.
- 2026-07-27 — Wrote the full reference body from the sdx repo, its spec suite, roadmap, and the npm registry.
- 2026-07-28 — specdx 0.4.0-alpha.4 is published: updated the version count and the design-decisions section, which had recorded the schema fix as committed but unreleased.
- 2026-07-28 — Published.

---

sdx — published on npm as [`specdx`](https://www.npmjs.com/package/specdx) — is a CLI
toolchain for spec-driven development. It gives
specs a formal schema, validates and lints them deterministically — no LLM calls in the core
pipeline, no API keys — packs the relevant ones into token-budgeted context for an agent
session, and checks implementation drift afterwards. This page is its canonical reference;
the revision log records changes.

It is alpha software, and this page says so plainly: fourteen published versions since March
2026, all prereleases — 0.4.0-alpha.4 is current. The practice it tools up is documented separately in
[Spec-driven development](/spec-driven-development); this page is about the tool.

## What sdx is

The problem, as I keep meeting it: most AI-assisted development workflows are ad hoc.
Specs — where they exist at all — are markdown files in inconsistent formats, pasted into
context windows by hand, with no validation, no diffing, and no CI integration. The agent is
asked to respect constraints that nothing enforces. sdx makes the spec a first-class,
machine-checked artifact: a schema says what a spec must contain, a linter holds the line, a
packer decides what an agent session actually needs to read, and two drift checks catch the
places where documents and code have quietly stopped agreeing.

One naming wrinkle, stated once: the project is called **sdx**; the npm package and the
installed binary are both **`specdx`**. The shape underneath is a ten-package monorepo with
one-way data flow — a schema package at the bottom, a core parser above it, then lint, pack,
diff, and check as siblings, all bundled into a single CLI with sixteen subcommands.

The minimal loop looks like this:

```bash
npm install -g specdx

specdx init --name "my-app" --template lightweight
# → spec.config.yaml plus stub specs with their required sections scaffolded

specdx pack --task "implement user authentication" --copy
# → the relevant specs, token-budgeted, on your clipboard for the agent session

specdx check   # after implementing: does the code still match the specs?
specdx lint    # before committing: are the specs themselves still healthy?
```

Everything deterministic exits 0 or 1, which is the entire CI contract.

## Why specs, not prompts

The argument for working spec-first is made in [the methodology page](/spec-driven-development)
and I won't repeat it here. The argument for *tooling* is narrower: without enforcement, "we
have specs" degrades into "we have markdown files someone hopes the agent reads." Each command
exists because I kept hitting a specific failure that hoping didn't fix:

- A spec missing the section that mattered — acceptance criteria, non-goals — and nobody
  noticing until an agent invented the missing part. → `validate` and `lint`.
- A PRD updated while the test plan that depends on it stayed put. → `diff`, which walks the
  dependency graph and flags downstream specs as stale.
- An implementation drifting from its API contract weeks after everyone stopped re-reading the
  spec. → `check`.
- Pasting the same 30 KB of spec into every session, or worse, choosing by memory which parts
  to paste. → `pack`.

Prompts are ephemeral; specs are reviewable artifacts. The toolchain's job is to keep the
artifacts honest at machine speed.

## The pieces

**The schema.** Nine spec types — PRD, technical design, user story, test plan, ADR, API
contract, epic, quick-spec, and project-context — each with named required sections (a PRD
must contain Problem Statement, Goals, Non-Goals, Features, and Success Criteria; an ADR must
contain Context, Decision, Status, Consequences). Seven required frontmatter fields per spec,
and typed cross-references between specs with five relationship kinds, from `implemented-by`
to `supersedes`. A `spec.config.yaml` at the repo root declares the suite: which specs exist,
their types, and what requires what.

**The linter.** Thirteen rules: five structural ones that default to `error` (valid
frontmatter, required sections, valid references, no circular dependencies, a single
project-context spec) and eight content rules that default to `warn` — staleness, vague
language, terminology consistency, story coverage, edge-case coverage among them. Three
presets: `minimal` is the five structural rules, `recommended` is all thirteen, `strict` is
all thirteen with every severity forced to error. Errors fail the build; warnings don't.

**The packer.** Three stages: resolve, allocate, format. Relevance is scored from your task
string against each spec — tag and title hits weighted ×3, section headings ×2, body ×1 —
and specs adjacent in the dependency graph inherit half the score, so packing "implement
auth" pulls in the design doc the auth story depends on. The budget defaults to 12,000
tokens. A project-context spec gets a reserved allocation (up to 2,000 tokens, uncompressed)
before anything else competes, because the coding-rules spec is the one you never want
evicted. Remaining specs fill greedily by relevance until the budget runs out. The token
report prints to stderr so stdout stays pipeable.

**The differ.** Spec-to-spec drift between two git refs: which specs changed, and which
downstream specs are now suspect, each with a staleness score. The default staleness
threshold is 14 days.

**The checker.** Spec-to-implementation drift, fully static — an AST pass, not a model.
Routes extracted from Express, Hono, or Next.js App Router code are matched against API
contract specs; TypeScript interfaces, Zod schemas, and Prisma models against a technical
design's data model; test titles against test-plan cases by word-set similarity. The output
is an implementation-coverage percentage. Code that exists but isn't in any spec is reported
as `extra` and never penalised — the score only drops for what's missing or mismatched,
because the specs constrain what must exist, not everything that may.

**The gate.** `specdx ready` runs five checks — required specs present, zero lint errors, no
broken references or cycles, nothing stale, every PRD feature covered by a story — and exits
1 if any fail. It's the boundary between planning and building, and it's what CI runs.

**The agent surface.** The package ships nine Claude Code skills (spec authoring, drift
checking, pre-commit review, task kickoff among them) and an MCP server exposing seven tools,
so agents can run the same checks the CLI does without shelling out blind.

## Using sdx with an agent

The working loop on a real project: `pack --task` before the session, so the agent starts
from the specs that matter instead of the whole suite; the agent implements; `check` and
`lint` after; `ready` in CI as the gate. sdx's own repository runs exactly this — its CI
workflow executes `validate`, `lint`, and `status` on every spec change, plus
`diff --base origin/main` to flag staleness on pull requests. The tool maintains a
fifteen-spec suite about itself and holds it at zero lint errors.

The design decision that shapes all of this: the core pipeline makes **no LLM calls**. The
reasoning engine in this loop is the agent already sitting in your editor; sdx's job is to
hand it validated context going in and deterministic findings coming out. There is exactly
one exception, and it's opt-in: `check --ai` sends the already-computed findings (not your
source code) to a model to classify likely false positives. It requires an API key, and
nothing in the workflow requires it.

## Design decisions

Numbered and dated, mirroring the revision-log ethos of this site:

1. **2026-03 — Skills-first, not API-first.** The original design had sdx calling LLM APIs
   directly for intent analysis, ambiguity scoring, and drift detection — which meant
   provider abstraction, cost estimation, caching, confidence thresholds. Recognising that
   the developer's coding tool already has a model in it cut roughly 40% of a phase's scope
   and left the core deterministic and CI-safe.
2. **2026-03 — No provider-specific packing formats.** Pack emits XML, markdown, or JSON.
   Modern models handle all of them well enough that per-provider formatting is marginal
   complexity for marginal gain.
3. **2026-03 — `js-tiktoken` for token counting.** Accurate enough (the test target is
   within 5% of real tokenisation) with no native dependencies to break installs.
4. **2026-07 — Derive every spec-type enum from one source of truth.** The config schema had
   drifted from the sections registry: it accepted six of the nine spec types, making the
   other three impossible to declare at all. The fix derives the enums from a single
   `SPEC_TYPES` constant and adds drift tests against the raw schema JSON, so the two can't
   disagree again.

Decision 4 came from dogfooding. Setting sdx up on this website is what surfaced the bug —
and the root cause was its own small horror story: the schema package's incremental build
didn't track JSON files, so a correct fix could land in source one minute before a publish
and still ship a stale artifact. The fix shipped in 0.4.0-alpha.3, four months after the bug
did — an earlier revision of this page recorded the gap while it was still open, which is
exactly what a revision log is for.

## Roadmap

What's deliberately not built yet, and what each item is waiting on:

- **Confidence-scored findings.** `check` currently reports ~59 spurious "add a test
  matching…" warnings against sdx's own suite, because word-set similarity can't reliably map
  prose test-plan bullets to real test titles. Findings need a confidence score so CI can
  block on high-confidence errors and downgrade the rest — until then, the checker is honest
  but noisy.
- **Workflow enforcement hooks** — detecting when a session skipped the spec steps entirely,
  rather than trusting that it didn't.
- **Spec-aware commits** — commit messages that reference the spec IDs they implement.
- **Methodology modules as npm packages** — pluggable spec methodologies beyond the built-in
  templates. Deferred until the core templates have survived more real projects.
- **Cross-repo spec federation.** The tool is monorepo-first by decision; federation waits
  for a real multi-repo consumer, not a hypothetical one.

The constraint carried across all of it, from the roadmap's own risk notes: AI-assisted
features stay opt-in and are never required for the core workflow. The deterministic pipeline
is the product; everything else negotiates for a place around it.
