Spec-Driven Extensions with OpenSpec
Foundry baselines are designed to be forked and extended. For anything beyond a quick edit — a new tool, a new endpoint, swapping a backend, hardening for ATO — it pays to write down what you're changing and why before touching code. OpenSpec is the lightweight spec-driven development (SDD) workflow we use for that, and the baselines are structured to work well with it.
This guide is optional. If you're making a one-line change, skip it. If you're planning a feature, hand work to a coding agent, or want a paper trail for review, this is the path.
Why pair OpenSpec with a baseline
- Baselines are extension points, not products. They expect you to add tools, routes, config, and integrations. A proposal forces you to scope that work before you start.
- Coding agents perform better with a spec. Asking Claude or Cursor to "add a vector-store tool" produces drift. Asking it to "implement
openspec/changes/add-vector-tool/" produces a focused diff. - Reviewable diffs. The proposal + tasks live next to the implementation, so reviewers see the intent, not just the code.
- Archive becomes documentation. Completed changes move to
openspec/changes/archive/and double as a record of how the baseline evolved.
Prerequisites
- A forked baseline checked out locally (e.g. strands-base-agent)
- OpenSpec CLI installed —
npm i -g @fission-ai/openspecorbrew install openspec - A coding agent that knows the OpenSpec slash commands (Claude Code, Cursor, etc.)
Workflow
1. Make sure OpenSpec is set up in your fork
Foundry baselines ship with OpenSpec already initialized — there's an
openspec/ directory and a config.yaml at the repo root. Before you
start a new change, refresh the instruction files so your coding agent
gets the latest slash commands and prompts:
cd my-strands-agent
openspec update
If you're working in a repo that doesn't have openspec/ yet (older fork,
internal project, etc.), run openspec init first and commit the result.
2. Propose a change
Use your agent's /openspec-new-change slash command and describe what you
want. (Equivalent CLI: openspec new change <change-name> scaffolds the
directory, then you fill in the proposal yourself.) The result is a folder
under openspec/changes/<change-name>/ with:
proposal.md— why, what changes, capabilities added/modified, acceptance criteriatasks.md— a checklist of concrete stepsspecs/— any new or modified capability specs
Review and edit the proposal before implementation. This is the cheap moment to course-correct.
openspec list # see active changes
openspec show <change-name> # inspect one
openspec validate <change-name> # confirm the proposal structure is well-formed
3. Implement against the proposal
Run /openspec-apply-change <change-name>. The agent works through
tasks.md, ticking items off as it goes. Because the spec is in the working
tree, the agent stays anchored to it. There's no apply CLI subcommand —
the slash command drives the implementation directly from tasks.md.
4. Verify and archive
openspec validate <change-name> # check the proposal/spec structure
openspec archive <change-name> # move it to openspec/changes/archive/
/openspec-verify-change and /openspec-archive-change are the slash-command
equivalents — they additionally cross-check the diff against acceptance
criteria before archiving. The archived change becomes part of your fork's
history.
Worked example: add a new tool to strands-base-agent
A typical extension to strands-base-agent is adding a domain tool. The SDD version looks like:
- Propose —
/openspec-new-change add-jira-tool- Proposal captures: tool signature, which MCP server (if any), config surface, error behavior, who calls it.
- Acceptance criteria: tool appears in the agent's tool list, returns structured output on a known issue ID, fails closed on auth errors.
- Implement — agent adds
tools/jira_tool.py, registers it via the tool loader, updatesconfig.yaml, writes tests against fixtures. - Verify —
pytest, hit/querywith a prompt that triggers the tool, confirm the trace shows the tool call. - Archive — proposal lands in
openspec/changes/archive/alongside the merged code.
The same pattern applies to adding a new MCP server, swapping the session backend, or adding an auth middleware.
When not to use this
- Renaming a variable, fixing a typo, bumping a dependency
- Spikes where you don't yet know what you're building — start with code, write the spec when it stabilizes
- Throwaway POCs that won't be merged
Related
- OpenSpec on GitHub — CLI, schema, slash-command reference
- Strands Base Agent Quickstart — get the baseline running first
- Choose your path — pick the right baseline before proposing changes against it