Skip to main content

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/openspec or brew 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 criteria
  • tasks.md — a checklist of concrete steps
  • specs/ — 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:

  1. 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.
  2. Implement — agent adds tools/jira_tool.py, registers it via the tool loader, updates config.yaml, writes tests against fixtures.
  3. Verifypytest, hit /query with a prompt that triggers the tool, confirm the trace shows the tool call.
  4. 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