feat(sdd): add .specify scaffold — constitution, AGENTS, personas, templates, example, RTM
Introduces the SDD root: a v1.0.0 constitution and machine-readable AGENTS.md grounded in the project's real conventions; six EARS-aware persona spec-review checklists that cross-reference .claude/personas/; feature-spec/ADR/threat-model/ api-contract templates; a fully worked _example feature; a living RTM; and an adrs/ pointer that reuses the existing docs/adr/ archive. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
94
.specify/templates/api-contract-stub.md
Normal file
94
.specify/templates/api-contract-stub.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# API Contract Stub
|
||||
|
||||
This project is **REST + OpenAPI**. The backend serves the live spec via springdoc at
|
||||
`http://localhost:8080/v3/api-docs` (dev profile only), and the frontend generates its
|
||||
TypeScript client from it with `npm run generate:api` (`openapi-typescript` →
|
||||
`frontend/src/lib/generated/api.ts`). There is no GraphQL in this stack.
|
||||
|
||||
> **The live spec is generated from the Java controllers — it is the source of truth.** A
|
||||
> hand-written contract under `.specify/features/<name>/api-contract.yaml` is a *design
|
||||
> artifact*: it pins the intended shape during spec review and is checked against the
|
||||
> generated spec once the endpoint exists. Keep it OpenAPI **3.1**, and keep
|
||||
> `@Schema(requiredMode = REQUIRED)` on the Java side as the real driver of `required`.
|
||||
|
||||
## How to use this stub
|
||||
|
||||
1. Copy the skeleton below to `.specify/features/<name>/api-contract.yaml`.
|
||||
2. Fill in the paths/methods/schemas your feature adds. Every mutating path documents the
|
||||
`403`/`401` responses and the `cookieAuth` security requirement (matching the real
|
||||
`@RequirePermission` gate).
|
||||
3. The `sdd-gate.yml` CI job lints any changed `api-contract.yaml` with Spectral
|
||||
(`npx @stoplight/spectral-cli lint`). Run it locally the same way before pushing.
|
||||
4. After the endpoint ships, run `npm run generate:api` and diff the generated types against
|
||||
this contract; reconcile any drift (the generated spec wins — update the contract).
|
||||
|
||||
## OpenAPI 3.1 skeleton
|
||||
|
||||
```yaml
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: Familienarchiv API — <feature name>
|
||||
version: 0.0.1-SNAPSHOT
|
||||
description: Design-time contract for <feature>. Source of truth is the generated /v3/api-docs.
|
||||
servers:
|
||||
- url: http://localhost:8080
|
||||
description: Local backend (dev profile)
|
||||
- url: https://archiv.raddatz.cloud
|
||||
description: Production (behind Caddy)
|
||||
components:
|
||||
securitySchemes:
|
||||
cookieAuth: # Spring Session JDBC — opaque session id in the SESSION cookie
|
||||
type: apiKey
|
||||
in: cookie
|
||||
name: SESSION
|
||||
schemas:
|
||||
ErrorResponse: # shape produced by GlobalExceptionHandler
|
||||
type: object
|
||||
required: [code, message]
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
description: Machine-readable ErrorCode (see ErrorCode.java / errors.ts).
|
||||
example: FORBIDDEN
|
||||
message:
|
||||
type: string
|
||||
# <YourResponseView>: # always a view, never a lazy-collection entity (ADR-036)
|
||||
# type: object
|
||||
# required: [id]
|
||||
# properties:
|
||||
# id: { type: string, format: uuid }
|
||||
security:
|
||||
- cookieAuth: [] # default: every path requires a session unless overridden to []
|
||||
paths:
|
||||
/api/<resource>:
|
||||
post:
|
||||
summary: <create …>
|
||||
operationId: <createResource>
|
||||
security:
|
||||
- cookieAuth: [] # plus @RequirePermission(Permission.X) on the controller
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/<CreateDTO>' }
|
||||
responses:
|
||||
'201':
|
||||
description: Created
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/<YourResponseView>' }
|
||||
'400': { description: Validation failed, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
|
||||
'401': { description: Unauthenticated, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
|
||||
'403': { description: Missing permission, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
|
||||
```
|
||||
|
||||
## Validating the contract in CI
|
||||
|
||||
The `sdd-gate.yml` workflow runs, on PRs that touch a `api-contract.yaml`:
|
||||
|
||||
```bash
|
||||
npx @stoplight/spectral-cli lint .specify/features/**/api-contract.yaml
|
||||
```
|
||||
|
||||
Spectral's default OpenAPI ruleset catches malformed specs, missing `operationId`s, and
|
||||
undefined `$ref`s. Add a `.spectral.yaml` at the repo root to tune rules if needed.
|
||||
Reference in New Issue
Block a user