SDD Orchestrator
by dbianco
README.md
# SDD Orchestrator
A standalone [Model Context Protocol](https://modelcontextprotocol.io) server
that gives coding agents the right spec-driven-development (SDD) context at the
right moment.
For each task it:
- **routes** the task to the best-fitting SDD framework (OpenSpec, GitHub Spec
Kit, BMAD, Kiro, or a house workflow) using explainable rules;
- **keeps lifecycle state** per feature and runs deterministic gate checks
between phases, so gates are enforced rather than advisory;
- **assembles a context pack** per phase from a company-wide knowledge base,
with app-scoped memory (ADRs, accepted specs, decisions) and explicit
cross-app lookup.
The host agent (Claude Code, Cursor, or any MCP client) remains the only thing
that edits files, runs tests or spawns sub-agents. The server reads and writes
only its own database.
## Why
Coding agents tend to follow whatever process was in the prompt last, forget
why a past decision was made once the conversation that made it is gone, and
start writing code before checking whether a spec is actually complete. None
of that is a model problem; it is a missing place to keep process state
outside the agent's own context window. This server is that place: one
explainable choice of framework per task, phase gates that block on the
artifact's own text rather than on trust, and a knowledge base that survives
across sessions, branches and hosts.
## Use cases
- **Brownfield feature work.** "Add CSV export to the orders page" against a
service with an existing `openspec/` directory routes to OpenSpec's
`default` track: a proposal and delta spec gated on measurable acceptance
criteria before `implement` starts.
- **Production incidents.** A hotfix routes to OpenSpec's `hotfix` track,
which defers spec review until after the fix ships and adds a mandatory
`learn` phase whose gate requires an `Incident Memory Proposal` section, so
the agent has to write up the incident before the feature can archive.
- **Compliance-sensitive paths.** An app's policy can pin a framework by path
glob (`**/payments/**` → BMAD) regardless of what `route_task` would
otherwise pick; overriding that pin needs an explicit
`policy_override_reason` recorded on `start_feature`.
- **Greenfield services.** A repository with no spec library and fewer than
20 commits routes to Spec Kit's `default` track — the full seven-phase
lifecycle from `specify` through `learn`.
- **Behaviour-preserving refactors.** OpenSpec and Spec Kit both ship a
`refactor` track that requires characterization tests before any change and
blocks the move out of `verify` if the evidence shows an existing test was
modified (`max_existing_tests_modified: 0`).
- **Requirement traceability.** The spec gate captures requirement ids
(`FR-001` in Spec Kit, `FR1` in BMAD, `R1` in the house flow, requirement
names in OpenSpec) and the gate out of `verify` checks them against
`evidence.implements`. `sdd-admin export rtm <app>`, the
`sdd://apps/{slug}/rtm` resource and the admin UI show, per app, which
requirement was covered by which evidence and who approved it.
- **Cross-app knowledge reuse.** `search_memory` with `scope: "company"` or a
list of app slugs answers "how do other apps handle X" without pulling in
every other app's private decisions by default.
## Status
v1 implemented per
[`docs/superpowers/specs/2026-09-10-sdd-orchestrator-design.md`](docs/superpowers/specs/2026-09-10-sdd-orchestrator-design.md)
and the plan in
[`docs/superpowers/plans/2026-09-10-sdd-orchestrator-v1.md`](docs/superpowers/plans/2026-09-10-sdd-orchestrator-v1.md).
Host verification status is tracked in
[`docs/verification/feature-matrix.md`](docs/verification/feature-matrix.md).
Deployment characteristics (pool sizing against embedding latency, long-lived
`GET /mcp` streams, migrations on CLI start-up) are in
[`docs/operations.md`](docs/operations.md); what a host may and may not assume
about a context pack is in
[`docs/verification/host-integration.md`](docs/verification/host-integration.md).
An interactive runtime architecture diagram (core components, primary path,
external dependencies, trust boundaries) is published at
[dbianco.github.io/sdd-orchestrator/architecture/sdd-orchestrator.architecture.html](https://dbianco.github.io/sdd-orchestrator/architecture/sdd-orchestrator.architecture.html).
## Development
```bash
npm install
npm run db:test:up # Postgres + pgvector on :55432
export SDD_TEST_DATABASE_URL=postgres://sdd:sdd@localhost:55432/sdd_test
npm test # unit, integration and contract tests
npm run typecheck
npm run dev:stdio # server over stdio against SDD_DATABASE_URL
npm run admin -- app list # sdd-admin without building
npm run admin -- eval packs/evals/seed.yaml # retrieval recall@8 and MRR on golden cases
node docs/verification/walkthrough.mjs --url http://localhost:8080 \
--host-token sdd_... --approver-token sdd_... --ci-token sdd_... # scripted host walkthrough
```
## Planned stack
| Component | Choice |
|---|---|
| Runtime | TypeScript on Node 22 |
| Transport | MCP over Streamable HTTP (shared) and stdio (local dev) |
| Storage | Postgres with pgvector, shipped via Docker Compose |
| Embeddings | Voyage AI by default, Ollama for development |
| Admin | `sdd-admin` CLI for ingesting packs, approving proposals, deprecating items |
## Usage examples
The interface below is the v1 contract.
### 1. Run the server
```bash
git clone https://github.com/dbianco/sdd-orchestrator
cd sdd-orchestrator
cp .env.example .env # set VOYAGE_API_KEY, or SDD_EMBEDDING_PROVIDER=ollama
docker compose up -d # Postgres + pgvector + sdd-orchestrator on :8080
curl http://localhost:8080/healthz
```
### 2. Seed the knowledge base (admin, once)
```bash
sdd-admin app register checkout --name "Checkout Service" --compliance
sdd-admin app update checkout --stack typescript,react,node --budget 6000
sdd-admin app set-policy checkout policy.json --reason "PCI scope: BMAD for payments paths"
sdd-admin ingest packs/openspec
sdd-admin ingest packs/spec-kit
sdd-admin ingest packs/bmad
sdd-admin ingest packs/quality-layer
sdd-admin ingest packs/stack-guides/react
sdd-admin ingest packs/company # always-on constitution
```
`policy.json`:
```json
{
"framework": null,
"path_rules": [{ "glob": "**/payments/**", "framework": "bmad" }],
"risk_paths": ["**/webhooks/**"]
}
```
### 3. Issue tokens and connect a host
For Claude Code, the `sdd` plugin (`hosts/claude-code/`) is the recommended
setup: it connects the server and adds hooks that block code edits until the
task is routed and its feature reaches `implement`.
```bash
export SDD_URL=http://sdd.internal:8080 SDD_TOKEN=sdd_...
claude plugin marketplace add dbianco/sdd-orchestrator
claude plugin install sdd@sdd-orchestrator
```
Without the plugin, configure the server by hand as below.
Each person and each pipeline gets its own token; the server records the
token's identity instead of trusting an `actor` field. `SDD_AUTH_MODE`
defaults to `warn` (calls without a token still work, with a warning); set it
to `enforce` once every host sends a token.
```bash
sdd-admin token create --for daniel --scope host,approver --name "daniel laptop"
sdd-admin token create --for checkout-ci --scope ci --app checkout --name "checkout pipeline"
```
Claude Code (`.mcp.json` in the workspace):
```json
{
"mcpServers": {
"sdd": { "type": "http", "url": "http://sdd.internal:8080/mcp", "headers": { "Authorization": "Bearer ${SDD_TOKEN}" } }
}
}
```
Cursor (`.cursor/mcp.json`):
```json
{
"mcpServers": {
"sdd": { "url": "http://sdd.internal:8080/mcp", "headers": { "Authorization": "Bearer ${env:SDD_TOKEN}" } }
}
}
```
Local development against a local database, over stdio:
```json
{
"mcpServers": {
"sdd": { "command": "sdd-orchestrator", "args": ["--stdio"] }
}
}
```
### 4. Route a task and start a feature
The developer types a task; the host agent calls the server.
```
You: Add CSV export to the orders page.
```
The agent calls `route_task`:
```json
{
"task_description": "Add CSV export to the orders page",
"app": "checkout",
"workspace": {
"stack": ["typescript", "react"],
"is_greenfield": false,
"has_spec_library": true,
"estimated_files": 4,
"paths_touched": ["src/orders/"],
"host": "claude-code"
}
}
```
and receives:
```json
{
"decision": {
"framework": "openspec",
"track": "default",
"confidence": "high",
"rule": "10-brownfield-small-medium",
"reasons": ["brownfield (asserted by host)", "size: medium (4 files, one top-level dir)"],
"high_risk": false,
"policy_version": 3,
"framework_pack_version": "1.0.0"
},
"attached_layers": [
{ "pack_name": "quality-layer", "pack_version": "1.0.0", "kind": "standard" },
{ "pack_name": "stack-guides/react", "pack_version": "1.0.0", "kind": "stack_guide" }
]
}
```
The agent shows the decision, the developer accepts, and the agent calls
`start_feature` with the same decision and `actor: "daniel"`. The result
carries a `feature_id` and the first context pack: header, always-on
constitution, the OpenSpec proposal template, retrieved app memory such as
`ADR-7 Exports go through the reporting service`, React guide sections, and
the stop conditions plus the checks the next gate will run.
`route_task` also returns a `routing_id`: the server records one routing
event per unit of work (deduplicated by `external_ref` or by task text), so
trivial fixes that never become features still show up in the admin. Pass
the `routing_id` to `start_feature`, and call `record_commit` after each
commit to link it to the work.
### 5. Advance through the gates
After the agent writes the proposal and the developer reviews it:
```json
{
"feature_id": "f_01j9…",
"actor": "daniel",
"expected_phase": "specify",
"target_phase": "implement",
"artifacts": { "proposal.md": "…", "specs": "…", "tasks.md": "…" }
}
```
Spec review is a person's decision, not the agent's: when every check
passes, the move comes back as `"result": "awaiting_approval"` with an
`approval_id`, and a reviewer approves or rejects it in the admin UI's
Approvals tab or with `sdd-admin approvals approve <id>`. Compliance apps and
high-risk features need a reviewer other than the requester. (With
`SDD_AUTH_MODE=off` the v1 behaviour applies: the host sends
`"human_approved": true`.)
A failing gate is a normal result, not an error:
```json
{
"result": "fail",
"findings": [
{ "check": "placeholder_scan", "severity": "blocker", "location": "proposal.md:41", "message": "marker TBD" },
{ "check": "measurable_criteria", "severity": "blocker", "location": "proposal.md:58", "message": "\"export must be fast\" has no threshold" }
]
}
```
The agent fixes the proposal and calls again. On pass it receives
`next_instructions` for `implement`. At the end of implementation the host
sends evidence with the move out of `verify`:
```json
{
"expected_phase": "verify",
"target_phase": "integrate",
"evidence": {
"tests": { "command": "npm test", "passed": 48, "failed": 0 },
"lint": "pass",
"security": { "status": "pass", "new_high": 0 },
"files_changed": ["src/orders/export.ts", "src/orders/export.test.ts"],
"implements": ["REQ-12"]
}
}
```
If tests keep failing, the agent moves back from `verify` to `implement` with
`cycle_failed: true`. The third such move blocks the feature until a human
intervenes.
### 6. Look things up across apps
```json
{ "query": "how do other apps handle CSV encoding", "app": "checkout", "scope": "company" }
```
or, for named apps:
```json
{ "query": "rate limiting decisions", "app": "checkout", "scope": ["billing", "reporting"] }
```
### 7. Grow and retire memory
During the `learn` phase the agent proposes an ADR:
```json
{
"feature_id": "f_01j9…",
"actor": "daniel",
"kind": "app_memory",
"memory_type": "adr",
"title": "ADR-9 CSV exports stream rather than buffer",
"body": "…",
"links": ["openspec/changes/archive/2026-09-10-orders-csv-export/"]
}
```
An admin reviews and retires knowledge:
```bash
sdd-admin proposals list
sdd-admin proposals approve p_42
sdd-admin deprecate checkout.adr.0003 --successor checkout.adr.0009 --reason "superseded by streaming"
sdd-admin deprecate-framework kiro --reason "no longer used"
sdd-admin reindex # after switching embedding model
sdd-admin export rtm checkout > rtm.csv # requirement traceability matrix
```
### 8. Report CI evidence
For compliance apps and high-risk features (and apps whose policy sets
`"evidence": "ci"`), the move out of `verify` takes tests, lint and security
results only from CI, and only for the feature's latest commit. The pipeline
posts them with `scripts/sdd-ci-evidence.mjs`, which finds the feature from
the `SDD-Ref` commit trailer; see `docs/ci/github-actions.md`.
### 9. Review approvals and browse metrics
The admin page at `/admin` shows feature counts, gate blocker counts by
check, a phase-to-phase flow heatmap, the memory-proposal queue, a **Work**
tab listing everything routed with linked commits, per-app traceability and
an **Approvals** tab. Log in with any username and, as the password, a
personal token with the `approver` scope (to approve or reject) or the
shared `SDD_ADMIN_TOKEN` (read-only). The page is absent (a plain 404) when
`SDD_AUTH_MODE=off` and `SDD_ADMIN_TOKEN` is unset.
```bash
docker compose up -d
open http://localhost:8080/admin # any username, password = your sdd_ token
```
## Repository layout
```
.claude-plugin/ marketplace manifest for the Claude Code plugin
.github/workflows/ CI: typecheck, unit, integration, contract, admin UI, plugin, eval, image
docs/ci/ reporting CI evidence from a pipeline
docs/operations.md deployment and operating notes
docs/superpowers/specs/ design specifications
docs/superpowers/plans/ implementation plans
docs/verification/ host integration guide, feature matrix, walkthroughs, scripted walkthrough, workspace-facts script
hosts/claude-code/ the sdd plugin for Claude Code: hooks, skill, commands
migrations/ node-pg-migrate schema
scripts/ sdd-ci-evidence.mjs for pipelines
packs/ seed knowledge packs (frameworks, quality layer, stack guides, company) and golden eval cases
src/ server, services, assembler, ingestion and CLI
test/ unit, integration and contract tests
```
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessUnresponsive