OpenSpec Hands‑On: 3 Commands, 4 Artifacts, 4‑Step Closed Loop for Incremental Feature Development

This tutorial walks through adding a priority field to an existing todo‑API using OpenSpec’s incremental workflow—proposing the change with /opsx:propose, reviewing the generated Delta Specs, applying the code with /opsx:apply, and archiving the change with /opsx:archive—while illustrating ADDED and MODIFIED specifications, code updates, testing, and best‑practice tips.

Shuge Unlimited
Shuge Unlimited
Shuge Unlimited
OpenSpec Hands‑On: 3 Commands, 4 Artifacts, 4‑Step Closed Loop for Incremental Feature Development

In real projects, new requirements often need to be added to an existing code base, which can cause bugs if changes are not carefully scoped. OpenSpec provides a brownfield‑first workflow that lets developers create, review, apply, and archive incremental changes using three slash commands.

You Will Complete

✅ Create an incremental change proposal with /opsx:propose ✅ Understand the difference between ADDED and MODIFIED entries in Delta Specs

✅ Generate code automatically with /opsx:apply ✅ Archive the change and merge Delta Specs into the main specification with

/opsx:archive

Prerequisites

Completed the first tutorial (todo‑api project with CRUD)

OpenSpec CLI installed: npm install -g @fission-ai/openspec@latest Claude Code or another OpenSpec‑compatible AI coding assistant

Node.js >= 20.19.0

Step 1: Create an Incremental Change with /opsx:propose

Run the slash command in the project directory: /opsx:propose add-todo-priority The assistant asks for a concise requirement description. A detailed description such as:

Give the Todo API a priority field. The field supports Low / Medium / High, defaults to Medium, and the list endpoint must support filtering by priority. Update the existing Update endpoint to accept priority.

This description becomes the source for proposal.md, which in turn drives the generation of four artifacts under openspec/changes/add-todo-priority/: proposal.md – why the change, what it does, scope, risks specs/ – Delta Specs with ADDED and MODIFIED sections design.md – technical decisions (e.g., string enum for priority, default value) tasks.md – granular implementation checklist

Delta Specs Example

## ADDED Requirements
### Requirement: Todo has priority field
Each todo item SHALL have a `priority` field with value `Low`, `Medium`, or `High`. Default: `Medium`.

#### Scenario: Default priority
- **WHEN** a POST request is sent to `/todos` with `{"title": "Buy groceries"}`
- **THEN** the system SHALL create a todo with `priority: "Medium"`

#### Scenario: Explicit priority on create
- **WHEN** a POST request is sent to `/todos` with `{"title": "Fix bug", "priority": "High"}`
- **THEN** the system SHALL create a todo with `priority: "High"`

#### Scenario: Invalid priority value
- **WHEN** a POST request is sent to `/todos` with `{"title": "Test", "priority": "Critical"}`
- **THEN** the system SHALL return HTTP 400

### Requirement: Filter todos by priority
... (omitted for brevity)

## MODIFIED Requirements
### Requirement: Update todo
... (existing scenarios) ...
#### Scenario: Update priority
- **WHEN** a PUT request is sent to `/todos/{id}` with `{"priority": "Low"}`
- **THEN** the system SHALL return HTTP 200 with `priority: "Low"`

The key difference from the first‑time CRUD change is that the Delta Specs now contain both ADDED (new priority field, filter) and MODIFIED (extension of existing create/update scenarios) entries.

首次变更 vs 增量变更 Delta Specs 对比
首次变更 vs 增量变更 Delta Specs 对比

Step 2: Review the Four Artifacts

proposal.md – answers “why change, what change, impact, risks”. Used by humans and the AI assistant.

specs/ – Delta Specs described above; focus on ADDED vs MODIFIED.

design.md – records decisions such as using a string enum for priority and choosing “Medium” as the default.

tasks.md – a checklist grouped by Model, API, Testing. Example excerpt:

## 1. Model Changes
- [ ] 1.1 Add priority field to Todo typedef in `src/todo.js`
- [ ] 1.2 Add VALID_PRIORITIES constant and `isValidPriority()` function
- [ ] 1.3 Modify `createTodo()` to accept optional priority

## 2. API Changes - Create
- [ ] 2.1 Modify POST `/todos` handler to accept optional `priority`
- [ ] 2.2 Validate priority value, return 400 for invalid values

## 3. API Changes - List
- [ ] 3.1 Modify GET `/todos` handler to filter by `priority`
- [ ] 3.2 Return 400 for invalid filter values

## 4. API Changes - Update
- [ ] 4.1 Modify PUT `/todos/:id` handler to accept `priority`
- [ ] 4.2 Validate and apply priority update

## 5. Testing
- [ ] 5.1‑5.5 Verify priority scenarios

Step 3: Apply the Change with /opsx:apply

After confirming the artifacts, run: /opsx:apply The assistant reads tasks.md and implements each step, marking completed items with [x]. Key code changes include:

src/todo.js – Add priority field and validation

const VALID_PRIORITIES = ['Low', 'Medium', 'High'];

