diff --git a/CLAUDE.md b/CLAUDE.md index 810524b7..1181c41c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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//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 diff --git a/COLLABORATING.md b/COLLABORATING.md index 532d80a8..c8e96198 100644 --- a/COLLABORATING.md +++ b/COLLABORATING.md @@ -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. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7b7ba8a0..472bea43 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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). diff --git a/SPEC_DRIVEN_DEVELOPMENT.md b/SPEC_DRIVEN_DEVELOPMENT.md new file mode 100644 index 00000000..1440bb52 --- /dev/null +++ b/SPEC_DRIVEN_DEVELOPMENT.md @@ -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//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//` (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/.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//` to + `.specify/features/_archive//`. 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 shall .` | +| Event-driven | `When , the shall .` | +| State-driven | `While , the shall .` | +| Optional-feature | `Where , the shall .` | +| Unwanted-behavior | `If , then the shall .` | + +**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//{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//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 +```