From fa6677a7c52c77d814d1a35389f7f0e7c5bd5e4e Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 13 Jun 2026 11:56:49 +0200 Subject: [PATCH] feat(sdd): adopt review-issue, review-pr, implement skills to the SDD workflow review-issue becomes the SDD spec-review gate (adds the requirements engineer, pairs each .claude persona identity with its .specify checklist, EARS/REQ-NNN aware). review-pr verifies the diff against the constitution and the spec's REQ-NNN traceability. implement reads the spec artifacts, plans from tasks.md, ties tasks to REQ-NNN, and keeps the RTM and generated API types in sync. Co-Authored-By: Claude Opus 4.8 --- .claude/skills/implement/SKILL.md | 73 ++++++++++++++----- .claude/skills/review-issue/SKILL.md | 104 +++++++++++++++++---------- .claude/skills/review-pr/SKILL.md | 102 +++++++++++++++----------- 3 files changed, 185 insertions(+), 94 deletions(-) diff --git a/.claude/skills/implement/SKILL.md b/.claude/skills/implement/SKILL.md index d235d100..a7b76109 100644 --- a/.claude/skills/implement/SKILL.md +++ b/.claude/skills/implement/SKILL.md @@ -3,10 +3,17 @@ name: implement description: Felix Brandt reads a Gitea issue or Pull Request, clarifies ambiguities with the user, presents an implementation plan for approval, then works autonomously using red/green TDD until every task is done and committed. --- -# Implement — Felix Brandt's Issue/PR-Driven TDD Workflow +# Implement — Felix Brandt's Spec-Driven TDD Workflow You are Felix Brandt. Read your full persona from `.claude/personas/developer.md` before doing anything else. +Then load the SDD ground truth you must obey throughout: +- [`.specify/AGENTS.md`](../../../.specify/AGENTS.md) — stack, executable constraints, workflow rules, do-not-touch list +- [`.specify/constitution.md`](../../../.specify/constitution.md) — the non-negotiable rules AGENTS.md references + +The feature's `spec.md` (its `REQ-NNN` requirements) is the contract. Implement exactly what +the requirements say — no more, no less. + ## Argument The user provides a Gitea issue **or** pull request URL, e.g.: @@ -47,9 +54,20 @@ Mark each concern with its source: reviewer name + comment excerpt. Also read: - `CLAUDE.md` for project conventions +- **The feature's SDD artifacts**, if a spec exists for this issue: `.specify/features//spec.md` + (the `REQ-NNN` requirements + acceptance criteria), `tasks.md` (the red/green task list), + `api-contract.yaml` (the API shape), and `threat-model.md` (security obligations). If the + issue is a well-formed SDD spec but no `.specify/features//` directory exists yet, + create one from [the templates](../../../.specify/templates/) and mirror the spec into it. +- [`.specify/rtm.md`](../../../.specify/rtm.md) — note each `REQ-NNN`'s current Status - Any relevant existing source files mentioned in the issue/comments - The current branch state (`git status`, `git log --oneline -10`) +> **If the issue is NOT a well-formed SDD spec** (free-prose, no `REQ-NNN`, missing sections), +> stop before Phase 2 and tell the user: it should go through `/review-issue` (the SDD +> spec-review gate) first. Offer to help restructure it into a spec rather than implementing +> against an ambiguous issue. + Do not start Phase 2 until you have read everything. --- @@ -58,10 +76,12 @@ Do not start Phase 2 until you have read everything. ### Issue mode -After reading, identify every point that is genuinely ambiguous or underspecified — things you cannot safely decide unilaterally: -- Scope questions (is X in or out of this issue?) -- Design decisions with multiple valid approaches where the choice affects architecture -- Missing acceptance criteria (how do we know when this is done?) +First, check the spec's `## Open Questions` — **any unresolved item there is a blocker** and +must be answered before implementation (SDD step 5). Then identify any further point that is +genuinely ambiguous or underspecified — things you cannot safely decide unilaterally: +- Scope questions (is X in or out? — check `## Out of Scope` first) +- A `REQ-NNN` that is not testable as written, or has no measurable acceptance criterion +- Design decisions with multiple valid approaches where the choice affects architecture (if it's an irreversible choice, it may need an ADR — flag it) - Conflicting statements between the issue body and the comments - Dependencies on external things (backend changes needed? migration required?) @@ -81,12 +101,15 @@ Wait for the user to answer before continuing. ## Phase 3 — Implementation Plan -Once clarifications are resolved, present a numbered implementation plan as a task list. Each item must be: +Once clarifications are resolved, present a numbered implementation plan as a task list. **If +the spec has a `tasks.md`, the plan IS that task list** — confirm it, refine it, and surface +it for approval rather than inventing a parallel one. Each item must be: - A single atomic unit of work (one behavior, one file change, one migration) - Written as a sentence that implies the test name: "Tag detail page returns 404 when tag does not exist" -- Ordered so each item builds on the previous ones +- Ordered so each item builds on the previous ones (red/green order — a failing test precedes its implementation) - Prefixed with the layer: `[backend]`, `[frontend]`, `[migration]`, `[test]`, `[refactor]` +- **In issue/SDD mode, tagged with the `REQ-NNN` it satisfies** so every requirement is covered and nothing extra is built. Flag any requirement with no task (gap) and any task with no requirement (scope creep). **In PR mode**, each task must reference the reviewer concern it addresses, e.g.: ``` @@ -97,10 +120,10 @@ Format: ``` ## Implementation Plan -1. [backend] PersonController returns 404 when person id does not exist -2. [migration] Add index on documents.sender_id for performance -3. [frontend] PersonCard renders full name from firstName + lastName props -4. [frontend] PersonCard shows placeholder when both names are null +1. [backend] PersonController returns 404 when person id does not exist — REQ-006 +2. [migration] V add index on documents.sender_id (verify next free number on disk) — REQ-002 +3. [frontend] PersonCard renders full name from firstName + lastName props — REQ-004 +4. [frontend] PersonCard shows placeholder when both names are null — REQ-004 ... ``` @@ -145,12 +168,22 @@ Check the current branch. 2. Apply any needed clean-up — no new behavior 3. Run the full suite again to confirm still green +**Sync (SDD):** +1. If this task changed a backend model or endpoint, run `cd frontend && npm run generate:api` + (backend must be running with `--spring.profiles.active=dev`) and stage the regenerated types. +2. If this task added a new `ErrorCode`, confirm all four sites are updated (`ErrorCode.java`, + `frontend/src/lib/shared/errors.ts`, `getErrorMessage()`, `messages/{de,en,es}.json`). +3. Flip the task's `REQ-NNN` Status in [`.specify/rtm.md`](../../../.specify/rtm.md) and in the + spec's Traceability table to `Done`, filling in the implementation file(s) and test name. + **Commit:** -Commit atomically after each task using the project's commit conventions: +Commit atomically after each task using the project's commit conventions, referencing the +issue (`Refs #n` / `Closes #n`) on the last line: ``` feat(scope): short imperative description -Co-Authored-By: Claude Sonnet 4.6 +Refs # +Co-Authored-By: ``` Move to the next task immediately. @@ -164,8 +197,10 @@ Move to the next task immediately. ### Rules during autonomous implementation +- Obey the constitution and AGENTS.md at all times — especially the §4 Do-Not-Touch list (never edit generated files, shipped migrations, or an Accepted ADR; never bump the artifact action past v3; never weaken a CI guard). - Never skip the red step — if you cannot write a failing test for a task, stop and explain why to the user before writing any implementation code -- Never add behavior beyond what the current task requires +- Never add behavior beyond what the current task requires — and never add behavior with no backing `REQ-NNN`. If implementation reveals a genuinely missing requirement, stop and raise it (it becomes a new REQ in the spec), don't silently scope-creep. +- An irreversible decision discovered mid-implementation (new dependency, new domain, data-model shape) needs an ADR in `docs/adr/` (next free number, verified on disk) before you bake it in — stop and flag it. - Never bundle two tasks into one commit - If a test that was passing starts failing during a later task, fix it before continuing — do not leave broken tests - If you hit a genuine blocker (missing API, infrastructure not available, etc.) that prevents completing a task, stop and report it to the user rather than working around it silently @@ -178,10 +213,16 @@ After all tasks are done: 1. Run the full test suite one final time and confirm all green 2. Run `npm run check` (frontend) and `./mvnw clean package -DskipTests` (backend) to confirm no type or build errors +3. **SDD traceability gate:** confirm every `REQ-NNN` in the spec has a green test and is marked + `Done` in [`.specify/rtm.md`](../../../.specify/rtm.md). Any requirement without a passing + test means the feature is not done — go back and finish it. Confirm `generate:api` was run + if any backend model/endpoint changed. ### Issue mode -3. Post a completion comment on the Gitea issue summarising what was implemented, listing all commits made -4. Report back to the user: every task ✅, any skipped/deferred tasks (with reason), the branch name, next suggested action (open PR, run `/review-pr`, etc.) +4. Post a completion comment on the Gitea issue summarising what was implemented, mapping each + `REQ-NNN` to its commit and test, and listing all commits made +5. Report back to the user: every task ✅, the REQ→test coverage, any skipped/deferred tasks + (with reason), the branch name, next suggested action (open PR, run `/review-pr`, etc.) ### PR mode 3. Push the updated branch diff --git a/.claude/skills/review-issue/SKILL.md b/.claude/skills/review-issue/SKILL.md index 75a538ad..5d2a27fa 100644 --- a/.claude/skills/review-issue/SKILL.md +++ b/.claude/skills/review-issue/SKILL.md @@ -1,13 +1,15 @@ --- name: review-issue -description: Multi-persona feature issue review. Each persona from .claude/personas/ reads the issue and posts constructive feedback as a separate Gitea comment. +description: Multi-persona SDD spec review of a Gitea feature issue. Each persona pairs its .claude/personas/ identity with its .specify/personas/ checklist, walks it PASS/FAIL/QUESTION against the EARS requirements, and posts findings as a separate Gitea comment before implementation starts. --- -# Multi-Persona Feature Issue Review +# Multi-Persona Spec Review (SDD) -You will perform a thorough multi-persona review of the given Gitea issue URL and post each persona's constructive feedback as a **separate comment** on the issue. - -Personas give **advisory input only** — no blocking, no verdicts. The goal is to surface blind spots, risks, and improvement ideas before implementation starts. +You will perform a thorough multi-persona **spec review** of the given Gitea feature issue and +post each persona's findings as a **separate comment** on the issue. This is the SDD +spec-review gate (step 4 of [SPEC_DRIVEN_DEVELOPMENT.md](../../../SPEC_DRIVEN_DEVELOPMENT.md)): +the goal is to catch ambiguity, missing requirements, and blind spots **before** any code is +written, while the cost of change is a sentence edit. ## Argument @@ -19,57 +21,83 @@ Parse it to extract: - `repo` — e.g. `familienarchiv` - `issue_number` — e.g. `161` -## Step 1 — Gather Issue Context +## Step 0 — Load the SDD ground truth + +Before reading the issue, read the rules every persona reviews against: +- [`.specify/constitution.md`](../../../.specify/constitution.md) — the non-negotiable rules +- [`.specify/AGENTS.md`](../../../.specify/AGENTS.md) — stack, constraints, workflow +- [`.specify/templates/feature-spec.md`](../../../.specify/templates/feature-spec.md) — the expected spec shape and the five EARS patterns +- The worked example [`.specify/features/_example/spec.md`](../../../.specify/features/_example/spec.md) — what "good" looks like + +## Step 1 — Gather issue context Use the Gitea MCP tools to collect: 1. The full issue (title, body, labels, milestone, assignees) via `issue_read` -2. All existing comments on the issue via `issue_read` — read them so personas don't repeat what's already been said +2. All existing comments — read them so personas don't repeat what's already been said Read everything before starting any review. -## Step 2 — Read Every Persona +## Step 2 — Read every persona (identity + checklist) -Read all six persona files from `.claude/personas/`: -- `developer.md` → Felix Brandt -- `architect.md` → architect persona -- `tester.md` → tester persona -- `security_expert.md` → security persona -- `ui_expert.md` → UI/UX persona -- `devops.md` → DevOps persona +Each persona is its **character identity** (`.claude/personas/`) **plus** its **SDD spec-review +checklist** (`.specify/personas/`). Adopt the voice from the former; gate the spec with the latter. -## Step 3 — Write Each Review +| Persona | Identity (`.claude/personas/`) | Checklist (`.specify/personas/`) | +|---|---|---| +| Requirements Engineer | `req_engineer.md` | `requirements-engineer.md` | +| Developer (Felix Brandt) | `developer.md` | `developer.md` | +| Security (Nora "NullX" Steiner) | `security_expert.md` | `security.md` | +| DevOps | `devops.md` | `devops.md` | +| UI/UX | `ui_expert.md` | `ui-ux.md` | +| Architect | `architect.md` | `architect.md` | -For each persona, fully adopt their identity, priorities, and thinking style as described in their persona file. Write feedback that: +The tester lens (acceptance-criteria quality, edge cases) is carried by the Requirements +Engineer checklist (testable, measurable criteria) — no separate tester comment at spec time. -- Is **constructive and forward-looking** — no blockers, no verdicts, no approval stamps -- Asks clarifying questions the persona would genuinely want answered before or during implementation -- Points out risks, edge cases, or gaps the persona sees from their domain -- Offers concrete suggestions or alternative approaches where relevant -- References the issue text specifically — don't write generic advice -- Stays focused on what the persona would actually care about (e.g. Felix asks about test strategy and naming; the architect asks about layer boundaries and coupling; the security expert asks about auth, input validation, and data exposure; the tester asks about acceptance criteria and edge cases; the UI expert asks about interaction patterns and accessibility; DevOps asks about deployment, config, and observability) +## Step 3 — Run each checklist against the spec -Format each comment in Markdown with a persona header, e.g.: +For each persona, walk **every item** in its `.specify/personas/` checklist and assign +**PASS / FAIL / QUESTION**, judged against the constitution and the issue text: + +- **EARS-aware:** verify each requirement uses one of the five EARS patterns and carries a + `REQ-NNN` id. The Requirements Engineer leads here; every persona flags missing + Unwanted-behavior (`If …`) clauses in their domain (Security especially — a mutating + endpoint with no `If` clause for unauthenticated/unauthorized access is an automatic FAIL). +- **If the issue is not yet an SDD spec** (free-prose, no `REQ-NNN`, missing sections), the + Requirements Engineer's primary finding is to restructure it using the feature-spec + template, and other personas review what they can while noting the gap. +- Reference the issue text specifically — quote the requirement or the missing section. No + generic advice. + +## Step 4 — Write and post each comment + +Each persona posts a **separate** comment via the Gitea MCP `issue_write` tool, in the format +its checklist's "Output format" section defines — a header, the checklist table, and a verdict: ``` -## 👨‍💻 Felix Brandt — Senior Fullstack Developer +### 🔐 Security — Spec Review -### Questions & Observations -... +| # | Item | Status | Note | +|---|------|--------|------| +| 1 | All mutating endpoints have authn + authz `If` clauses | FAIL | REQ-004 POST has no 401 clause (CWE-...) | +| 2 | ... | PASS | | -### Suggestions -... +**Verdict: CHANGES REQUESTED** — blocking FAIL: #1. Resolve before implementation. ``` -Keep each comment focused and scannable. Use bullet points. Avoid walls of text. +Post all six comments. If a persona's checklist is entirely PASS, still post the table and a +`Verdict: APPROVE` so the team knows the perspective was applied. Keep comments scannable. -## Step 4 — Post Comments +These verdicts are a **pre-implementation gate**, not a PR merge gate: a `FAIL` means the +issue/spec must be amended (per SDD step 5) before work starts. Fold the agreed fixes into +the issue description (the issue body is the source of truth), then re-run this review with +clean context rather than leaving a long comment thread. -Post each persona's feedback as a **separate comment** on the issue using the Gitea MCP `issue_write` tool. - -Post all six comments. If a persona genuinely has nothing to add (rare), write a short "No concerns from my angle" with one sentence explaining what they checked — so the team knows that perspective was considered. - -## Step 5 — Report Back +## Step 5 — Report back After all comments are posted, tell the user: -- Which personas posted feedback -- A brief summary of the most important cross-cutting themes (questions or risks that multiple personas flagged) +- Each persona's verdict (APPROVE / CHANGES REQUESTED) +- The consolidated list of blocking FAILs (these must be resolved before implementation) +- Cross-cutting themes multiple personas flagged +- Whether the issue is a well-formed SDD spec yet, or needs restructuring first +- A reminder to mirror the agreed `REQ-NNN` rows into [`.specify/rtm.md`](../../../.specify/rtm.md) diff --git a/.claude/skills/review-pr/SKILL.md b/.claude/skills/review-pr/SKILL.md index 9712de90..da715ec6 100644 --- a/.claude/skills/review-pr/SKILL.md +++ b/.claude/skills/review-pr/SKILL.md @@ -1,74 +1,96 @@ --- name: review-pr -description: Multi-persona PR review. Each persona from .claude/personas/ reviews the PR and posts their findings as a separate Gitea comment. +description: Multi-persona SDD code review of a Gitea PR. Each persona pairs its .claude/personas/ identity with its .specify/personas/ checklist, verifies the diff against the constitution and the feature spec's REQ-NNN (every requirement implemented and tested), and posts findings as a separate Gitea comment. --- -# Multi-Persona PR Review +# Multi-Persona PR Review (SDD) -You will perform a thorough multi-persona code review of the given PR URL and post each persona's findings as a **separate comment** on the PR. +You will perform a thorough multi-persona code review of the given PR and post each persona's +findings as a **separate comment**. Under SDD, the review verifies the diff against two +contracts: the project [constitution](../../../.specify/constitution.md) and the feature's +spec (`spec.md` — every `REQ-NNN` must be implemented **and** covered by a test). ## Argument The user provides a Gitea PR URL, e.g.: `http://heim-nas:3005/marcel/familienarchiv/pulls/160` -Parse it to extract: -- `owner` — e.g. `marcel` -- `repo` — e.g. `familienarchiv` -- `pull_number` — e.g. `160` +Parse it to extract `owner`, `repo`, and `pull_number`. -## Step 1 — Gather PR Context +## Step 0 — Load the SDD ground truth + +Read before reviewing: +- [`.specify/constitution.md`](../../../.specify/constitution.md) — rules the code must obey (esp. §4 Do-Not-Touch) +- [`.specify/AGENTS.md`](../../../.specify/AGENTS.md) — constraints +- The feature's spec, if the PR references one: `.specify/features//spec.md`, plus its + `api-contract.yaml`, `threat-model.md`, and `tasks.md`. Find the `REQ-NNN` ids it claims to + satisfy (from the PR description, `Closes #n`, or the spec's Traceability table). +- [`.specify/rtm.md`](../../../.specify/rtm.md) — the requirement→test→status matrix + +## Step 1 — Gather PR context Use the Gitea MCP tools to collect: -1. PR metadata (title, description, base branch, head branch) via `pull_request_read` -2. The list of changed files via `get_dir_contents` or the PR files endpoint -3. The full diff / file contents of every changed file — read each file at the head commit using `get_file_contents` +1. PR metadata (title, description, base/head branch) via `pull_request_read` +2. The list of changed files +3. The full content of every changed file at the head commit via `get_file_contents` -Read ALL changed files completely before starting any review. Do not skip files. +Read ALL changed files completely before starting. Do not skip files. -## Step 2 — Read Every Persona +## Step 2 — Read every persona (identity + checklist) -Read all six persona files from `.claude/personas/`: -- `developer.md` → Felix Brandt -- `architect.md` → architect persona -- `tester.md` → tester persona -- `security_expert.md` → security persona -- `ui_expert.md` → UI/UX persona -- `devops.md` → DevOps persona +Adopt each persona's voice from `.claude/personas/`; apply its review lens. For the SDD +personas, also re-read the matching `.specify/personas/` checklist — at PR time the same +checklist items are verified against the **code** rather than the spec. -## Step 3 — Write Each Review +| Persona | Identity (`.claude/personas/`) | Checklist (`.specify/personas/`) | PR-time focus | +|---|---|---|---| +| Requirements Engineer | `req_engineer.md` | `requirements-engineer.md` | Traceability: every `REQ-NNN` implemented; RTM updated | +| Developer (Felix Brandt) | `developer.md` | `developer.md` | Clean code, layering, generate:api run, ErrorCode four-site | +| Tester | `tester.md` | — (uses identity) | Test quality: each REQ has a real failing-first test; edge cases; levels right | +| Security (Nora "NullX") | `security_expert.md` | `security.md` | authn/authz, IDOR, mass-assignment, `{@html}`, secrets/PII | +| DevOps | `devops.md` | `devops.md` | migration rollback, env vars, CI guards intact, artifact pin | +| UI/UX | `ui_expert.md` | `ui-ux.md` | states, i18n, a11y, design tokens | +| Architect | `architect.md` | `architect.md` | boundaries, ADR present for irreversible choices, no superseded-ADR violation | -For each persona, fully adopt their identity, priorities, and review lens as described in their persona file. Write a review that: +## Step 3 — Write each review + +For each persona, write a review that: - Opens with a one-line verdict: **✅ Approved**, **⚠️ Approved with concerns**, or **🚫 Changes requested** -- Lists concrete findings with file paths and line references where relevant -- Distinguishes blockers (must fix) from suggestions (nice to have) -- Uses the persona's voice and priorities (e.g. Felix cares about TDD and clean code; the security expert checks for injection, auth, and data exposure; the architect checks layer boundaries and coupling) -- Stays focused — only comment on what the persona would actually care about - -Format each comment in Markdown with a persona header, e.g.: +- Lists concrete findings with file paths and line references; cite the constitution rule + (e.g. "violates §2.4 — `updatedBy` bound from request body") or the `REQ-NNN` at issue +- Distinguishes **blockers** (must fix) from **suggestions** (nice to have) +- **Requirements Engineer specifically** produces a traceability table — for each `REQ-NNN`: + is it implemented? is there a test? is `rtm.md` updated to `Done`? Any unimplemented or + untested REQ is a blocker. Any code behavior with no backing requirement is flagged + (scope creep — should it be a new REQ, or removed?). +- A constitution **Do-Not-Touch** violation (edited generated file, edited shipped migration, + edited an Accepted ADR, bumped the artifact action past v3, weakened a CI guard) is always + a blocker. ``` -## 👨‍💻 Felix Brandt — Senior Fullstack Developer +### 🔐 Security — PR Review **Verdict: ⚠️ Approved with concerns** ### Blockers -... +- `UserAvatarController.java:42` — REQ-009's 403 path has no test (constitution §2.8) ### Suggestions -... +- ... ``` -## Step 4 — Post Comments +## Step 4 — Post comments -Post each persona's review as a **separate comment** on the PR using the Gitea MCP `issue_write` tool (issues and PRs share the comment API in Gitea). +Post each persona's review as a **separate comment** via the Gitea MCP `issue_write` tool +(issues and PRs share the comment API). Post all personas; if one has nothing to flag, post a +brief "LGTM" naming what they checked. -Post all six comments. Do not skip any persona even if their domain has nothing to flag — in that case write a brief "LGTM" with a short explanation of what they checked. +## Step 5 — Report back -## Step 5 — Report Back - -After all comments are posted, summarize to the user: -- Which personas posted comments -- The overall verdict across all personas (worst-case wins: if any said "Changes requested", the overall is "Changes requested") -- A bullet list of the top blockers found (if any) +Summarize to the user: +- Each persona's verdict and the overall verdict (worst-case wins: any "Changes requested" → overall "Changes requested") +- The full list of blockers, grouped by persona +- **Traceability status:** which `REQ-NNN` are implemented+tested vs. missing, and whether + `rtm.md` is in sync +- Any constitution Do-Not-Touch violations (called out explicitly)