function createTodo(title, options = {}) {
  return {
    id: crypto.randomUUID(),
    title,
    completed: false,
    priority: options.priority || 'Medium',
  };
}

function isValidPriority(priority) {
  return VALID_PRIORITIES.includes(priority);
}

module.exports = { createTodo, isValidTitle, isValidPriority, VALID_PRIORITIES };

src/app.js – Update routes (Express‑style illustration)

// POST /todos — create todo
app.post('/todos', (req, res) => {
  const { title, priority } = req.body;
  if (!isValidTitle(title)) {
    return res.status(400).json({ error: 'title is required and must be non‑empty' });
  }
  if (priority !== undefined && !isValidPriority(priority)) {
    return res.status(400).json({ error: `priority must be one of: ${VALID_PRIORITIES.join(', ')}` });
  }
  const todo = createTodo(title, { priority });
  save(todo);
  res.status(201).json(todo);
});

// GET /todos — list with optional filter
app.get('/todos', (req, res) => {
  const { priority } = req.query;
  if (priority !== undefined && !isValidPriority(priority)) {
    return res.status(400).json({ error: `priority must be one of: ${VALID_PRIORITIES.join(', ')}` });
  }
  const todos = findAll();
  if (priority !== undefined) {
    return res.json(todos.filter(t => t.priority === priority));
  }
  res.json(todos);
});

// PUT /todos/:id — update including priority
app.put('/todos/:id', (req, res) => {
  if (!exists(req.params.id)) {
    return res.status(404).json({ error: 'Todo not found' });
  }
  const todo = findById(req.params.id);
  const { title, completed, priority } = req.body;
  if (priority !== undefined && !isValidPriority(priority)) {
    return res.status(400).json({ error: `priority must be one of: ${VALID_PRIORITIES.join(', ')}` });
  }
  if (title !== undefined) todo.title = title;
  if (completed !== undefined) todo.completed = Boolean(completed);
  if (priority !== undefined) todo.priority = priority;
  save(todo);
  res.json(todo);
});

Running the existing test suite plus the new curl commands verifies the behavior:

# Create with explicit priority
curl -X POST http://localhost:3000/todos -H "Content-Type: application/json" -d '{"title": "Fix bug", "priority": "High"}'
# Create without priority (defaults to Medium)
curl -X POST http://localhost:3000/todos -H "Content-Type: application/json" -d '{"title": "Buy groceries"}'
# Filter by priority
curl "http://localhost:3000/todos?priority=High"
# Invalid priority should return 400
curl -X POST http://localhost:3000/todos -H "Content-Type: application/json" -d '{"title": "Test", "priority": "Critical"}'

If all four tests pass, the feature is correctly implemented.

Step 4: Archive the Change with /opsx:archive

/opsx:archive

The assistant moves the change directory to openspec/changes/archive/YYYY‑MM‑DD‑add-todo-priority/, checks that all tasks are completed, and merges ADDED entries into the main specification while replacing MODIFIED entries.

归档前后目录结构对比
归档前后目录结构对比

After archiving, the active openspec/changes/ directory is empty, and the main openspec/specs/ reflects the new priority capability.

Advanced Tips

Delta Specs Operations : ADDED (new feature), MODIFIED (extend existing), REMOVED (delete).

Naming : Use concise action‑object names like add-todo-priority, fix-login-timeout, remove-legacy-auth.

Explore Phase : Run /opsx:explore to discuss vague requirements before proposing.

Context Reset : After propose, start a new AI session for apply to avoid accumulated noise.

Common Questions

Q1: What if a task fails during apply ?

The assistant pauses, offering options to skip, fix manually, or retry. In tasks.md, completed tasks stay marked with [x], and the next apply resumes from the first unfinished task.

Q2: Can I edit Delta Specs manually?

Yes. All artifacts are plain Markdown files; editing them updates what the AI reads on subsequent runs.

Q3: Are the commands different for incremental vs. first‑time changes?

No. The same three commands work; OpenSpec determines ADDED vs. MODIFIED based on the existing specs/ content.

Q4: Must every change be archived?

Archiving promptly keeps openspec/changes/ as an active work queue and improves proposal quality for future changes.

Q5: How to handle conflicting incremental changes?

Complete and archive one change before starting the next, or manually reconcile overlapping tasks in tasks.md.

Conclusion

The tutorial demonstrates the repeatable four‑step loop— propose → apply → archive —that lets developers add or modify features in an existing code base with minimal friction. By focusing on Delta Specs (ADDED, MODIFIED, REMOVED), the workflow isolates the exact change set, enabling quick reviews and reliable AI‑generated code while keeping the project’s source of truth up‑to‑date.

增量开发四步流程
增量开发四步流程
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

CLINode.jsAI programmingincremental developmentOpenSpecDelta specs
Shuge Unlimited
Written by

Shuge Unlimited

Formerly "Ops with Skill", now officially upgraded. Fully dedicated to AI, we share both the why (fundamental insights) and the how (practical implementation). From technical operations to breakthrough thinking, we help you understand AI's transformation and master the core abilities needed to shape the future. ShugeX: boundless exploration, skillful execution.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.