Spec-driven development framework for AI coding agents
Project description
scafld
An opinionated orchestration layer for AI coding agents.
Canonical repo: https://github.com/nilstate/scafld. Default branch: main.
Most AI coding tools let agents jump straight into your codebase and start writing. No plan. No review. No audit trail. Just vibes and a prayer.
The result is predictable: code that looks right, passes the tests, and slowly rots from the inside. Duplicated blocks. Architectural drift. Changes nobody asked for buried in changes somebody did. The agent ships fast and you spend the next week figuring out what it actually did.
scafld enforces a simple constraint: think before you type.
Every non-trivial task becomes a YAML specification before a single line of code changes. The spec defines what will change, in what order, with what acceptance criteria, and how to roll it back if it breaks. A human reviews and approves the spec. Only then does the agent execute - phase by phase, validated at every checkpoint, auditable after the fact.
This isn't a wrapper around a prompt. It's a development methodology - the same separation of planning from execution that every serious engineering discipline has always required, applied to the one context where people have decided to skip it entirely.
User Request
|
v
PLAN MODE AI explores codebase, generates spec
(read-only) .ai/specs/drafts/{task-id}.yaml
|
v
HARDEN Grounded interrogation of the draft
(optional) scafld harden <task-id>
|
v
Human Review Developer reviews and approves
|
v
EXEC MODE AI executes spec phase-by-phase
(autonomous) with validation at every checkpoint
|
v
REVIEW Adversarial self-review finds what
execution missed (ideally fresh session)
|
v
Archive Completed spec + audit trail
Why This Exists
We built scafld because every AI coding workflow we used was broken in the same way. The agent would receive a task, immediately start modifying files, and produce something that was technically functional but architecturally thoughtless. Ask it to add a feature and it might refactor three other things along the way. Ask it to fix a bug and it might introduce a dependency you didn't want. There was no contract between what was requested and what was delivered, and no way to verify the difference after the fact.
The spec is the contract. It forces the planning to happen explicitly, in a format that a human can review and a machine can validate. It creates an audit trail that answers "what changed, why, and did it match what was agreed." It makes AI-assisted development reproducible instead of hopeful.
Install
pip install scafld
npm install -g scafld
git clone https://github.com/nilstate/scafld.git ~/.scafld && ~/.scafld/install.sh
curl -fsSL https://raw.githubusercontent.com/nilstate/scafld/main/install.sh | sh
pip install scafld installs the console entry point plus the runtime bundle used by scafld init and scafld update.
npm install -g scafld installs the same CLI package for environments that distribute tooling through npm. The CLI still requires python3 at runtime because the executable itself is Python. Commands that edit YAML specs, such as scafld harden, also need PyYAML available in that Python runtime:
python3 -m pip install PyYAML
The git install clones scafld to ~/.scafld and symlinks the scafld command to ~/.local/bin/.
To update the installed checkout: scafld update --self
To refresh the managed bundle in the current workspace: scafld update
To refresh every scafld workspace under a development tree: scafld update --scan-root ~/dev
Release
The canonical release version lives in scafld/_version.py. Everything else derives from that.
python3 scripts/bump_version.py X.Y.Z
python3 scripts/sync_version.py --check
git commit -am "Release vX.Y.Z"
git tag vX.Y.Z
git push origin main vX.Y.Z
Pushing the tag triggers the release workflow, which validates the tree, builds both packages, publishes to PyPI and npm, and creates the GitHub release.
Setup
cd your-project
scafld init
This scaffolds the full structure into your project:
your-project/
.ai/
scafld/ # Managed runtime bundle refreshed by `scafld update`
config.yaml # Validation rules, rubric, safety controls
config.local.yaml # Your overrides (build/test/lint commands)
prompts/ # Plan + exec mode instructions
schemas/ # Spec validation schema
specs/
drafts/ # Planning in progress
approved/ # Ready for execution
active/ # Currently executing
archive/ # Completed work
logs/ # Execution logs (gitignored)
AGENTS.md # Your project's invariants and policies
CLAUDE.md # Project overview, essential commands
CONVENTIONS.md # Tech stack, patterns, coding standards
Make It Yours
- AGENTS.md - Your architectural invariants, domain rules, forbidden actions
- CONVENTIONS.md - Your tech stack, naming conventions, testing patterns
- CLAUDE.md - Project overview, essential commands, agent-specific tips
.ai/config.local.yaml- Your build/test/lint commands (merges on top of config.yaml)
Project Structure
scafld is opinionated about how your project should be organised, because the structure is what gives the AI agent visibility over your entire codebase.
Single Repo
For a single codebase, just run scafld init at the root. The agent sees everything.
Multi-Repo Workspace
For projects with multiple codebases - an API, a frontend, an SDK, an MCP server - the workspace pattern gives the agent visibility across all of them from a single root.
Create a root repo that acts as the orchestration layer. Add your codebases as git submodules underneath. Run scafld init at the root. Now the agent can see your specs, your conventions, your architectural invariants, AND all your code - in one context.
mkdir my-project && cd my-project
git init
git submodule add git@github.com:org/api.git api
git submodule add git@github.com:org/app.git app
git submodule add git@github.com:org/sdk.git sdk
scafld init
my-project/ # Root workspace repo
.ai/ # scafld config and specs
AGENTS.md # Cross-project invariants
CLAUDE.md # Agent overview of the whole system
CONVENTIONS.md # Shared coding standards
api/ # Submodule: your API
app/ # Submodule: your frontend
sdk/ # Submodule: your SDK
The root repo is lightweight - it holds the orchestration layer (scafld files, agent docs) and pointers to the real code. Each submodule is still its own repo with its own history. But the agent sees the whole picture from the root, which means it can plan changes that span multiple codebases and understand how they connect.
This is how we work. It's not the only way, but if you're running AI agents across multiple repos without a unified root, you're asking the agent to plan with half the context.
CLI
scafld new <task-id> [-t title] [-s size] [-r risk] # Scaffold a new spec
scafld list [filter] # List all specs
scafld status <task-id> [--json] # Show spec details
scafld validate <task-id> [--json] # Validate against schema
scafld harden <task-id> [--mark-passed] # Optional: interrogate draft with grounded questions
scafld approve <task-id> # Validate + move to approved
scafld start <task-id> # Move to active
scafld exec <task-id> [-p phase] [-r] # Run acceptance criteria (-r = resume)
scafld audit <task-id> [-b base-ref] # Spec vs actual git diff
scafld diff <task-id> # Git history for a spec
scafld review <task-id> [--json] # Run configured automated passes + scaffold Review Artifact v3
scafld complete <task-id> [--json] # Read review, record verdict, archive (requires passing review)
scafld complete <task-id> --human-reviewed --reason "manual audit"
# Exceptional audited override when the gate is blocked
scafld fail <task-id> # Archive as failed
scafld cancel <task-id> # Archive as cancelled
scafld report # Aggregate stats
scafld update [--scan-root PATH] [--self] # Refresh the managed framework bundle
Managed Bundle
Each workspace now carries a framework-managed runtime bundle under .ai/scafld/.
.ai/scafld/config.yamlprovides the current scafld defaults.ai/config.yamlremains the repo's project-level config/overlay.ai/config.local.yamlremains the local machine override layer.ai/scafld/manifest.jsonrecords the scafld version, source commit, and bundle file hashes
scafld update refreshes .ai/scafld/ without overwriting repo-owned docs or project-specific config.
Per-Criterion Working Directory
In monorepo/workspace setups, different acceptance criteria may target different submodules. Use the optional cwd field to set the working directory for a command, relative to the workspace root:
acceptance_criteria:
- id: ac1
type: test
cwd: api
command: "bundle exec rspec spec/services/"
expected: "0 failures"
- id: ac2
type: test
cwd: app
command: "yarn test"
expected: "0 failures"
Commands without cwd run from the workspace root. The path must be relative and must resolve within the workspace — paths that escape the root are rejected.
You can also set a spec-level default under task.context.cwd so you don't repeat it on every criterion:
task:
context:
cwd: api
packages:
- app/services
Individual criteria can still override with their own cwd.
Per-Criterion Timeout
Acceptance criteria default to a 600 second timeout. Long-running checks can override that with timeout_seconds:
acceptance_criteria:
- id: ac3
type: test
cwd: api
command: "bundle exec rspec"
expected: "0 failures"
timeout_seconds: 900
Use specific expectations like 0 failures or exit code 0 when possible. Generic phrases like All pass are accepted, but the explicit forms are easier for scafld to verify and for humans to audit.
Usage
Tell your AI agent: "Let's plan [feature]. Create a task spec."
The agent enters read-only planning mode, explores your codebase, and produces a YAML spec with objectives, phases, acceptance criteria, and rollback commands. You review it, approve it, and the agent executes autonomously within those bounds.
What It Actually Does
- Spec-driven - Every task is a versioned, schema-validated YAML artifact. Not a prompt. Not a conversation. A machine-readable contract.
- Harden (optional) - Interrogate a draft against grounded questions before approval; every question and recommended answer must cite a spec gap, a verified code location, or an archived precedent. Refuses to invent citations or rehash settled fields.
- Approval gate - No code changes until a human reviews the plan. The agent thinks; you decide.
- Phase-by-phase execution - Acceptance criteria at every checkpoint, not just at the end.
- Scope audit -
scafld auditcompares what the spec declared against what actually changed in git. Undeclared changes get flagged. - Adversarial review - Before archiving,
scafld reviewruns the configuredspec_complianceandscope_driftpasses, scaffolds Review Artifact v3, and prepares the adversarialregression_hunt,convention_check, anddark_patternssections.scafld completerequires a structurally valid latest review or an exceptional human-reviewed override with an audited reason. - Self-evaluation - Agents score their own work against a configurable rubric. Below 7/10 triggers a second pass.
- Rollback commands - Per-phase rollback for safe failure recovery. Every phase declares how to undo itself.
- Resume protocol - Interrupted executions pick up where they left off.
- Validation profiles - Light, standard, or strict, configured per-task or derived from risk level.
- Reporting -
scafld reportaggregates pass rates, self-eval scores, and scope drift across your entire spec history. - Agent-agnostic - Works with Claude, Cursor, Copilot, Windsurf, or any AI coding agent.
Review Pipeline
The default review model is a five-pass pipeline declared in .ai/config.yaml:
spec_compliancescope_driftregression_huntconvention_checkdark_patterns
Pass ordering is explicit through per-pass order fields, so the review pipeline does not depend on YAML mapping order. scafld review scaffolds Review Artifact v3 with per-pass pass_results and round_status: "in_progress". The reviewer fills the configured adversarial sections, updates the metadata to round_status: "completed", and sets final pass results before scafld complete archives the spec.
Trust Boundary
scafld now enforces a materially stronger local review workflow, but local CLI checks are still not the whole trust boundary.
For best-in-class review governance, add the next layer outside the agent session:
- CI or merge gate validates the latest review artifact before code lands
- Diff or commit binding ties the review artifact to the exact reviewed diff or commit
- External reviewer driver runs the adversarial review from a configurable tool or service instead of trusting the executor path alone
- Out-of-band approval moves human override out of the terminal session and into a separate approval surface
Documentation
| File | Audience | Purpose |
|---|---|---|
| AGENTS.md | AI agents | Invariants, modes, validation, conventions |
| CLAUDE.md | Claude Code | Claude-specific tool tips |
| CONVENTIONS.md | AI agents | Coding standards template |
| .ai/config.yaml | Both | All configuration in one place |
| .ai/OPERATORS.md | Developers | Human cheat sheet for working with specs |
License
MIT
Contributing
Contributions welcome. Follow the spec-driven approach - practice what we preach.
Built by Sourcey. We build AI infrastructure that works in production, not in pitch decks.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file scafld-1.4.6.tar.gz.
File metadata
- Download URL: scafld-1.4.6.tar.gz
- Upload date:
- Size: 76.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cd5057a3ef0388ba7815292f314de2bd11547afce565befd335366a1ad6bb402
|
|
| MD5 |
43d22cdd0d64133539ca12aa1520c3ca
|
|
| BLAKE2b-256 |
bec19eed6b282cc4dc3bf2ddc5dee06132c1d5dad828485ac19776084925b846
|
File details
Details for the file scafld-1.4.6-py3-none-any.whl.
File metadata
- Download URL: scafld-1.4.6-py3-none-any.whl
- Upload date:
- Size: 86.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
61725e845318f45c1b966b7fde7e778bb8c5a645b9567b8eb4f4a05143c3903f
|
|
| MD5 |
cf6815d73eaca8bdc4070ac308339d13
|
|
| BLAKE2b-256 |
0d0916d3aa6c8bfa7baa1ec4e491fa88a606a6921c49e953e6bcf4c76e0ab402
|