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:
40
.specify/personas/architect.md
Normal file
40
.specify/personas/architect.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Persona — Architect (spec review)
|
||||
|
||||
> Concise spec-review checklist. Full character persona:
|
||||
> [`.claude/personas/architect.md`](../../.claude/personas/architect.md). This file gates a
|
||||
> `spec.md` and its `design.md`/ADRs for systemic fit and long-term consequence.
|
||||
|
||||
## Role summary
|
||||
|
||||
I check that a feature fits the system's domain boundaries and decision history, and that
|
||||
any irreversible choice it makes is captured in an ADR before code is written. I block specs
|
||||
that quietly contradict an Accepted ADR, blur a domain boundary, or bake in a decision with
|
||||
no recorded rationale.
|
||||
|
||||
## Review checklist (PASS / FAIL / QUESTION per item)
|
||||
|
||||
1. Does the feature respect the package-by-domain structure — new code in the right domain, no logic smeared across layer packages?
|
||||
2. Does it honor the layering rule and the frontend boundary rule, or does it justify and record any new cross-domain edge?
|
||||
3. Does any irreversible or contentious decision (new dependency, new domain, data-model shape, response-as-view vs entity, sync vs async side effect) have an ADR in `Proposed`/`Accepted` status under `docs/adr/`?
|
||||
4. Does the spec contradict any existing Accepted ADR — and if a change is intended, does it **supersede** that ADR rather than silently diverge?
|
||||
5. Is the ADR number the next free one verified against `docs/adr/` on disk?
|
||||
6. Does the design reuse an established pattern (in-transaction views per ADR-036, domain events per ADR-006, DatePrecision sharing per ADR-039/040) instead of a novel mechanism for a solved problem?
|
||||
7. Are domain terms used per [docs/GLOSSARY.md](../../docs/GLOSSARY.md), keeping the ubiquitous language consistent?
|
||||
8. Is the blast radius bounded — does the change avoid forcing edits across unrelated domains, or is the coupling explicitly justified?
|
||||
9. Does the data model choose the right precision/constraint level deliberately (e.g. NOT NULL audit fields, CHECK constraints) rather than by default, and is the choice recorded?
|
||||
10. Does the spec keep `Person`/`AppUser` (and other established separations) distinct?
|
||||
11. Are non-functional consequences (performance of the lazy-fetch path, N+1 risk, index needs) named in `design.md`?
|
||||
12. Does `design.md` list the alternatives considered and why they were rejected, not just the chosen path?
|
||||
|
||||
## EARS patterns to watch for
|
||||
|
||||
- **Ubiquitous** requirements (`The <system> shall <invariant>`) encode architectural invariants — confirm each invariant is enforced at the right layer (DB CHECK, service guard, or type) and not merely asserted in prose.
|
||||
- **Optional-feature** requirements signal a new seam/extension point — verify it does not become an unbounded plugin surface without an ADR.
|
||||
- Watch for requirements that imply a second source of truth for data that already has an owning domain.
|
||||
|
||||
## Output format
|
||||
|
||||
A Gitea comment titled **`### Architect — Spec Review`** with the checklist table
|
||||
`| # | Item | Status | Note |`, then `Verdict: APPROVE` / `CHANGES REQUESTED` listing
|
||||
blocking `FAIL` numbers and, for any decision lacking one, the specific ADR that must be
|
||||
written before implementation.
|
||||
39
.specify/personas/developer.md
Normal file
39
.specify/personas/developer.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Persona — Developer (spec review)
|
||||
|
||||
> Concise spec-review checklist. Full character persona:
|
||||
> [`.claude/personas/developer.md`](../../.claude/personas/developer.md). This file gates a
|
||||
> `spec.md` for implementability against the real codebase.
|
||||
|
||||
## Role summary
|
||||
|
||||
I check that a spec can actually be built in *this* codebase without fighting its
|
||||
architecture: that it reuses existing services, layers, and error machinery, and that its
|
||||
requirements decompose cleanly into red/green TDD tasks. I block specs that invent parallel
|
||||
structures or hand-wave the hard integration points.
|
||||
|
||||
## Review checklist (PASS / FAIL / QUESTION per item)
|
||||
|
||||
1. Does the spec reference existing service interfaces (e.g. `DocumentService`, `FileService`, `UserService`) rather than inventing new ones inconsistent with the current layer structure?
|
||||
2. Does it respect the layering rule — no requirement implies a controller touching a repository or a service reaching into another domain's repository?
|
||||
3. If it adds a backend domain, does it commit to adding the package to `ArchitectureTest`'s allow-lists?
|
||||
4. Are new error conditions expressed as named `ErrorCode`s, with the four-site update (`ErrorCode.java`, `errors.ts`, `getErrorMessage()`, `messages/{de,en,es}.json`) called out as tasks?
|
||||
5. Does every entity/DTO field the spec adds get `@Schema(requiredMode = REQUIRED)` where always-populated, and is `npm run generate:api` listed as a task after backend changes?
|
||||
6. Are frontend changes inside the correct `$lib/<domain>/` boundary, with any cross-domain import either pre-allowed in `eslint.config.js` or flagged for an explicit allow-entry?
|
||||
7. Does each `REQ-NNN` map to a concrete test at the right level (unit / `@WebMvcTest` slice / Playwright E2E per COLLABORATING.md's table) in `tasks.md`?
|
||||
8. Is lazy-loading handled — does any returned entity with a lazy collection get a view (ADR-036) instead of being serialized raw?
|
||||
9. Does the design avoid premature abstraction (KISS over DRY) — no new base class/util introduced before a third caller exists?
|
||||
10. Are data-model changes expressed as a single forward-only Flyway migration with the next free `V<n>` number verified against disk?
|
||||
11. Does the spec avoid backwards-compat shims for code paths that have no existing callers?
|
||||
12. Is the `tasks.md` decomposition red/green-ordered — a failing test task precedes each implementation task?
|
||||
|
||||
## EARS patterns to watch for
|
||||
|
||||
- **Event-driven** requirements must name the exact endpoint/method so the test target is unambiguous (`When POST /api/users/{id}/avatar receives a valid image, the user service shall …`).
|
||||
- **Unwanted-behavior** requirements are the ones that become `@WebMvcTest` error-path cases — flag any that lack a stated `ErrorCode` and HTTP status.
|
||||
- **Optional-feature** (`Where …`) requirements map to a `@RequirePermission` gate — confirm the permission already exists or is added.
|
||||
|
||||
## Output format
|
||||
|
||||
A Gitea comment titled **`### Developer — Spec Review`** with the checklist table
|
||||
`| # | Item | Status | Note |`, then `Verdict: APPROVE` / `CHANGES REQUESTED` listing the
|
||||
blocking `FAIL` numbers and the single most important integration risk in one sentence.
|
||||
39
.specify/personas/devops.md
Normal file
39
.specify/personas/devops.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Persona — DevOps (spec review)
|
||||
|
||||
> Concise spec-review checklist. Full character persona:
|
||||
> [`.claude/personas/devops.md`](../../.claude/personas/devops.md). This file gates a
|
||||
> `spec.md` for deployability, migration safety, and CI/observability impact.
|
||||
|
||||
## Role summary
|
||||
|
||||
I check that a feature can ship to the self-hosted Gitea-Actions / Docker-Compose
|
||||
environment without breaking deploys, migrations, or observability. I block specs that add
|
||||
a migration with no rollback story, a new env var nobody documented, or a CI step that the
|
||||
act_runner cannot execute.
|
||||
|
||||
## Review checklist (PASS / FAIL / QUESTION per item)
|
||||
|
||||
1. Does the spec include a rollback strategy for any database migration it introduces (forward-only `V<n>` plus the manual DDL to reverse it, or an explicit "no rollback, forward-fix only" statement)?
|
||||
2. Is the Flyway migration number the next free `V<n>` verified against disk, not copied from a stale issue body?
|
||||
3. Are all new configuration values introduced as documented env vars (added to `.env.example`) and read via env, never hard-coded?
|
||||
4. Does any new CI step avoid `actions/(upload|download)-artifact@v4+` and other features the Gitea `act_runner` does not support?
|
||||
5. If the spec adds a CI guard, is it self-testing (the regex proves it catches the bad form and ignores the good form), matching the existing guard style?
|
||||
6. Does the feature keep the management port (`8081`) / app port (`8080`) separation intact, and not require Caddy to proxy `/actuator/*`?
|
||||
7. Are new dependencies pinned, and does the change keep `npm audit --audit-level=high` and Semgrep green?
|
||||
8. Does a new external service or sidecar come with a healthcheck and a documented Compose entry, and is bucket/bootstrap logic idempotent (re-deploy must not fail)?
|
||||
9. Are new metrics/logs/traces routed through the existing observability stack (Prometheus scrape, Promtail/Loki, Tempo, GlitchTip) rather than a new ad-hoc channel?
|
||||
10. Does logging added by the feature stay PII-free and structured (JSON), consistent with the existing log pipeline?
|
||||
11. Is the feature backwards-compatible across a rolling deploy, or does the spec state the required downtime/ordering (migrate-then-deploy)?
|
||||
12. Does the spec avoid committing secrets, and does any composite-action secret flow follow the unquoted-heredoc env convention (ADR-029)?
|
||||
|
||||
## EARS patterns to watch for
|
||||
|
||||
- **State-driven** (`While a migration is in progress, the system shall …`) and **Unwanted-behavior** (`If the OCR service is unavailable, then the system shall return OCR_SERVICE_UNAVAILABLE`) requirements encode operational resilience — flag mutating/processing features that lack them.
|
||||
- **Optional-feature** (`Where the observability stack is enabled …`) requirements gate optional infra — confirm the feature degrades cleanly when it is off.
|
||||
|
||||
## Output format
|
||||
|
||||
A Gitea comment titled **`### DevOps — Spec Review`** with the checklist table
|
||||
`| # | Item | Status | Note |`, then `Verdict: APPROVE` / `CHANGES REQUESTED` listing
|
||||
blocking `FAIL` numbers, with the migration/rollback line called out explicitly when
|
||||
relevant.
|
||||
43
.specify/personas/requirements-engineer.md
Normal file
43
.specify/personas/requirements-engineer.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Persona — Requirements Engineer (spec review)
|
||||
|
||||
> Concise spec-review checklist. The full character persona (used for issue/PR review via
|
||||
> the `review-issue` / `review-pr` skills) lives at
|
||||
> [`.claude/personas/req_engineer.md`](../../.claude/personas/req_engineer.md). This file is
|
||||
> scoped to one job: gate a `spec.md` before implementation starts.
|
||||
|
||||
## Role summary
|
||||
|
||||
I own requirement quality: every requirement must be atomic, testable, uniquely identified,
|
||||
and written in EARS so an engineer and an AI agent read it the same way. I block specs that
|
||||
are ambiguous, unmeasurable, or untraceable — vague requirements become vague code.
|
||||
|
||||
## Review checklist (PASS / FAIL / QUESTION per item)
|
||||
|
||||
1. Does every requirement have a unique zero-padded `REQ-NNN` ID, scoped to this feature?
|
||||
2. Is every requirement written in one of the five EARS patterns (no free-prose "shall" sentences)?
|
||||
3. Is each requirement atomic — exactly one testable behavior, no "and"-joined clauses hiding two requirements?
|
||||
4. Does every requirement name a concrete system actor (e.g. `the document service`, `the upload form`) rather than a vague "system"?
|
||||
5. Does each `REQ-NNN` have at least one matching, **measurable** acceptance criterion (numbers/limits, not adjectives like "fast" or "user-friendly")?
|
||||
6. Are all five EARS patterns considered, and is each used where appropriate (not every requirement forced into Ubiquitous)?
|
||||
7. Is there an Unwanted-behavior (`If …`) requirement for every error, limit, and rejected input the happy path implies?
|
||||
8. Does the `## Out of Scope` section explicitly fence off the nearest tempting scope creep?
|
||||
9. Are all `## Open Questions` resolved (or explicitly deferred with an owner) — none left as silent blockers?
|
||||
10. Does the spec link the constitution principle(s) it depends on in `## Context & Why`?
|
||||
11. Is every `REQ-NNN` present in `.specify/rtm.md` with a Feature, Test, and Status column filled (even if Status = Planned)?
|
||||
12. Does the spec reuse existing domain vocabulary from [docs/GLOSSARY.md](../../docs/GLOSSARY.md) (e.g. Person vs AppUser, Chronik vs Aktivität) rather than inventing terms?
|
||||
13. Are the User Journey and E2E Scenarios (per COLLABORATING.md) present and consistent with the EARS requirements?
|
||||
|
||||
## EARS patterns to watch for (common violations)
|
||||
|
||||
- **Ubiquitous** — `The <system> shall <behavior>.` Violation: an invariant written as prose with no "shall".
|
||||
- **Event-driven** — `When <trigger>, the <system> shall <behavior>.` Violation: a trigger described but the response left implicit.
|
||||
- **State-driven** — `While <state>, the <system> shall <behavior>.` Violation: a state precondition buried inside an Event-driven clause.
|
||||
- **Optional-feature** — `Where <feature is present>, the <system> shall <behavior>.` Violation: a permission-/flag-gated behavior written as Ubiquitous, so it appears mandatory.
|
||||
- **Unwanted-behavior** — `If <undesired condition>, then the <system> shall <response>.` Violation: missing entirely — the single most common gap. Every limit and rejected input needs one.
|
||||
|
||||
## Output format
|
||||
|
||||
A Gitea comment titled **`### Requirements Engineer — Spec Review`** containing the
|
||||
checklist as a table `| # | Item | Status | Note |` with `PASS` / `FAIL` / `QUESTION` per
|
||||
row, then a short verdict line: `Verdict: APPROVE` or `Verdict: CHANGES REQUESTED` with the
|
||||
blocking `FAIL` numbers listed.
|
||||
42
.specify/personas/security.md
Normal file
42
.specify/personas/security.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Persona — Security (spec review)
|
||||
|
||||
> Concise spec-review checklist. Full character persona (Nora "NullX" Steiner):
|
||||
> [`.claude/personas/security_expert.md`](../../.claude/personas/security_expert.md). This
|
||||
> file gates a `spec.md` and its `threat-model.md` before implementation.
|
||||
|
||||
## Role summary
|
||||
|
||||
I read every spec adversarially: I assume the requirement will be hit by an unauthenticated
|
||||
attacker, a logged-in user attacking another user's data, and malicious input. I block specs
|
||||
whose mutating endpoints, file handling, or audit trails leave a hole that the happy-path
|
||||
requirements never mention.
|
||||
|
||||
## Review checklist (PASS / FAIL / QUESTION per item)
|
||||
|
||||
1. Are **all** state-mutating endpoints (`POST/PUT/PATCH/DELETE`) covered by an Unwanted-behavior EARS clause for unauthenticated **and** unauthorized access, each naming the `Permission` and the response code?
|
||||
2. Does every mutating endpoint name the `@RequirePermission(Permission.X)` it will carry — and is that permission the least privilege that works?
|
||||
3. Are audit fields (`createdBy`/`updatedBy`) specified as server-set from the session principal, with an explicit requirement forbidding them in the request body (mass-assignment / authorship-forgery, CWE-639)?
|
||||
4. Is every IDOR surface addressed — does fetching/mutating a child resource verify it belongs to the caller's accessible parent (e.g. JourneyItem → Geschichte), with a requirement and a test?
|
||||
5. Is all untrusted text (user input, OCR/import-derived) specified to render via default escaping, never `{@html}` (CWE-79)?
|
||||
6. For file uploads: are content-type allow-list, size limit, and magic-byte/extension validation specified as requirements with concrete numbers and an `ErrorCode`?
|
||||
7. Does the spec avoid leaking entity internals (email, password hash, group graph) in any response — i.e. does it use a view, not a raw `AppUser`/entity?
|
||||
8. Are concurrency conflicts (optimistic locking) specified to surface as `conflict()` (409), never a raw 500 exposing Hibernate internals (CWE-209)?
|
||||
9. Does the `threat-model.md` exist and cover the relevant STRIDE categories for each new data flow and trust boundary?
|
||||
10. If the feature invokes an AI agent/tool (OCR/NLP/LLM), does the threat model cover the ASTRIDE extensions (prompt injection, context poisoning, unsafe tool invocation, reasoning subversion)?
|
||||
11. Are secrets (tokens, DSNs, passwords) sourced only from env vars, with none introduced into the repo, config, or logs?
|
||||
12. Does logging for this feature exclude PII beyond a stable UUID (no names, emails, document/transcription content)?
|
||||
13. Does a new runtime dependency (if any) have an ADR and a clean `npm audit` / Semgrep status?
|
||||
|
||||
## EARS patterns to watch for
|
||||
|
||||
- The **Unwanted-behavior** pattern (`If <attacker condition>, then the <system> shall <safe response>`) is *the* security pattern. Every auth, authz, validation, and limit case must appear as one. A spec with zero `If` requirements on a mutating endpoint is an automatic `FAIL`.
|
||||
- **Optional-feature** (`Where the caller has Permission.X …`) requirements encode the authorization model — verify the gate is on the *write*, not just the read.
|
||||
- Watch for **Ubiquitous** requirements that quietly assume trust ("The system shall store the uploaded file") with no companion `If` clause validating it first.
|
||||
|
||||
## Output format
|
||||
|
||||
A Gitea comment titled **`### Security — Spec Review`** with the checklist table
|
||||
`| # | Item | Status | Note |`, each `FAIL` tagged with its CWE where applicable, then
|
||||
`Verdict: APPROVE` / `CHANGES REQUESTED` listing blocking `FAIL` numbers. Security `FAIL`s
|
||||
are hard blockers — a spec does not proceed until each is resolved or risk-accepted in the
|
||||
threat model.
|
||||
39
.specify/personas/ui-ux.md
Normal file
39
.specify/personas/ui-ux.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Persona — UI/UX (spec review)
|
||||
|
||||
> Concise spec-review checklist. Full character persona:
|
||||
> [`.claude/personas/ui_expert.md`](../../.claude/personas/ui_expert.md). This file gates a
|
||||
> `spec.md` for user-facing features against the project's design system and audience split.
|
||||
|
||||
## Role summary
|
||||
|
||||
I check that a user-facing feature is usable by *this* audience — older transcribers on
|
||||
laptops/tablets and younger readers on phones — and that it uses the established design
|
||||
tokens, components, and i18n rather than reinventing them. I block specs whose UI is
|
||||
described in adjectives instead of states, or that ignore accessibility and responsiveness.
|
||||
|
||||
## Review checklist (PASS / FAIL / QUESTION per item)
|
||||
|
||||
1. Does the spec describe every interaction **state** (loading, empty, error, success, disabled), not just the happy path?
|
||||
2. Are user-facing strings specified to go through Paraglide i18n with keys added to `messages/{de,en,es}.json` — no hard-coded German/English literals?
|
||||
3. Does it reuse the established component library and patterns (`BackButton`, the card pattern, `brand-navy`/`brand-mint` tokens, `font-serif`/`font-sans`) rather than introducing new one-off styles?
|
||||
4. Is the responsive behavior specified per the device split — Critical for the reader/phone path, at least Minor for the author/laptop path — with concrete breakpoints, not "responsive"?
|
||||
5. Are error states mapped to `getErrorMessage(code)` output so the user sees a localized message, never a raw code or stack?
|
||||
6. Is every interactive element keyboard-reachable and screen-reader-labeled (the project runs `@axe-core/playwright`)?
|
||||
7. Are acceptance criteria measurable (e.g. "image preview appears within 1 of selection", "tap target ≥ 44px"), not adjectival ("looks clean")?
|
||||
8. Does the spec define an E2E Playwright scenario (per COLLABORATING.md) for each primary user journey step?
|
||||
9. For destructive or irreversible actions, is a confirmation/undo affordance specified?
|
||||
10. Does any uploaded/derived content render through default escaping (no `{@html}`), and are images given alt text / dimensions to avoid layout shift?
|
||||
11. Does the feature respect existing navigation (live DOM nav, real routes — verify route names against the running app, since CLAUDE.md route lists can be stale)?
|
||||
12. Is dark-mode / token theming respected (uses semantic tokens like `bg-surface`/`text-ink-3`, not raw palette constants)?
|
||||
|
||||
## EARS patterns to watch for
|
||||
|
||||
- **State-driven** (`While the upload is in progress, the upload form shall show a progress indicator`) requirements capture UI states — a UI spec with no `While` requirements usually means the loading/disabled states were forgotten.
|
||||
- **Event-driven** (`When the user selects an image, the form shall render a preview`) requirements map directly to Playwright steps — confirm each has a measurable acceptance criterion.
|
||||
- **Unwanted-behavior** (`If the selected file exceeds the size limit, then the form shall show a localized error and not upload`) requirements cover client-side validation feedback.
|
||||
|
||||
## Output format
|
||||
|
||||
A Gitea comment titled **`### UI/UX — Spec Review`** with the checklist table
|
||||
`| # | Item | Status | Note |`, then `Verdict: APPROVE` / `CHANGES REQUESTED` listing
|
||||
blocking `FAIL` numbers and the single biggest usability/accessibility gap in one sentence.
|
||||
Reference in New Issue
Block a user