docs(sdd): add SDD onboarding guide and cross-reference from governance docs
Adds SPEC_DRIVEN_DEVELOPMENT.md (8-step workflow, before/after issue, persona review example, agent-prompt example, maintenance rules, cheatsheet) and points CLAUDE.md, COLLABORATING.md, and CONTRIBUTING.md at the new .specify/ workflow without altering the existing cycle. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,10 @@ See [COLLABORATING.md](./COLLABORATING.md) for the full rules: issue tracking wo
|
||||
|
||||
See [CODESTYLE.md](./CODESTYLE.md) for coding standards: Clean Code, DRY/KISS trade-offs (KISS wins), and SOLID principles applied to this stack.
|
||||
|
||||
## Spec-Driven Development
|
||||
|
||||
This project uses Spec-Driven Development. **Before implementing a feature, read [`.specify/AGENTS.md`](./.specify/AGENTS.md)** (the short, machine-readable agent rules) and obey the [`.specify/constitution.md`](./.specify/constitution.md) it references. A feature's contract is its `.specify/features/<name>/spec.md` (EARS `REQ-NNN` requirements) plus any `api-contract.yaml`. Full workflow: [SPEC_DRIVEN_DEVELOPMENT.md](./SPEC_DRIVEN_DEVELOPMENT.md); worked example: [`.specify/features/_example/`](./.specify/features/_example/). The LLM reminders below restate constitution rules — the constitution and AGENTS.md are authoritative if they ever diverge.
|
||||
|
||||
---
|
||||
|
||||
## Stack
|
||||
|
||||
@@ -8,6 +8,14 @@ Evaluate all suggestions on their technical merits. No sycophancy — if somethi
|
||||
|
||||
## Core Workflow: Research → Plan → Implement → Validate
|
||||
|
||||
> **Spec-Driven Development.** Feature work is front-ended by an SDD spec: EARS-formatted
|
||||
> `REQ-NNN` requirements, persona spec-review checklists, and the project constitution. The
|
||||
> sequence below is unchanged — SDD formalises its *inputs* (the issue body becomes a
|
||||
> structured spec; the User Journey + E2E Scenarios below feed it). See
|
||||
> [SPEC_DRIVEN_DEVELOPMENT.md](./SPEC_DRIVEN_DEVELOPMENT.md) and
|
||||
> [`.specify/`](./.specify/) ([constitution](./.specify/constitution.md),
|
||||
> [AGENTS.md](./.specify/AGENTS.md)).
|
||||
|
||||
Every non-trivial feature or bug fix follows this sequence:
|
||||
|
||||
1. **Research** — Read the relevant code. Understand existing patterns before touching anything.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Contributing to Familienarchiv
|
||||
|
||||
For the full collaboration rules (issue workflow, PR process, Red/Green TDD, commit conventions) see [COLLABORATING.md](./COLLABORATING.md).
|
||||
For the Spec-Driven Development workflow (EARS specs, persona review, the constitution, and `.specify/`) see [SPEC_DRIVEN_DEVELOPMENT.md](./SPEC_DRIVEN_DEVELOPMENT.md).
|
||||
For coding style see [CODESTYLE.md](./CODESTYLE.md).
|
||||
For the system architecture see [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) (introduced in DOC-2; until that PR merges, see [docs/architecture/c4-diagrams.md](./docs/architecture/c4-diagrams.md)).
|
||||
For domain terminology see [docs/GLOSSARY.md](./docs/GLOSSARY.md).
|
||||
|
||||
157
SPEC_DRIVEN_DEVELOPMENT.md
Normal file
157
SPEC_DRIVEN_DEVELOPMENT.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# Spec-Driven Development (SDD)
|
||||
|
||||
How we turn a feature idea into merged, traceable code in this repo. SDD layers a uniform,
|
||||
machine-readable front-end onto the workflow we already run (Gitea issues → branch/PR →
|
||||
multi-persona review → red/green TDD). It does not replace any of that — see
|
||||
[ADR-041](./docs/adr/041-sdd-adoption.md) for the why.
|
||||
|
||||
- **The rules** live in [`.specify/constitution.md`](./.specify/constitution.md) (humans) and
|
||||
[`.specify/AGENTS.md`](./.specify/AGENTS.md) (AI agents, every invocation).
|
||||
- **The templates** live in [`.specify/templates/`](./.specify/templates/).
|
||||
- **The worked example** is [`.specify/features/_example/`](./.specify/features/_example/) — read it first.
|
||||
|
||||
---
|
||||
|
||||
## 1. The workflow in 8 steps
|
||||
|
||||
| # | 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) |
|
||||
|
||||
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.
|
||||
|
||||
## 2. How a Gitea issue becomes a spec
|
||||
|
||||
**Before (free-form issue):**
|
||||
|
||||
> **Title:** Add profile pictures
|
||||
> Users should be able to upload a picture for their profile. Make sure it's not too big and
|
||||
> only admins can remove other people's. Show initials if there's no picture.
|
||||
|
||||
Ambiguous: how big? which formats? what status code on rejection? what about unauthenticated
|
||||
callers? No identifiers to trace, no measurable criteria.
|
||||
|
||||
**After (SDD-structured issue — excerpt):**
|
||||
|
||||
> **Title:** As a user I want to upload a profile picture so other family members recognise me
|
||||
>
|
||||
> **## Requirements**
|
||||
> - **REQ-002** (Event-driven) — When an authenticated user sends `POST /api/users/me/avatar`
|
||||
> with a valid image, the user service shall store it and return a profile view with a
|
||||
> non-null `avatarUrl`.
|
||||
> - **REQ-008** (Unwanted-behavior) — If the uploaded file exceeds 2 MB, then the user service
|
||||
> shall return `400 ErrorCode.AVATAR_TOO_LARGE` and store nothing.
|
||||
> - **REQ-009** (Unwanted-behavior) — If a caller without `Permission.ADMIN_USER` targets
|
||||
> another user's avatar, then the system shall return `403 ErrorCode.FORBIDDEN`.
|
||||
>
|
||||
> **## Acceptance Criteria**
|
||||
> - **REQ-008** — a 2.1 MB PNG returns `400 AVATAR_TOO_LARGE`; bucket object count unchanged.
|
||||
|
||||
Every behavior is now a uniquely-identified, testable, EARS-formed requirement with a
|
||||
measurable acceptance criterion. See the full version in
|
||||
[`.specify/features/_example/spec.md`](./.specify/features/_example/spec.md).
|
||||
|
||||
## 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
|
||||
item and a verdict. A `FAIL` from Security or Architect is a hard block. Concrete example:
|
||||
|
||||
> ### Security — Spec Review
|
||||
>
|
||||
> | # | Item | Status | Note |
|
||||
> |---|---|---|---|
|
||||
> | 1 | All mutating endpoints have authn + authz `If` clauses | PASS | REQ-006 (401), REQ-009 (403) |
|
||||
> | 3 | Audit fields server-set, forbidden in body | **FAIL** | `avatarObjectKey` is bound from the request body → mass-assignment (CWE-639). Make it server-set in `UserService`. |
|
||||
> | 6 | Upload type allow-list + size | PASS | REQ-007 / REQ-008 |
|
||||
> | 9 | threat-model.md present & STRIDE-complete | **QUESTION** | Is the avatar URL public or proxied? If public S3, that's information disclosure. |
|
||||
>
|
||||
> **Verdict: CHANGES REQUESTED** — blocking FAIL: #3. Resolve #9 in the threat model.
|
||||
|
||||
The author folds the fix into the spec (here: server-set key + authenticated proxy URL),
|
||||
empties the finding, and the persona re-reviews until `APPROVE`. This mirrors the existing
|
||||
`review-issue` skill — the persona checklists just make the spec pass/fail explicit.
|
||||
|
||||
## 4. How the AI agent uses the spec
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## 5. Maintenance rules
|
||||
|
||||
- **Constitution** ([`.specify/constitution.md`](./.specify/constitution.md)) — change it only
|
||||
when a project-wide rule genuinely changes. Bump the semantic version (MAJOR = rule
|
||||
removed/weakened, MINOR = rule added/tightened, PATCH = wording), run the §6 Sync Impact
|
||||
review, and let the `constitution-diff` CI job list the files to reconcile. Record the bump
|
||||
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.
|
||||
- **Personas** — update `.specify/personas/*.md` checklists when a recurring blind spot
|
||||
appears; keep them aligned with the richer `.claude/personas/`.
|
||||
|
||||
## 6. Quick-start cheatsheet
|
||||
|
||||
**EARS patterns** (every requirement is one of these + a `REQ-NNN` id):
|
||||
|
||||
| Pattern | Shape |
|
||||
|---|---|
|
||||
| Ubiquitous | `The <system> shall <behavior>.` |
|
||||
| Event-driven | `When <trigger>, the <system> shall <behavior>.` |
|
||||
| State-driven | `While <state>, the <system> shall <behavior>.` |
|
||||
| Optional-feature | `Where <feature/permission present>, the <system> shall <behavior>.` |
|
||||
| Unwanted-behavior | `If <undesired condition>, then the <system> shall <response>.` |
|
||||
|
||||
**File locations:**
|
||||
|
||||
| What | Where |
|
||||
|---|---|
|
||||
| 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` |
|
||||
| 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` |
|
||||
| ADR archive | `docs/adr/NNN-*.md` |
|
||||
| Issue templates | `.gitea/ISSUE_TEMPLATE/{feature,bug}.md` |
|
||||
| CI gate | `.gitea/workflows/sdd-gate.yml` |
|
||||
|
||||
**Before you mark a feature done:** every `REQ-NNN` has a green test, the RTM Status is
|
||||
`Done`, all six personas APPROVE, `npm run lint` and the targeted tests pass, and
|
||||
`npm run generate:api` has been run if the backend model changed.
|
||||
|
||||
**Commands:**
|
||||
|
||||
```bash
|
||||
# validate a contract locally (same as CI)
|
||||
npx @stoplight/spectral-cli lint .specify/features/<name>/api-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
|
||||
```
|
||||
Reference in New Issue
Block a user