Spec-driven development means you and an AI coding agent write down what to build before any code exists: a requirements file with testable acceptance criteria, a design file, and a task list the agent then works through. Unlike direct prompting, every step leaves a reviewable file. Put security requirements, allowed dependencies and test tasks in those files, not in chat.
Moving intent out of a chat window and into files next to the code also creates a new thing to secure: a spec is an input the agent trusts and acts on, just like a prompt. This guide covers how the main workflows structure specs, how EARS notation makes acceptance criteria testable, where security requirements belong, how agents should pick dependencies, and what can go wrong. It ends with a worked example: an avatar upload spec with OWASP ASVS references, an approved dependency list, and a CI job that runs tests and a scan.
Key takeaways
- A spec-driven workflow splits work into three kinds of file: requirements (what and why), design (how), and tasks (the ordered steps). The agent writes drafts of each; a human reviews each before the next phase starts.
- GitHub's open source Spec Kit produces
spec.md,plan.mdandtasks.mdper feature through/speckit.*commands. Kiro producesrequirements.md,design.mdandtasks.md. Claude Code's plan mode is a lighter version: the agent proposes a plan and makes no edits until you approve it. - EARS (Easy Approach to Requirements Syntax), published by Alistair Mavin and colleagues from Rolls-Royce at the IEEE Requirements Engineering conference in 2009, gives each requirement a fixed shape such as "When [trigger], the [system] shall [response]". That shape maps cleanly onto a test case.
- Security requirements belong in every layer: project principles (constitution or steering files), requirements with ASVS identifiers, threat notes in the design, and explicit tasks for tests, dependency review and the SBOM update.
- Treat the dependency list as part of the spec. Name approved packages with pinned versions and licences, make "update the SBOM" a task, and have a human approve any new package before the agent installs it.
- The main risks are conceptual and manageable: a spec can carry injected instructions, specs drift from code, and agents can mark tasks done without a passing test. Each has a concrete control, covered below.
- Map the workflow to NIST SSDF practices (PO.1, PW.1, PW.4, PW.7, PW.8, PS.3) with evidence you can point to, not just a table in a policy document.
What is spec-driven development with coding agents?
Spec-driven development (SDD) is a way of working with an AI coding agent where the first output is not code but a set of structured documents. You describe a feature in plain language. The agent turns that into a requirements document, you correct it, the agent proposes a technical design, you correct that, and only then does the agent break the design into tasks and start writing code against them.
GitHub's Spec Kit, an open source toolkit, describes the process as defining "what to build and why", then turning the specification into a plan, tasks and working code. Its methodology document goes further and argues that the specification, not the code, should be the primary artifact that teams maintain. Kiro's documentation describes specs as "structured artifacts that formalize the development process", with three files and a three-phase workflow. The vocabulary differs between tools, but the shape is the same: requirements, then design, then tasks, then implementation, with review gates in between.
How is it different from prompting the agent directly?
With direct prompting, the specification exists only in your head and the chat transcript. The agent infers what you meant and starts editing. If it guesses wrong about authentication, error handling or which library to use, you find out by reading the diff, and the reasoning is gone when the session ends.
Spec-driven development changes four things:
- Intent is written down before code, in a form a reviewer can read in five minutes.
- Ambiguity is forced into the open. Spec Kit's templates tell the agent to mark unknowns with
[NEEDS CLARIFICATION: ...]rather than guess; its methodology document's example is a login system where the agent must ask which authentication method to use. - Each phase has a review gate. You approve requirements before design and design before tasks.
- The files persist. Kiro's best-practices page says specs are "designed to be version-controlled", so the next person or agent session starts from the same written intent.
The cost is time up front. The benefit is that review happens on a page of requirements instead of a thousand-line diff, and security requirements have an obvious home.
What goes in the requirements, design and tasks files?
The three files answer different questions and should be reviewed by different people where you can.
| File | Question it answers | Typical contents | Who should review it | Security content that belongs here |
|---|---|---|---|---|
Requirements (requirements.md in Kiro, spec.md in Spec Kit) | What must the feature do, and why? | User stories, acceptance criteria, out of scope items, open questions | Product owner, security champion | ASVS requirement references, abuse cases written as "If ... then ... shall" criteria, data classification, compliance constraints |
Design (design.md in Kiro, plan.md plus research.md, data-model.md and contracts/ in Spec Kit) | How will it be built? | Architecture, data model, interfaces, sequence of calls, error handling, test strategy | Tech lead, security reviewer | Trust boundaries, threat model notes, chosen libraries with pinned versions and licences, where validation and authorisation happen |
Tasks (tasks.md in both) | In what order, in what steps? | Numbered, dependency-ordered tasks, each traceable to a requirement | Tech lead | Tasks for security tests, the dependency review, the SBOM update and the CI scan, each with a done condition |
Project principles (Spec Kit constitution, Kiro steering files, AGENTS.md, CLAUDE.md) | What rules apply to every feature? | Coding standards, testing rules, stack choices | Engineering leadership, security | Default security rules, the approved registry, the "no new dependency without approval" rule |
The fourth row matters as much as the first three. Feature specs inherit from project principles, and that is where rules you do not want to repeat in every feature should live.
How do open source and vendor tools structure specs?
The descriptions below come from each tool's own documentation as fetched in October 2026. These tools change quickly, so check the current docs before you rely on a specific file name or command.
Spec Kit (open source)
Spec Kit is an open source toolkit from GitHub that adds structured processes and templates to an existing coding agent. Its README says it needs Python 3.11 or later, the uv tool, and a supported coding agent on Linux, macOS or Windows. Installation and project setup look like this:
uv tool install specify-cli
specify init my-project --integration copilot
cd my-project
The --integration value selects which agent the commands are installed for. After that, you run Spec Kit's commands inside the agent's chat, not in the terminal. The command reference lists the core order as:
/speckit.constitution -> /speckit.specify -> /speckit.clarify -> /speckit.plan
-> /speckit.checklist -> /speckit.tasks -> /speckit.analyze -> /speckit.implement
-> /speckit.converge
The documentation notes that invocation syntax varies by agent: some agents in skills mode use /speckit-specify, and some use $speckit-specify. What each command does, according to the reference:
/speckit.constitutionsets project principles that every later phase is evaluated against. The methodology document describes the constitution (memory/constitution.md) as nine articles, including a test-first article, and leaves Articles IV to VI for each project to define, with security and access boundaries given as one example./speckit.specifywrites the feature specification, focused on what and why, not the tech stack./speckit.clarifyasks up to five targeted questions about underspecified areas and writes the answers intospec.md./speckit.plangenerates design artifacts such asplan.md,research.md,data-model.mdandcontracts/under aspecs/[branch-name]/folder. This is where you name the stack./speckit.checklistgenerates what the docs call "unit tests for your requirements". The reference says custom checklists are reviewer-owned and implementation must not self-approve them./speckit.taskswrites a dependency-orderedtasks.mdin phases: Setup, Foundational, one per user story, then Polish./speckit.analyzeis a read-only consistency check acrossspec.md,plan.mdandtasks.md, reporting gaps such as a task with no matching requirement./speckit.implementexecutes the tasks, and asks before proceeding if any checklist item is unchecked./speckit.convergecompares the implementation against the artifacts. Its only possible write is appending tasks totasks.md; you repeat implement and converge until it reports converged.
Two parts matter for security teams. Presets can enforce organisational or regulatory standards, such as traceability in plans. And the README warns that community catalog listings "are not audits or endorsements", so review extension source code before installing it, as you would any dependency.
Kiro specs
Kiro's documentation says every spec generates requirements.md (or bugfix.md for bug fixes), design.md and tasks.md, stored under .kiro/specs/. Feature specs come in a Requirements-First variant and a Design-First variant; the docs recommend Design-First when the system must meet strict non-functional requirements such as latency, throughput or compliance. A Quick Spec mode generates all three files without approval gates between phases.
Kiro writes requirements in EARS notation, WHEN [condition/event] THE SYSTEM SHALL [expected behavior], and an Analyze Requirements option checks them for inconsistencies, ambiguities, conflicting constraints and gaps before design. When you run all tasks, Kiro builds a dependency graph from tasks.md and runs independent tasks concurrently in waves. A Sync Files action creates new tasks when requirements change. Bugfix specs record unchanged behaviour as WHEN [condition] THEN the system SHALL CONTINUE TO [existing behavior], and Kiro generates property-based tests for both the fix and the preserved behaviour.
Project-wide guidance lives in steering files under .kiro/steering/ or ~/.kiro/steering/. The docs suggest a security-policies.md alongside the product.md, tech.md and structure.md foundation files, say Kiro also reads AGENTS.md files, and warn: "Never include API keys, passwords, or sensitive data."
Plan mode in Claude Code
Claude Code does not ship a three-file spec format, but its plan mode gives you the core idea: a written plan and a gate before edits. The documentation says plan mode "tells Claude to research and propose changes without making them". Claude reads files and runs shell commands to explore, and edits stay blocked until you approve the plan (the docs note an exception for interactive sessions where bypass permissions are available). Enter it with Shift+Tab, by prefixing a prompt with /plan, or at startup:
claude --permission-mode plan
You can approve the plan and choose how edits proceed, or keep planning; Ctrl+G opens the plan in your text editor first. Setting defaultMode to plan in .claude/settings.json makes it the default for a project's terminal sessions.
Plan mode suits changes too small for a full spec, but it does not produce a persistent requirements file on its own. Ask Claude to save the approved plan in the repository if you want one. Claude Code's memory docs say CLAUDE.md can import files with @path/to/import syntax, and that Claude can read a repository's AGENTS.md in place of CLAUDE.md.
AGENTS.md and project instruction files
AGENTS.md is an open format described at agents.md as "a README for agents": a predictable place for build steps, test commands and conventions. It is not a spec format, but it is where project-wide rules that every spec inherits can live, and many agents read it.
OpenAI's Codex documentation explains how Codex discovers these files. It reads a global file from the Codex home directory, then walks from the project root down to the current directory, taking at most one file per directory and checking AGENTS.override.md before AGENTS.md. Files closer to the working directory appear later in the combined prompt, so they take precedence. Codex stops adding files once the combined size reaches project_doc_max_bytes, which the docs give as 32 KiB by default. That limit is worth remembering: a long instruction file can push a security rule past the cut-off. Our earlier post on how Codex discovers AGENTS.md instructions covers the discovery rules in detail, and Agent Skills explained covers the skill format that some spec toolkits now install.
Side by side
| Aspect | Spec Kit | Kiro specs | Claude Code plan mode | AGENTS.md alone |
|---|---|---|---|---|
| Requirements artifact | spec.md | requirements.md (EARS) | Plan text in session; save to a file yourself | None |
| Design artifact | plan.md, research.md, data-model.md, contracts/ | design.md | Part of the plan | None |
| Task artifact | tasks.md (phased, dependency-ordered) | tasks.md (run in waves) | Plan steps | None |
| Project-wide rules | Constitution, presets | Steering files, AGENTS.md | CLAUDE.md, imports, AGENTS.md | AGENTS.md |
| Review gates | Optional clarify, checklist and analyze gates; implement asks if checklists are unchecked | Approval between phases; Quick Spec skips them | Approve the plan before edits | None built in |
| Consistency check | /speckit.analyze (read-only), /speckit.converge (append-only) | Analyze Requirements, Sync Files | Your review | None |
| Where it lives | specs/ folder in the repo | .kiro/specs/ in the repo | Session, unless saved | Repo root and subfolders |
How does EARS make acceptance criteria testable?
Most requirement problems are language problems. "The upload should be secure" cannot be tested. "Files must be small" cannot be tested either, because nobody wrote down how small. EARS fixes this by constraining the sentence shape.
Where EARS comes from
EARS stands for Easy Approach to Requirements Syntax. Alistair Mavin's EARS page says he and colleagues at Rolls-Royce developed it while analysing airworthiness regulations for a jet engine's control system, and that the notation was first published in 2009. The paper is "Easy Approach to Requirements Syntax (EARS)" by Alistair Mavin, Philip Wilkinson, Adrian Harwood and Mark Novak, in the proceedings of the 17th IEEE International Requirements Engineering Conference (RE 2009), pages 317 to 322 (DOI 10.1109/RE.2009.9).
The generic EARS structure, from the EARS page, is a fixed clause order: optional preconditions, an optional trigger, one system name and one or more responses. The ruleset says a requirement has zero or many preconditions, zero or one trigger, one system name and one or many system responses.
The five patterns
| Pattern | Keyword | Template | Security example |
|---|---|---|---|
| Ubiquitous | none | The [system] shall [response] | The upload service shall store files under server-generated names. |
| State driven | While | While [precondition], the [system] shall [response] | While an account is suspended, the upload service shall reject all upload requests with HTTP 403. |
| Event driven | When | When [trigger], the [system] shall [response] | When a user uploads an avatar, the upload service shall verify that the file content matches the declared image type. |
| Optional feature | Where | Where [feature is included], the [system] shall [response] | Where image resizing is enabled, the upload service shall reject images wider or taller than 4,096 pixels before decoding. |
| Unwanted behaviour | If, then | If [trigger], then the [system] shall [response] | If an upload exceeds 2 MB, then the upload service shall reject it with HTTP 413 and write a security log entry. |
Complex requirements combine keywords, for example "While the user is authenticated, when the user uploads an avatar, the service shall ...". Kiro's documented form, WHEN ... THE SYSTEM SHALL ..., is the event-driven pattern.
Why the shape makes tests easy
Each EARS clause maps to one part of a test:
- The precondition (While) becomes the test's setup or fixture.
- The trigger (When, or If) becomes the action under test.
- The response (shall) becomes the assertion.
For the oversized upload example, the test almost writes itself: send a payload one byte over 2 MB as an authenticated user, assert HTTP 413, assert nothing reached storage, assert one security log entry. If a requirement cannot become a setup, an action and an assertion, it is not finished.
Security teams get two extra benefits. The unwanted behaviour pattern is a natural home for abuse cases, so a threat model turns into acceptance criteria. And the fixed shape makes gaps visible: a success requirement with no matching "If ... then" rows for oversized, wrong-type or unauthorised input stands out. EARS does not make a requirement correct, since a precise requirement can still name the wrong limit, but it makes it checkable.
Where do security requirements belong in a spec?
Short answer: at every layer, each with a different job. Putting everything into one "Security" heading at the bottom of the requirements file is the most common way security gets skimmed.
Project principles: the rules every feature inherits
The constitution, steering files or AGENTS.md should carry rules you never want to restate: allowed registries, no new dependency without human approval, one test per requirement, no secrets in code or specs, the target ASVS level and allowed licences. Step 1 below has a copyable version.
The OpenSSF Security-Focused Guide for AI Code Assistant Instructions, published on 1 August 2025, recommends keeping such instructions "concise, specific, and actionable", and warns that irrelevant rules can lead the assistant to add code for attacks that cannot happen. Codex's 32 KiB default cap and Claude Code's advice to keep each CLAUDE.md under 200 lines are further reasons to stay short.
Requirements: reference ASVS by versioned identifier
OWASP's Application Security Verification Standard (ASVS) is a catalogue of testable security requirements for web applications and services. The current stable version is 5.0.0, which the project page says was published on 30 May 2025. ASVS 5.0 is organised into 17 chapters, from V1 Encoding and Sanitization to V17 WebRTC, and each requirement is tagged with a level from 1 to 3.
The ASVS project asks that references include the version, in the form v5.0.0-5.2.1, because identifiers can change between versions. Do that in your specs. "Complies with ASVS 5.2.1" means nothing in two years; "v5.0.0-5.2.1" stays precise.
For each feature, list the ASVS requirements that apply and turn each into one or more EARS acceptance criteria. ASVS requirements say what to verify; EARS criteria say exactly how this feature behaves. For an avatar upload, the relevant ASVS 5.0.0 requirements include:
| ASVS 5.0.0 ID | Level | What it asks you to verify (paraphrased) |
|---|---|---|
| v5.0.0-5.1.1 | 1 | Documentation defines permitted file types, extensions and maximum size for each upload feature |
| v5.0.0-5.2.1 | 1 | The application only accepts files of a size it can process without loss of performance |
| v5.0.0-5.2.2 | 1 | The file extension matches an expected extension and the content matches the type it claims |
| v5.0.0-5.2.6 | 3 | Images with a pixel size larger than the maximum are rejected |
| v5.0.0-5.3.1 | 1 | Uploaded files stored in a public folder are not executed as server-side code |
| v5.0.0-5.3.2 | 1 | File paths are built from internally generated or trusted data, not user-submitted filenames |
| v5.0.0-8.2.2 | 1 | Data-specific access is restricted to consumers with explicit permission (to prevent IDOR and BOLA) |
| v5.0.0-16.3.2 | 2 | Failed authorisation attempts are logged |
| v5.0.0-15.1.2 | 2 | An inventory such as an SBOM is maintained for all third-party libraries |
| v5.0.0-15.2.1 | 1 | Components have not breached the documented update and remediation time frames |
Note the first row. ASVS v5.0.0-5.1.1 is a documentation requirement: the spec itself is where file types and size limits must be defined. In a spec-driven workflow, the requirements file can be the evidence for that control.
Design: threat model notes next to the architecture
The design file is where trust boundaries become visible: browser to API, API to storage, API to an image library. Beside each boundary, note what crosses it, what could go wrong (conceptually, such as "a user requests another user's avatar by ID") and which requirement covers it. If a threat has no requirement, the spec is incomplete. NIST SSDF practice PW.1.1 calls for risk modelling such as threat modelling during design; a threat notes table in design.md is a light way to show it.
Data classification
State what data the feature touches and how your policy classifies it. An avatar is usually personal data. Classification drives retention, what may be logged, and whether data can go to an external service. Agents cannot infer it from code, so write it down.
Allowed dependencies and licences
The design file names every third-party package the feature may use, with exact version and licence, and says any other package needs approval. The next section covers how.
Mapping the workflow to NIST SSDF
NIST SP 800-218, the Secure Software Development Framework (SSDF) version 1.1, was published in February 2022. A draft version 1.2 (SP 800-218 Rev. 1) was published for comment on 17 December 2025; check its status on the CSRC page before citing it. SP 800-218A (July 2024) adds an SSDF Community Profile for generative AI. Version 1.1 maps onto a spec-driven workflow like this:
| SSDF 1.1 practice | What it asks for | Where it shows up in a spec-driven workflow | Evidence you can point to |
|---|---|---|---|
| PO.1 Define security requirements | Identify and document security requirements for software and infrastructure | Constitution or steering rules; ASVS references and EARS criteria in requirements | The committed requirements file and its review history |
| PW.1 Design software to meet security requirements | Use risk modelling; track security requirements and risks | Threat notes and trust boundaries in the design file | The design file with requirement IDs beside each threat |
| PW.4 Reuse existing, well-secured software | Acquire well-secured components; verify them, including checking for known vulnerabilities | Approved dependency list with pinned versions; dependency review task | The approved list, the review approval, the scan results |
| PW.7 Review and analyse code | Review code for vulnerabilities | Human pull request review plus automated analysis tasks | Pull request reviews; scan output attached to the build |
| PW.8 Test executable code | Test to find vulnerabilities and verify compliance with requirements | One test per acceptance criterion, run in CI | CI test reports linked to requirement IDs |
| PS.3 Archive and protect each release | Collect and share provenance data, such as an SBOM (PS.3.2) | "Update the SBOM" as a task; SBOM stored with the release | The SBOM file for each release |
How should agents choose dependencies under a spec?
Dependency choice is the decision an agent makes most often without being asked. Under a spec, the agent proposes and a human approves.
Start from an approved list
The design file, or an approved-dependencies.txt beside it, lists each package the feature may use with an exact version, its licence and a one-line reason. The OpenSSF guide suggests telling assistants to prefer well-known, community-trusted libraries and the official package manager. It also cites a study that found 19.7% of packages proposed by code-generating models did not exist, names attackers can try to register. A short list of named, pre-approved packages means the agent does not need to remember a package name at all.
Pin versions and use frozen installs
The build should install exactly what the lockfile says. npm's documentation says npm ci requires an existing lockfile, exits with an error if it disagrees with package.json, and never writes to either. For Python, pip's hash-checking mode (--require-hashes) is all or nothing: once one requirement has a hash, every requirement needs one. Either way, a CI run cannot quietly change a resolved version.
Make the SBOM update a task
Every feature that changes dependencies gets a task to regenerate the SBOM. ASVS v5.0.0-15.1.2 asks for an inventory such as an SBOM, and SSDF PS.3.2 asks for provenance data such as an SBOM to be collected and shared. CISA, NSA, the FBI and international partners published the 2026 Minimum Elements for a Software Bill of Materials on 29 July 2026, replacing the 2021 NTIA minimum elements. An npm project can generate a CycloneDX SBOM itself:
npm sbom --sbom-format cyclonedx > sbom.cdx.json
Human review of the dependency diff
In every pull request an agent opens, check three things:
- The manifest diff. A direct dependency not on the approved list is a stop: update the list through review or remove the dependency.
- The lockfile diff. A large, unexplained lockfile change on a feature that added nothing needs an explanation.
- The registry. Resolved URLs should point at the registry your principles allow. ASVS v5.0.0-15.2.4 asks you to verify components come from the expected repository, with no risk of dependency confusion.
A CI check can enforce the first point; Step 7 below includes one.
What are the risks of spec-driven development?
Spec-driven development reduces some risks of agent coding and introduces others. These are concept-level risks with concept-level controls; none needs exotic tooling.
Specs as prompt injection carriers
A spec is text an agent reads and acts on, which makes it a prompt. The OWASP Top 10 for LLM Applications describes indirect prompt injection as an LLM accepting input from external sources, such as websites or files, that alters its behaviour in unintended ways. A spec pasted from an issue tracker, imported through an MCP connection, or contributed from outside the team can carry instructions nobody meant the agent to follow. The same applies to constitution files, steering files, AGENTS.md and community presets.
Controls: require code owner review on specs/, .kiro/ and instruction files, the same as on source. Read imported requirements before an agent acts on them. Review community extensions before installing them. Keep the agent's permissions tight while it implements; Claude Code's security documentation recommends not piping untrusted content directly into the agent, and an agent that must ask before running commands gives you a chance to notice when a spec has steered it somewhere odd.
Specs drifting from code
A spec that no longer matches the code is worse than none, because people and agents trust it. Drift happens when someone fixes a bug directly in code, when an agent changes behaviour without updating the spec, or when requirements change and nobody regenerates tasks. Make "update the spec" part of done for any behaviour change, and use the workflow's consistency tools, such as /speckit.analyze, /speckit.converge or Kiro's Sync Files.
Agents marking tasks done without tests
A ticked box in tasks.md is an assertion, not proof. Agents can write a test that passes trivially, skip a failing one, or tick a task after a partial change. Define done as "the linked test passes in CI", and review tests against the acceptance criteria. We cover this in can you trust tests an AI coding agent wrote.
Skipping the gates
Kiro's Quick Spec skips approval gates, Spec Kit's clarify, checklist and analyze steps are optional, and approving a plan can switch straight into a mode with no further prompts. That is fine for low-risk work. For authentication, file handling, payments or personal data, skipping the gates removes the review that made the approach worth using.
Too much text, too little signal
Long instruction files cost context and can be truncated: Codex stops adding files at its byte limit, and Claude Code's docs note that imports do not reduce context cost. A security rule on line 600 may never reach the model. Put the few rules that matter at the top.
For a broader view of agent risks beyond specs, see AI coding agent security risks and controls.
Direct prompting, plan mode, spec-driven or test-driven: which fits?
| Approach | What gets reviewed | Reviewability | Up-front cost | Drift risk | Security fit | Best for |
|---|---|---|---|---|---|---|
| Direct prompting | The final diff | Low: intent lives in chat and is lost after the session | Lowest | High: no written intent to drift from, so nothing catches it | Weak: security depends on what the prompt remembered to say | Throwaway scripts, small fixes, exploration |
| Plan mode | A written plan, then the diff | Medium: plan is readable but usually not kept | Low | Medium unless the plan is saved to the repo | Fair: you can check the plan for risky steps before edits | Small to medium changes in a known codebase |
| Spec-driven workflow | Requirements, design, tasks, then the diff | High: each phase is a file with review history | Higher: more text to write and review | Low if spec updates are part of done; high if not | Strong: security requirements, threat notes and dependency lists have a home | New features, anything touching auth, data, files or new dependencies |
| Test-driven workflow | Tests first, then the code that makes them pass | High for behaviour, lower for design intent | Medium | Low for tested behaviour; untested behaviour can still drift | Strong for behaviour you thought to test; weak for requirements nobody wrote down | Logic with clear inputs and outputs; bug fixes |
These are not exclusive. For security-sensitive work, the strongest combination is a spec-driven workflow whose tasks are written test-first: each EARS criterion becomes a failing test before the agent writes code. Spec Kit's methodology document includes a test-first article for this reason.
Step by step: a short spec with security acceptance criteria
The example below builds an avatar upload feature in a Node.js service. It works with any of the tools above. File names follow a plain specs/ layout; adapt them to Spec Kit's or Kiro's folders if you use one. Package names in the dependency list are placeholders: replace them with the packages your team actually approves.
Step 1: put project-wide security rules in the instruction file
Add a short section to AGENTS.md (or your constitution or steering file) so every feature inherits it:
## Security rules for all features
- Target OWASP ASVS 5.0.0 Level 2. Reference requirements as v5.0.0-<chapter>.<section>.<req>.
- Every acceptance criterion needs at least one automated test. A task is done only when its test passes in CI.
- Do not add, remove or upgrade a dependency unless it is listed in the feature's approved-dependencies.txt.
If you need a package that is not listed, stop and ask.
- Install only from the project's configured npm registry. Use npm ci in CI.
- Never put secrets, tokens or real personal data in code, specs, tests or fixtures.
- When behaviour changes, update the spec in the same pull request.
Keep it short. This file is loaded into every session.
Step 2: write the requirements with EARS and ASVS references
Create specs/004-avatar-upload/requirements.md:
# Feature 004: Avatar upload
## User story
As a signed-in user, I want to upload a profile picture so other team members recognise me.
## Data classification
Avatar images are personal data (internal: confidential). Retain until the user deletes
the account or replaces the avatar. Do not send images to any external service.
## Acceptance criteria
REQ-1 (v5.0.0-5.1.1, v5.0.0-5.2.2)
When a signed-in user uploads a file, the upload service shall accept it only if the
content is a PNG or JPEG image and the extension is .png, .jpg or .jpeg.
REQ-2 (v5.0.0-5.2.1)
If an upload is larger than 2 MB, then the upload service shall reject it with HTTP 413
before writing anything to storage.
REQ-3 (v5.0.0-5.2.6)
If an image is wider or taller than 4096 pixels, then the upload service shall reject it
with HTTP 422 without fully decoding it.
REQ-4 (v5.0.0-5.3.2)
The upload service shall store each avatar under a server-generated random name and
shall ignore the client-supplied filename for storage paths.
REQ-5 (v5.0.0-5.3.1)
The upload service shall serve avatars from storage that never executes uploaded content.
REQ-6 (v5.0.0-8.2.2)
If a user requests to replace or delete another user's avatar, then the upload service
shall reject the request with HTTP 403.
REQ-7 (v5.0.0-16.3.2)
When the upload service rejects a request under REQ-6, the upload service shall write a
security log entry with user ID, target user ID, time in UTC and reason, and no image data.
REQ-8 (v5.0.0-15.1.2)
When this feature changes dependencies, the build shall produce an updated CycloneDX SBOM.
## Out of scope
Animated images, SVG, image editing, avatars for service accounts.
## Open questions
[NEEDS CLARIFICATION: should admins be able to remove another user's avatar?]
Every criterion has an ID, an ASVS reference and a shape that maps to setup, action and assertion. The open question stays open until a human answers it; an agent should not resolve it by guessing.
Step 3: write the design with threat notes and the dependency list
Ask the agent to draft specs/004-avatar-upload/design.md, then review and edit it. The security-relevant parts should look like this:
## Trust boundaries and threat notes
| Boundary | What crosses it | Threat (concept) | Covered by |
|---|---|---|---|
| Browser to API | Multipart upload, user session | Non-image content with an image extension | REQ-1 |
| Browser to API | Multipart upload | Oversized or very high resolution file exhausts memory | REQ-2, REQ-3 |
| API to storage | File bytes, storage key | Client filename used to build a path | REQ-4 |
| Storage to browser | Image bytes | Stored content executed or served with the wrong type | REQ-5 |
| API to API | User ID in request | User acts on another user's avatar | REQ-6, REQ-7 |
## Dependencies
See approved-dependencies.txt. No other package may be added for this feature.
And specs/004-avatar-upload/approved-dependencies.txt:
# name exact-version licence reason
example-image-probe 3.1.0 MIT read image dimensions from the header without full decode
example-multipart-parser 2.4.2 MIT streaming multipart parsing with a size limit
The two package names are placeholders. Your reviewer fills in real packages, checks each licence against your licence policy, and checks each version against known vulnerabilities before approving.
Step 4: have the agent produce tasks, and check them
Ask the agent to generate tasks.md from the requirements and design, in plan mode or with your spec tool's tasks command. Then check that the list includes tasks like these, each tied to a requirement:
- [ ] T1 Write failing tests for REQ-1 to REQ-7 (one test file per requirement group)
- [ ] T2 Add approved dependencies at the exact versions listed; run npm install once, commit package-lock.json
- [ ] T3 Implement size limit in the multipart parser config (REQ-2)
- [ ] T4 Implement header-only dimension check (REQ-3)
- [ ] T5 Implement content type check and extension allowlist (REQ-1)
- [ ] T6 Implement server-generated storage keys (REQ-4)
- [ ] T7 Configure storage and response headers so avatars are served as images only (REQ-5)
- [ ] T8 Implement ownership check and security log entry (REQ-6, REQ-7)
- [ ] T9 Regenerate SBOM with npm sbom and commit sbom.cdx.json (REQ-8)
- [ ] T10 Run the full test suite and the dependency scan in CI; attach results to the pull request
- [ ] T11 Update requirements.md and design.md for any behaviour that changed during implementation
If a requirement has no task, or a task has no requirement, fix the spec before implementation starts. Spec Kit's /speckit.analyze reports exactly that kind of gap.
Step 5: review the dependency list before implementation
Before the agent runs T2, a human approves approved-dependencies.txt by asking: is each package needed, is it the package you think it is (check the name and publisher on the registry yourself), is the licence allowed, and does the pinned version have known vulnerabilities? For the last question, Gold Open Source at gold.safeguard.sh gives free CVE intelligence with affected and fixed versions, with no login.
Step 6: implement with the agent in a mode that asks before changes
In Claude Code, approving the plan with "Yes, manually approve edits" keeps per-edit review. In Spec Kit, /speckit.implement can be scoped to one phase at a time, and the reference recommends validating each stage before continuing. Whatever the tool, stop any install command that does not match the approved list.
Step 7: run tests, a dependency check and a scan in CI
The CI job below runs on every pull request. It installs from the lockfile, runs tests, fails if a direct dependency is not on the approved list, generates the SBOM, and runs a Safeguard scan of the working tree. Pin third-party actions to full commit SHAs in production; tags are shown here for readability.
name: spec-checks
on:
pull_request:
permissions:
contents: read
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Install exactly what the lockfile says
run: npm ci
- name: Run tests
run: npm test
- name: Fail on unapproved direct dependencies
run: |
jq -r '((.dependencies // {}) + (.devDependencies // {})) | keys[]' package.json | sort -u > declared.txt
cat specs/*/approved-dependencies.txt project-approved-dependencies.txt 2>/dev/null \
| awk '!/^#/ && NF {print $1}' | sort -u > approved.txt
unapproved=$(comm -23 declared.txt approved.txt)
if [ -n "$unapproved" ]; then
echo "Dependencies not on an approved list:"
echo "$unapproved"
exit 1
fi
- name: Generate SBOM
run: npm sbom --sbom-format cyclonedx > sbom.cdx.json
- name: Scan with Safeguard CLI
run: |
curl -fsSL https://cli.safeguard.sh/install | bash
set +e
safeguard scan --dir . --output output
code=$?
set -e
if [ "$code" -ne 0 ]; then
echo "Scan did not complete (exit $code)"
exit 1
fi
- uses: actions/upload-artifact@v4
with:
name: spec-evidence
path: |
sbom.cdx.json
output/
Notes from the Safeguard CLI documentation: safeguard scan writes JSON results such as vuln.json and license.json under the output folder. It does not fail on findings; it exits 0 when the scan ran, 1 when the command failed, and 2 when results were written but the vulnerability scan did not run, which is why the job treats any non-zero exit as a failure. On a fresh CI runner, the docs say to put the scanner tools the CLI relies on on the PATH or point TOOLS_DIR at them. Gating merges on findings is done by policy gates in the Safeguard platform for repositories connected to it, not by this CLI step, which produces evidence.
project-approved-dependencies.txt holds packages the project already uses, so the check does not fail on dependencies that predate the feature.
Step 8: update the spec
Compare the code against the spec one last time with /speckit.converge, Kiro's Sync Files or a plain review. If a limit changed during implementation, the requirement changes in the same pull request. Close the open question and commit sbom.cdx.json. The spec, tests, SBOM and scan output are the feature's evidence trail.
Common mistakes
- Specs with no non-functional requirements. If the spec does not state size limits, timeouts, rate limits, logging and retention, the agent picks defaults and you find out in production. ASVS v5.0.0-15.1.3 asks documentation to identify resource-demanding functionality; the spec is the place.
- Letting the agent edit the spec silently. If the agent can rewrite
requirements.mdto match what it built, the spec stops being a check. Spec changes must show in the pull request diff and get human review. - No traceability from requirement to test. Put requirement IDs in test names so a reviewer can see which criteria have no test.
- Security in one paragraph at the end. A generic "Security considerations" heading gets skimmed. Write security as acceptance criteria with ASVS IDs and as threat notes.
- Approving the dependency list after implementation. By then the agent has already written code against whatever it installed. Review the list before the install task runs.
- Copying a long security instruction file from the internet. Long, generic files cost context, may be truncated, and push the agent toward defences for attacks that cannot happen.
- Mapping to NIST SSDF only on paper. A table that says "PW.8: yes" is not evidence. Evidence is the test report linked to requirement IDs, the reviewed dependency list, the release SBOM and the scan output. Asked for proof of PO.1 for one feature, you should be able to hand over its requirements file and review history.
- Never closing the loop. Incidents found after release should become new requirements or constitution rules, or the same mistake returns in the next feature.
FAQ
What is spec-driven development in AI coding?
It is a workflow where an AI coding agent helps you write a requirements document, a design document and a task list before writing code, and then implements the tasks. Each document is reviewed by a person before the next phase starts, and all of them stay in the repository.
Is spec-driven development the same as plan mode?
No, but they are related. Plan mode, as Claude Code documents it, makes the agent research and propose a plan without editing files until you approve it. A spec-driven workflow adds separate, persistent requirements and design files with review between phases. Plan mode is a lighter option for smaller changes.
What files does Spec Kit create?
According to its documentation, Spec Kit creates a feature folder under specs/ with spec.md for requirements, plan.md and supporting design files such as research.md, data-model.md and contracts/, and tasks.md. Project principles live in a constitution file created with /speckit.constitution.
What files does Kiro create for a spec?
Kiro's documentation says each spec generates requirements.md (or bugfix.md for bugs), design.md and tasks.md, stored under .kiro/specs/. Project-wide guidance goes in steering files under .kiro/steering/.
What is EARS notation?
EARS, the Easy Approach to Requirements Syntax, is a way of writing requirements with a fixed clause order and a few keywords (While, When, Where, If and then). It was developed at Rolls-Royce and first published in 2009 by Alistair Mavin and colleagues. It makes requirements easier to read and to turn into tests.
How do I stop an AI agent adding unapproved dependencies?
Name the allowed packages with exact versions in the spec, tell the agent in its instruction file to stop and ask before adding anything else, install from the lockfile in CI with a command like npm ci, and add a CI step that fails when a direct dependency is not on the approved list. A human approves changes to the list.
Can a spec file contain a prompt injection?
Yes. A spec is text the agent reads and follows, so instructions hidden in a spec, an imported requirement or a community preset can steer the agent. Review spec and instruction file changes like code, be careful with imported text, and run the agent with permissions that make it ask before changing things.
Does spec-driven development satisfy NIST SSDF?
It can produce evidence for several SSDF practices, including PO.1 (security requirements), PW.1 (secure design), PW.4 (reusing well-secured components), PW.8 (testing) and PS.3 (provenance such as SBOMs). It does not satisfy them automatically. You need the artifacts, reviews and test results, not just the workflow.
Where Safeguard fits
Safeguard does not write specs or decide whether a requirement is right. The fit is the dependency and verification side of the workflow.
The approved dependency list in Step 3 needs a vulnerability check before a human signs it off, and the CI job in Step 7 needs a scan of what was actually installed. safeguard scan --dir . builds an SBOM for a directory, matches it against vulnerability databases and writes JSON results, including license results, that you can attach to the pull request. safeguard scan --sbom sbom.cdx.json scans an SBOM you already produced. In the platform, policies and gates decide what happens when a finding matches a rule: a gate can Block, Warn or Notify, and approved deviations go through exceptions rather than switching the gate off. The platform also shows which projects contain a given component and version, which helps when one approved package turns out to be vulnerable across several features. Reachability analysis ranks findings by whether your code calls the vulnerable path; how much that narrows the list depends on the codebase, so measure it during evaluation.
If you use the Safeguard CLI's own assistant to implement tasks, its /permissions modes (Ask, Accept edits, Plan, Auto, which is the default, and Strict) give the same "propose, then approve" control this guide recommends, and safeguard exec runs one non-interactive turn in CI with Strict as its default. Safeguard does not replace the human review of the spec text or of the dependency list.
Spec-driven development makes dependency choice visible, but the packages an agent installs are still ordinary open source with ordinary vulnerabilities. Once a vulnerable version sits on an approved list, the question is how quickly every feature using it moves to a fixed version, and what to do when no fix exists yet.
Fix it without waiting for upstream
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation categories include dependencies, licenses, security posture and findings, and each one is set separately. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds.
3. Gold Registry: start from components that are already fixed
Gold Registry provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs and a completed license check, with signed build provenance (signed with Safeguard's key and verifiable with cosign, SLSA Build Level 3 as the design target) and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. Ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | Safeguard policies and gates in CI |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
The first demo is a real Safeguard CLI session. It is asked to fix the lodash CVE in a repository, reads the finding and advisory, asks before any command that changes something, edits package.json to lodash 4.17.21, runs npm install and npm test with approval, prints a summary and pushes nothing, then shows /permissions, /model and /scan .. That is the implement step of a spec-driven workflow in miniature: a bounded task, a dependency change you can see, tests run before anything is called done, and a scan at the end.
Click through it at your own pace, or open it full screen.
The second demo sets up the Safeguard runner on your own machine, approves it with its device code, and scans from the terminal. Teams can use the same flow to scan a feature branch locally after the agent finishes its tasks, before the pull request and its spec go up for review.
Click through it at your own pace, or open it full screen.
The third demo connects Claude to the Safeguard MCP server with OAuth and shows how you choose which Safeguard tools Claude can use. If you run a spec workflow with Claude, this is how the agent can look up findings for the packages on your approved list while it plans, with tool access you decided up front.
Click through it at your own pace, or open it full screen.
You can download the apps at safeguard.sh/download and read the CLI documentation at docs.safeguard.sh/docs/cli.
Quick check
An agent finishes a spec task and ticks it in tasks.md, but no test covers the requirement it implemented. Is the task done?
No. A ticked box is the agent's claim, not proof. Define done as "the test linked to this requirement passes in CI", add the missing test, and check that the test actually asserts the response written in the requirement.
Why reference ASVS requirements as v5.0.0-5.2.1 instead of just 5.2.1?
The OWASP ASVS project says identifiers can change between versions and asks that references include the version. A versioned identifier keeps the spec precise after the standard is updated.
Sources
- Spec Kit README, GitHub
- Spec Kit, Spec-Driven Development methodology (spec-driven.md)
- Spec Kit documentation, Agentic SDD command reference
- Kiro documentation, Specs
- Kiro documentation, Feature Specs
- Kiro documentation, Specs best practices
- Kiro documentation, Steering
- AGENTS.md
- OpenAI Codex documentation, Custom instructions with AGENTS.md
- Claude Code documentation, Common workflows
- Claude Code documentation, Permission modes
- Claude Code documentation, Memory (CLAUDE.md and AGENTS.md)
- Claude Code documentation, Security
- Alistair Mavin, EARS: Easy Approach to Requirements Syntax
- Mavin, Wilkinson, Harwood and Novak, "Easy Approach to Requirements Syntax (EARS)", RE 2009, DOI 10.1109/RE.2009.9
- OWASP Application Security Verification Standard project
- OWASP ASVS 5.0.0 source, V5 File Handling
- OWASP ASVS 5.0.0 source, V15 Secure Coding and Architecture
- OWASP ASVS 5.0.0 source, V8 Authorization
- OWASP ASVS 5.0.0 source, V16 Security Logging and Error Handling
- NIST SP 800-218, Secure Software Development Framework version 1.1
- NIST SP 800-218 Rev. 1 (initial public draft), SSDF version 1.2
- NIST SP 800-218A, SSDF Community Profile for Generative AI and Dual-Use Foundation Models
- OpenSSF, Security-Focused Guide for AI Code Assistant Instructions
- OWASP GenAI Security Project, LLM01 Prompt Injection
- CISA, 2026 Minimum Elements for a Software Bill of Materials
- npm documentation, npm ci
- pip documentation, Secure installs
- Safeguard CLI documentation
- Safeguard CLI usage
Last reviewed: 25 October 2026. Spec tools and coding agents change quickly; check each tool's own documentation for current file names, commands and defaults before you rely on them.