CtrlK
BlogDocsLog inGet started
Tessl Logo

spec-driven-devlopment/spec-as-source

Spec-driven development on OpenSpec, with mechanical spec-as-source enforcement: a custom 'spec-as-source' OpenSpec schema adds file-ownership (targets) and test-verification ([@test]) metadata to every capability spec, three scripts (link check, ownership check, manifest build) keep code and specs from drifting apart, plus requirement-gathering, spec-writer, work-review, and a session-handoff skill with a proactive context-warning hook.

71

Quality

89%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Advisory

Suggest reviewing before use

Overview
Quality
Evals
Security
Files

SKILL.mdskills/spec-writer/

name:
spec-writer
description:
Creates and maintains capability spec.md files under openspec/specs/: requirements, scenarios, targets frontmatter, and Verified by test links. Trigger — write a spec, update a spec, document requirements, create capability spec, spec drift in frontmatter or links.

Spec Writer

Create and maintain openspec/specs/<capability>/spec.md files that capture functional requirements, declare file ownership, and link to their verification tests — following the spec-as-source OpenSpec schema installed by spec-as-source-setup.

This is the focused, manual counterpart to the specs artifact that openspec-propose/openspec-apply-change generate as part of a full change. Use it directly when you need to write or fix a spec file outside a full change workflow (e.g. retrofitting targets:/**Verified by** onto a spec openspec archive created without frontmatter), or as the detailed reference for what a correct spec.md looks like.

When to use

  • After requirement-gathering produces a confirmed requirements summary.
  • When an existing capability spec needs updating because requirements changed.
  • When implementation revealed gaps that need documenting.
  • When a spec is missing its targets: frontmatter (e.g. a capability openspec archive created fresh, per the known OpenSpec-core limitation) and needs one added.

Steps

  1. Determine scope. Decide whether to create a new capability or update an existing one. One spec per logical capability — don't combine unrelated features. New capability names are kebab-case (e.g. user-auth, data-export) and become openspec/specs/<name>/spec.md.

  2. Write the frontmatter. Every spec.md starts with YAML frontmatter, before the title heading:

    ---
    targets:
      - src/path/to/implementation.py
      - src/path/to/related/file.py
    ---
    • targets: project-root-relative paths (not relative to the spec file) to every source file this capability is the authoritative source of truth for. Use targets: [] if the capability has no dedicated source files yet.
  3. Write (or confirm) the body structure. A capability spec is:

    # <Capability> Specification
    
    ## Purpose
    
    <!-- one or two sentences: why this capability exists -->
    
    ## Requirements
    
    ### Requirement: <name>
    <!-- normative SHALL/MUST sentence -->
    
    **Verified by**: [@test] <path/to/test/file>
    
    #### Scenario: <name>
    - **WHEN** <condition>
    - **THEN** <expected outcome>

    When writing a delta spec inside a change (openspec/changes/<change>/specs/<capability>/spec.md), use OpenSpec's delta operations instead of the full body:

    • ## ADDED Requirements — new requirements
    • ## MODIFIED Requirements — changed behavior, MUST include the full updated requirement block
    • ## REMOVED Requirements — deprecated, MUST include Reason and Migration
    • ## RENAMED Requirements — name changes only, FROM:/TO: format
  4. Format every requirement correctly:

    • Header: ### Requirement: <name> — free-text name, not an ID. Names must be unique within the capability.
    • Use SHALL/MUST for the normative sentence (avoid should/may).
    • Immediately after the SHALL/MUST sentence (never as the first line of the block), add: **Verified by**: [@test] <path/to/test/file>, project-root-relative. Multiple tests: additional **Verified by** lines, or space-separated on one line.
    • Each scenario: #### Scenario: <name>exactly 4 hashtags. 3 hashtags or bullets fail silently.
    • Every requirement needs at least one scenario.
  5. MODIFIED requirements workflow (when changing existing behavior):

    1. Locate the existing requirement in openspec/specs/<capability>/spec.md.
    2. Copy the ENTIRE requirement block (header through all scenarios).
    3. Paste under ## MODIFIED Requirements in the delta spec, edit to reflect new behavior.
    4. Keep the header text identical (whitespace-insensitive) so OpenSpec's merge matches it to the original.

    Using MODIFIED with partial content loses detail at archive/merge time. If you're adding a new concern without changing existing behavior, use ADDED instead.

  6. Retrofitting a frontmatter-less spec. A capability spec created fresh by openspec archive has no targets: block (OpenSpec's built-in skeleton doesn't add one). The first time you touch such a spec non-trivially, add the targets: frontmatter per Step 2 — this is expected, not a bug to report.

  7. Save the spec at openspec/specs/<capability>/spec.md (main spec) or openspec/changes/<change>/specs/<capability>/spec.md (delta spec inside an in-flight change).

Complete example

---
targets:
  - src/cart.py
  - src/checkout.py
---

# Shopping Cart Specification

## Purpose

Manage adding, removing, and checking out items in a user's cart.

## Requirements

### Requirement: Add item to cart
The system SHALL allow a user to add a product to their cart with a quantity.

**Verified by**: [@test] tests/cart/test_add_item.py

#### Scenario: Adding a new item
- **WHEN** a user adds a product not already in the cart
- **THEN** the cart contains that product with the given quantity

#### Scenario: Adding an existing item
- **WHEN** a user adds a product already in the cart
- **THEN** the existing quantity is incremented, not duplicated

### Requirement: Reject invalid quantity
The system MUST reject a quantity of zero or less.

**Verified by**: [@test] tests/cart/test_quantity_validation.py

#### Scenario: Zero quantity rejected
- **WHEN** a user attempts to add a product with quantity 0
- **THEN** the system raises a validation error and the cart is unchanged

Output

A spec.md file that:

  • passes scripts/check-spec-links.sh (every [@test] path exists),
  • passes scripts/check-target-ownership.sh (declares targets: for everything it owns),
  • is ready for spec-verify to validate end-to-end once the implementation exists.

skills

spec-writer

README.md

tile.json