create-spec-driven-app v0.7.0

Reviewing changes#

How to modify a spec that already shipped, without the matrix and the feature files drifting apart.


Change a requirement that already shipped#

Goal: modify the spec tree of a live project without the matrix, the specs and the feature files drifting apart. Recipes 2–3 cover writing requirements on day one; this is the loop for every day after.

1. Open the change#

csda change new add-dynamic-pricing
  ✔ Change add-dynamic-pricing created (lite · REQ ids REQ-014…REQ-016 reserved)

    + docs/specs/changes/add-dynamic-pricing/change.yaml
    + docs/specs/changes/add-dynamic-pricing/proposal.md
    + docs/specs/changes/add-dynamic-pricing/tasks.md

The reserved ID range is written into change.yaml, so two changes in flight never hand out the same REQ-NNN. Use --full instead of the default --lite when the change also needs a design.md and contract artefacts.

2. Let the tool tell you what to write next#

csda change status
  add-dynamic-pricing (spec-driven)

    ✔ proposal   proposal.md
    ▶ specs      specs/**/spec.md
    — design     design.md
    ✔ tasks      tasks.md

  Next
    → Write specs/**/spec.md

Artefacts are a dependency graph, not a phase gate (ADR-0018): is what is ready to write, is not applicable at this rigor level. Nothing blocks you from writing them out of order.

3. Write the delta#

Only what moves — never a copy of the whole spec. One file per capability under specs/<capability>/spec.md:

# Delta — pricing

## ADDED Requirements

### Requirement: REQ-014 — Dynamic peak pricing

The system SHALL raise the tariff automatically when occupancy crosses a
configured threshold.

#### Scenario: SCN-014 — Peak rate applies above the threshold

- GIVEN occupancy is above 85%
- WHEN a vehicle enters
- THEN the peak tariff is applied

<!-- csda:trace uc=UC-007 cmd=CMD-011 agg=AGG-Pricing evt=EVT-PriceApplied
     feature=features/pricing/dynamic_pricing.feature -->

Three sections are valid: ## ADDED Requirements, ## MODIFIED Requirements, ## REMOVED Requirements. MODIFIED replaces the whole requirement block — it does not merge scenario by scenario.

Two things the validator is strict about:

  • Steps are plain - GIVEN bullets. - **GIVEN** fails with scenario_not_gherkin, because the keyword has to start the line.
  • The body needs an RFC-2119 keyword (SHALL / MUST / SHOULD / MAY), otherwise you get no_rfc2119_keyword. A requirement that states no obligation is not a requirement.

The csda:trace comment is optional — it is the bridge to the DDD matrix. Without it the requirement still archives, with - in those columns.

depends= says what a requirement builds on. One more key in the same comment, and the only one plan and the harness read rather than the matrix:

<!-- csda:trace uc=UC-008 feature=features/pricing/refunds.feature
     depends=REQ-014 -->

Several are separated by commas — depends=REQ-014, REQ-016. What it changes:

  • csda plan orders the queue so a requirement never comes before the work it needs, and marks one whose dependency is still pending as ⛔ blocked. Blocked work stops appearing under "next steps", because recommending it was never useful.
  • csda validate fails on a cycle (requirement_cycle), on a dependency that names no requirement in the project (unknown_dependency), and on a requirement that depends on itself (self_dependency). Each says which ids are involved and what to delete.

Declaring nothing means no dependencies, so a project that never writes a depends= behaves exactly as it did before.

3b. Or have an agent draft it#

csda change author add-dynamic-pricing --artifact proposal --agent "claude -p < {prompt_file}"
csda change author add-dynamic-pricing --artifact specs   --agent-profile local-claude

This is the spec-author role of the multi-agent harness, and it is a separate loop from csda harness run on purpose. That loop is built around a requirement — a worktree per REQ, a branch named for it, a gate of validate --strict-tdd plus your tests. A change has no requirement yet; writing one is the job. So authoring has its own scope and its own gate.

The prompt is not a second description of what an artefact is for: it is csda change instructions rendered for an agent, so the rules, the reserved REQ range and the template are the same ones a person is shown.

The scope is enforced, not requested. The agent may write inside docs/specs/changes/<id>/ and nowhere else. Anything it writes elsewhere is put back before you see it: a file it created is deleted, a file it modified is restored from git. An agent asked to describe a change, and able to edit the capability spec it is describing, can make the change unnecessary instead of proposing it — quietly, in a diff that looks like the work. csda change archive is what moves a delta into a capability spec, after a human has read it.

It refuses to run on a dirty tree, and that is not ceremony. Enforcing the scope means reverting what the agent wrote outside it, and on a dirty tree that cannot be told apart from what you were in the middle of. Commit or stash first, and the revert can only ever discard the agent's own work.

--dry-run prints the prompt and writes nothing, which is the cheap way to see what an agent would be asked before paying for it.

4. Validate#

csda change validate add-dynamic-pricing
  ✔ add-dynamic-pricing (1 delta(s))

You rarely need to run this by hand: plain csda validate validates every active change, so a broken delta fails the PR gate on its own. A project with no docs/specs/changes/ directory is unaffected.

5. Archive it#

Archiving is the step that earns the ceremony. Preview first:

csda change archive add-dynamic-pricing --dry-run
  Archive plan (dry run — nothing written)

    ~ docs/specs/capabilities/pricing/spec.md (spec)
    ~ docs/specs/traceability.md (traceability)
    → docs/specs/changes/add-dynamic-pricing moves to docs/specs/changes/archive/2026-08-16-add-dynamic-pricing

Unchecked tasks block it (archive_tasks_incomplete); --force overrides. Then apply:

csda change archive add-dynamic-pricing --yes
  ✔ Archived as 2026-08-16-add-dynamic-pricing

    specs:        1 added · 0 modified · 0 removed
    traceability: 1 row(s) added · 0 updated · 0 removed
    features:     0 materialised

6. The requirement is now real work#

The archive merged the delta into docs/specs/capabilities/pricing/spec.md and added the matrix row:

| REQ-014 | SCN-014 | `features/pricing/dynamic_pricing.feature` | UC-007 | CMD-011 | AGG-Pricing | EVT-PriceApplied | - | TBD | Draft |

csda plan lists it as pending immediately. It arrives as Draft, which --strict-tdd deliberately tolerates — an accepted proposal is not yet a commitment to have written the test. The gate bites the moment you start:

# after moving the row's status to In Dev
csda validate . --strict-tdd
❌ [ERROR] --strict-tdd violations detected:
  [TDD-1] Test artifact is TBD but status is 'In Dev' (scenario: SCN-014)
💡 [FIX] TDD-1: write the test first, then set its path in the row's 'Test artifact' column (or move the status back to Draft).

That is the whole point: a merged proposal cannot quietly become undone work.

It composes with packs#

You wantCommand
Review an upstream pack bump as intent, not as a file diffspecops diff --as-change --pack-version v0.2.0
Send a local change back upstream to the packspecops contribute --change add-dynamic-pricing
Fail CI when the project has drifted from the locked packvalidate --against-lock

See recipe 10 for the version-bump workflow those hook into.



Next#