refactor(sdd): make the feature spec issue-only (no committed spec.md)

The Gitea issue body is the single source of truth for a spec; the only
per-feature artifact in git is the RTM row (REQ-ID -> issue # -> test). Drops
per-feature spec.md/tasks.md/checklist files from the workflow (the _example
stays as a template/reference). Updates the guide, ADR-041, AGENTS.md, CLAUDE.md,
templates, the RTM (adds an Issue column), the implement/review-pr skills, and
replaces the file-spec CI jobs with an rtm-check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-06-13 12:14:32 +02:00
committed by marcel
parent fa6677a7c5
commit c160ab3223
13 changed files with 178 additions and 202 deletions

View File

@@ -17,17 +17,28 @@ multi-persona review → red/green TDD). It does not replace any of that — see
| # | Step | Who | Artifacts created / touched |
|---|---|---|---|
| 1 | **Idea → Gitea issue** using the Feature template | author | Gitea issue (labels `spec-required`, `needs-review`) from `.gitea/ISSUE_TEMPLATE/feature.md` |
| 2 | **Write the spec** — Context, User Journey, EARS `REQ-NNN` requirements, measurable acceptance criteria, Out of Scope | author | issue body **and** `.specify/features/<name>/spec.md` |
| 3 | **Add design artifacts** as needed | author | `design.md`; `api-contract.yaml` (any new endpoint); `threat-model.md` (uploads / new mutating endpoint / AI tool); feature-local `adr-NNN-*.md` or a `docs/adr/` entry for project-wide decisions |
| 4 | **Persona spec review** — the six checklists gate the spec | RE, Developer, Security, DevOps, UI/UX, Architect | `checklist-results.md` + the `## Persona Review Results` table; findings folded into the spec |
| 5 | **Resolve Open Questions & blocking FAILs** — spec does not proceed while any remain | author | spec updated; `Open Questions` emptied |
| 6 | **Decompose into tasks** in red/green order; seed the RTM | author | `tasks.md`; rows added to [`.specify/rtm.md`](./.specify/rtm.md) (`Status: Planned`) |
| 7 | **Implement** in a worktree, TDD per task (failing test → green → refactor → commit); agent reads `AGENTS.md` + `spec.md` + `api-contract.yaml` | implementer (often an AI agent) | code + tests; `npm run generate:api` after backend changes; RTM `Status``Done` |
| 8 | **PR → multi-persona PR review → merge**; archive the feature | reviewers | PR (`Closes #n`); on merge, move the feature dir under `.specify/features/_archive/<name>/` (or tag it shipped) |
| 2 | **Write the spec _in the issue body_** — Context, User Journey, EARS `REQ-NNN` requirements, measurable acceptance criteria, Out of Scope | author | the Gitea issue body **is** the spec (single source of truth — no committed `spec.md`) |
| 3 | **Capture durable design decisions** as needed | author | a `docs/adr/` ADR for any project-wide/irreversible decision; an OpenAPI contract and a STRIDE threat model inline in the issue (use the `.specify/templates/` as the writing aid) |
| 4 | **Persona spec review** — the six checklists gate the spec | RE, Developer, Security, DevOps, UI/UX, Architect | `/review-issue` posts each persona's checklist verdict as a Gitea comment; findings folded into the issue body |
| 5 | **Resolve Open Questions & blocking FAILs** — spec does not proceed while any remain | author | issue body updated; `Open Questions` emptied |
| 6 | **Seed the RTM** — one row per `REQ-NNN`, pointing at the issue | author | rows added to [`.specify/rtm.md`](./.specify/rtm.md) (`Issue: #n`, `Status: Planned`) — committed with the feature branch |
| 7 | **Implement** in a worktree, TDD per task (failing test → green → refactor → commit); agent reads `AGENTS.md` + the **issue body** (the spec) | implementer (often an AI agent) | code + tests; `npm run generate:api` after backend changes; RTM `Status``Done` |
| 8 | **PR → multi-persona PR review → merge** | reviewers | PR (`Closes #n`); the closed issue is the archived spec, the RTM rows record what shipped |
The personas at step 4 review the **spec**; the same personas at step 8 (via the existing
`review-pr` / `deliver-issue` skills) review the **code**. Step 4 catches at spec time what
used to surface only at step 8.
The personas at step 4 review the **spec (the issue)**; the same personas at step 8 (via the
existing `review-pr` / `deliver-issue` skills) review the **code**. Step 4 catches at spec time
what used to surface only at step 8.
**Skills that drive this:** `/draft-spec` (requirements engineer authors steps 12 → creates
the issue) → `/review-issue` (step 4 gate) → `/implement` (steps 67) → `/review-pr` (step 8).
`/deliver-issue` runs review → discuss → implement → review-loop end-to-end.
> **Why issue-only?** The Gitea issue body is the single source of truth for a spec — there is
> no committed per-feature `spec.md` to drift out of sync with it. The only SDD artifact that
> lives in git per feature is the RTM row (`REQ-ID → issue # → test`). The worked example under
> [`.specify/features/_example/`](./.specify/features/_example/) is a **template/reference**, not
> a live feature — it shows the full artifact set in one place; real features keep the spec in
> the issue.
## 2. How a Gitea issue becomes a spec
@@ -63,7 +74,7 @@ measurable acceptance criterion. See the full version in
## 3. How to run a persona review
Each persona reads the spec, walks its checklist in `.specify/personas/<persona>.md`, and
posts a Gitea comment (or fills `checklist-results.md`) with **PASS / FAIL / QUESTION** per
posts a Gitea comment with **PASS / FAIL / QUESTION** per
item and a verdict. A `FAIL` from Security or Architect is a hard block. Concrete example:
> ### Security — Spec Review
@@ -86,15 +97,16 @@ empties the finding, and the persona re-reviews until `APPROVE`. This mirrors th
Once the spec is `APPROVE`d and tasks are seeded, the implementer points the agent at the
artifacts. Example prompt:
> Implement `.specify/features/profile-picture-upload/`. Read `.specify/AGENTS.md` and obey
> the constitution it references. The contract is `spec.md` (REQ-001…REQ-009) and
> `api-contract.yaml`. Work through `tasks.md` in order, red/green TDD — write the failing
> test named in each task first, confirm it fails, then make it pass. After backend model
> changes run `npm run generate:api`. Each REQ has a test in the Traceability table; do not
> mark a task done until its test is green. Update `.specify/rtm.md` Status as you go.
> Implement Gitea issue #142 (profile picture upload). Read `.specify/AGENTS.md` and obey the
> constitution it references. The contract is the issue body — its EARS requirements
> REQ-001…REQ-009 and acceptance criteria. Build a red/green task list from them, write the
> failing test for each REQ first, confirm it fails, then make it pass. After backend model
> changes run `npm run generate:api`. Do not mark a REQ done until its test is green; flip its
> row in `.specify/rtm.md` to Done as you go.
The agent now has: the rules (`AGENTS.md` → constitution), the exact requirements with ids,
the API shape, and a test-first task list — so its output is bounded and verifiable.
The agent now has: the rules (`AGENTS.md` → constitution) and the exact requirements with ids
from the issue — so its output is bounded and verifiable. (The `/implement` skill fetches the
issue body for you via the Gitea API.)
## 5. Maintenance rules
@@ -105,13 +117,14 @@ the API shape, and a test-first task list — so its output is bounded and verif
in ADR-041's revision log (or a superseding ADR for MAJOR).
- **AGENTS.md** — keep it under 200 lines. It cross-references the constitution; it must never
duplicate or contradict it.
- **ADRs** — project-wide decisions go in [`docs/adr/`](./docs/adr/) (next free `NNN`, verify
on disk). Immutable once `Accepted`; supersede, don't edit. Feature-local decisions stay
beside the feature spec.
- **Feature specs** — archive on merge: move `.specify/features/<name>/` to
`.specify/features/_archive/<name>/`. The spec stays as the record of what shipped.
- **RTM** ([`.specify/rtm.md`](./.specify/rtm.md)) — append rows when a spec is approved;
flip `Status` as tests go green; never delete a shipped requirement's row. CI warns on drift.
- **ADRs** — project-wide/irreversible decisions go in [`docs/adr/`](./docs/adr/) (next free
`NNN`, verify on disk). Immutable once `Accepted`; supersede, don't edit.
- **Feature specs** — the spec is the Gitea issue body; there is no committed `spec.md`.
"Archiving" is just closing the issue (`Closes #n` on merge). The closed issue + the RTM
rows are the record of what shipped.
- **RTM** ([`.specify/rtm.md`](./.specify/rtm.md)) — append one row per `REQ-NNN` when a spec
is approved, each pointing at its issue (`#n`); flip `Status` as tests go green; never delete
a shipped requirement's row.
- **Personas** — update `.specify/personas/*.md` checklists when a recurring blind spot
appears; keep them aligned with the richer `.claude/personas/`.
@@ -133,11 +146,11 @@ the API shape, and a test-first task list — so its output is bounded and verif
|---|---|
| Non-negotiable rules | `.specify/constitution.md` |
| Agent rules (read every time) | `.specify/AGENTS.md` |
| Templates | `.specify/templates/{feature-spec,adr,threat-model,api-contract-stub}.md` |
| Templates (writing aids) | `.specify/templates/{feature-spec,adr,threat-model,api-contract-stub}.md` |
| Persona checklists | `.specify/personas/*.md` |
| In-flight feature | `.specify/features/<name>/{spec,design,tasks,checklist-results}.md` + `api-contract.yaml` + `threat-model.md` |
| Worked example | `.specify/features/_example/` |
| Traceability matrix | `.specify/rtm.md` |
| In-flight feature spec | the **Gitea issue body** (not a committed file) |
| Worked example (template/reference) | `.specify/features/_example/` |
| Traceability matrix | `.specify/rtm.md` (`REQ-ID → issue # → test`) |
| ADR archive | `docs/adr/NNN-*.md` |
| Issue templates | `.gitea/ISSUE_TEMPLATE/{feature,bug}.md` |
| CI gate | `.gitea/workflows/sdd-gate.yml` |
@@ -149,8 +162,8 @@ the API shape, and a test-first task list — so its output is bounded and verif
**Commands:**
```bash
# validate a contract locally (same as CI)
npx @stoplight/spectral-cli lint .specify/features/<name>/api-contract.yaml
# validate an OpenAPI contract locally (if you drafted one — same as CI)
npx @stoplight/spectral-cli lint <your-contract>.yaml
# regenerate the TS client after a backend model/endpoint change
cd frontend && npm run generate:api # backend must run with --spring.profiles.active=dev