Template
Security was one section among seven in the QA Engineer. That is structurally weak: the agent that wants to recommend "production ready: YES" also judged security, and as a subsection it competes for attention with acceptance criteria, regression and test authoring. The new agent: - has no Write/Edit tools, so "never fix what you find" is enforced by the tool list rather than by instruction — it cannot launder a finding into a patch - reports PASS / FAIL / UNVERIFIED, and UNVERIFIED must reach the spec; an audit that omits what it could not check reads as all-clear - audits against .claude/rules/security.md, the same standard the developer agents are held to, so every mandatory item gets a verdict - holds a veto: BLOCK stops /deploy regardless of functional test results /qa now runs both agents in parallel and keeps the verdicts separate. The QA Engineer is explicitly forbidden from speaking for security.
666 lines
26 KiB
Markdown
666 lines
26 KiB
Markdown
# AI Coding Starter Kit
|
|
|
|
Next.js-Vorlage mit einem durchgängigen KI-Workflow für [Claude Code](https://docs.anthropic.com/en/docs/claude-code).
|
|
Neun Skills führen von der Idee bis zum laufenden Deployment. Jede Phase schreibt ihr
|
|
Ergebnis in die Feature-Spec — der Projektstand liegt in Dateien, nicht im Chatverlauf.
|
|
|
|
Genau das macht die Vorlage fürs Vibe-Coden brauchbar: Du musst dich nicht erinnern,
|
|
wo du stehst. `/help` liest es aus den Dateien ab, und nach einem Kontextverlust
|
|
liest sich jede Skill selbst wieder ein.
|
|
|
|
---
|
|
|
|
## Inhalt
|
|
|
|
- [Schnellstart](#schnellstart)
|
|
- [Der Workflow im Überblick](#der-workflow-im-überblick)
|
|
- [Die neun Skills](#die-neun-skills)
|
|
- [Die vier Sub-Agents](#die-vier-sub-agents)
|
|
- [Die vier Rules](#die-vier-rules)
|
|
- [Kontext-Ebenen](#kontext-ebenen)
|
|
- [Das Feature-System](#das-feature-system)
|
|
- [Tech-Stack](#tech-stack)
|
|
- [Authentifizierung: Passkeys](#authentifizierung-passkeys)
|
|
- [Tests](#tests)
|
|
- [Deployment](#deployment)
|
|
- [Kommandos](#kommandos)
|
|
- [Dateibaum](#dateibaum)
|
|
- [Stolperfallen](#stolperfallen)
|
|
- [Anpassen](#anpassen)
|
|
|
|
---
|
|
|
|
## Schnellstart
|
|
|
|
```bash
|
|
git clone <repo-url> mein-projekt
|
|
cd mein-projekt
|
|
npm install
|
|
cp .env.example .env
|
|
npx playwright install chromium # einmalig pro Rechner, für E2E-Tests
|
|
npm run dev
|
|
```
|
|
|
|
Dann in Claude Code:
|
|
|
|
```
|
|
/init Ich will eine Aufgabenverwaltung für kleine Teams bauen
|
|
```
|
|
|
|
---
|
|
|
|
## Der Workflow im Überblick
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
subgraph P["Planung — inline mit dir im Gespräch"]
|
|
direction LR
|
|
INIT["/init<br/><i>PRD + Feature-Map</i>"]
|
|
SPEC["/write-spec<br/><i>User Stories, AC</i>"]
|
|
ARCH["/architecture<br/><i>Tech Design</i>"]
|
|
end
|
|
|
|
subgraph B["Bauen — delegiert an Sub-Agents"]
|
|
direction LR
|
|
FE["/frontend<br/><i>UI</i>"]
|
|
BE["/backend<br/><i>API + Daten</i>"]
|
|
QA["/qa<br/><i>Tests + Audit</i>"]
|
|
end
|
|
|
|
DEP["/deploy<br/><i>Docker-Stack</i>"]
|
|
|
|
INIT --> SPEC --> ARCH --> FE
|
|
FE -->|"braucht Server"| BE --> QA
|
|
FE -->|"nur Frontend"| QA
|
|
QA -->|"keine Critical/High"| DEP
|
|
QA -.->|"Bugs gefunden"| FE
|
|
QA -.->|"Bugs gefunden"| BE
|
|
|
|
REF["/refine PROJ-X"] -.->|"Spec überarbeiten"| SPEC
|
|
HELP["/help"] -.->|"Wo stehe ich?"| P
|
|
```
|
|
|
|
Übergaben sind **immer** von dir ausgelöst. Keine Skill springt selbstständig weiter,
|
|
jede endet mit einem Freigabe-Checkpoint.
|
|
|
|
### Das Grill-Me-Prinzip
|
|
|
|
`/init`, `/write-spec` und `/refine` interviewen nach denselben Regeln:
|
|
|
|
- Eine Frage pro Runde, niemals eine Liste
|
|
- Immer mit einer Empfehlung — du bestätigst oder korrigierst
|
|
- Dem Gespräch folgen statt einem Skript
|
|
- Erst im Code nachsehen, dann fragen
|
|
- Kein Fragenlimit: Schluss ist, wenn es verstanden ist
|
|
|
|
---
|
|
|
|
## Die neun Skills
|
|
|
|
| # | Skill | Argument | Liest | Schreibt | Ausführung |
|
|
|---|-------|----------|-------|----------|------------|
|
|
| 1 | **`/init`** | Beschreibung der Idee | PRD, INDEX | `docs/PRD.md`, `features/INDEX.md`, ggf. `docs/design-system.md` | inline |
|
|
| 2 | **`/write-spec`** | Feature-Name oder `PROJ-X` | PRD, INDEX, Code | `features/PROJ-X-name.md`, INDEX, PRD | inline |
|
|
| 3 | **`/refine`** | `PROJ-X` | Spec, INDEX, PRD | Spec, INDEX, PRD | inline |
|
|
| 4 | **`/architecture`** | `PROJ-X` | Spec, PRD, Code | Tech Design + Entscheidungslog in der Spec | inline |
|
|
| 5 | **`/frontend`** | `PROJ-X` | Spec, `components/ui/`, Design-System | Komponenten, Seiten, Spec-Notizen | → Sub-Agent |
|
|
| 6 | **`/backend`** | `PROJ-X` | Spec, `app/api/`, `lib/` | Route Handler, Schema, Tests, `.env.example` | → Sub-Agent |
|
|
| 7 | **`/qa`** | `PROJ-X` | Spec, INDEX, Git-Log | QA-Abschnitt in der Spec, Unit- und E2E-Tests | → Sub-Agent |
|
|
| 8 | **`/deploy`** | `PROJ-X` | Spec (QA-Teil), INDEX | Deployment-Abschnitt, Git-Tag | inline |
|
|
| 9 | **`/help`** | optionale Frage | PRD, INDEX, Specs, Code | nichts | inline |
|
|
|
|
### Was jede Skill konkret tut
|
|
|
|
<table>
|
|
<tr><td width="140"><b><code>/init</code></b></td><td>
|
|
Einmalig pro Projekt. Interviewt dich zu Problem, Zielgruppe, MVP-Umfang, Alternativen,
|
|
Rahmenbedingungen, Erfolgsmetriken und Nicht-Zielen. Klärt <b>verbindlich</b> drei Dinge,
|
|
bevor die Feature-Map entsteht: Braucht es dauerhafte Speicherung? Braucht es Nutzerkonten
|
|
(dann wird „Auth-Grundlage" automatisch PROJ-1)? Gibt es ein Design-System? Legt danach
|
|
PRD und priorisierte Feature-Map an — aber <i>keine</i> Spec-Dateien.
|
|
</td></tr>
|
|
<tr><td><b><code>/write-spec</code></b></td><td>
|
|
Macht aus einer Feature-Idee eine testbare Spec. Drei Einstiege: Feature steht schon als
|
|
„Roadmap" im INDEX, Feature fehlt dort noch (wird ergänzt), oder es hat bereits eine Spec
|
|
(verweist auf <code>/refine</code>). Füllt neben User Stories und Acceptance Criteria auch
|
|
Out of Scope, Produkt-Entscheidungen und Offene Fragen.
|
|
</td></tr>
|
|
<tr><td><b><code>/refine</code></b></td><td>
|
|
Öffnet eine bestehende Spec. Fragt zuerst „Was bringt dich zurück zu dieser Spec?" und
|
|
wählt danach den Pfad: <b>1)</b> Scope hat sich geändert, <b>2)</b> die Umsetzung hat eine
|
|
Lücke aufgedeckt, <b>3)</b> das Feature gehört grundsätzlich hinterfragt — bis hin zum
|
|
Aufsplitten in zwei Specs. Schließt erledigte Offene Fragen mit Datum.
|
|
</td></tr>
|
|
<tr><td><b><code>/architecture</code></b></td><td>
|
|
Übersetzt die Spec in ein Design, das auch ohne Technikhintergrund lesbar ist:
|
|
Komponentenbaum, Datenmodell in Worten, begründete Tech-Entscheidungen, Paketliste.
|
|
<b>Schreibt bewusst keinen Code</b> — kein SQL, kein TypeScript. Trifft hier die
|
|
Datenbank-Entscheidung, die die Vorlage offenlässt.
|
|
</td></tr>
|
|
<tr><td><b><code>/frontend</code></b></td><td>
|
|
Klärt Designfragen mit dir (oder liest <code>docs/design-system.md</code>), setzt den Status
|
|
auf „In Progress" und übergibt an den Frontend-Sub-Agent. Präsentiert dessen Bericht,
|
|
holt dein Urteil im Browser ein und iteriert im selben Sub-Agent-Kontext weiter.
|
|
</td></tr>
|
|
<tr><td><b><code>/backend</code></b></td><td>
|
|
Klärt Rechte, Nebenläufigkeit, Validierung und Rate-Limiting, dann Übergabe an den
|
|
Backend-Sub-Agent. Stoppt, wenn im Tech Design noch kein Daten-Layer steht — die
|
|
Entscheidung gehört zu <code>/architecture</code>, nicht hierher.
|
|
</td></tr>
|
|
<tr><td><b><code>/qa</code></b></td><td>
|
|
Setzt „In Review", übergibt an den QA-Sub-Agent, schreibt dessen Ergebnis als
|
|
QA-Abschnitt in die Spec und priorisiert die Bugs mit dir. Setzt bei sauberem Lauf
|
|
„Approved", sonst bleibt „In Review".
|
|
</td></tr>
|
|
<tr><td><b><code>/deploy</code></b></td><td>
|
|
Blockiert ohne QA-Abschnitt oder bei offenen Critical/High-Bugs. Fährt Lint, Typecheck,
|
|
Test und Build als Pre-Flight, führt beim ersten Mal durch die Servereinrichtung und
|
|
danach durch <code>./scripts/update.sh</code>. Setzt Status, Tag und Deployment-Notiz.
|
|
</td></tr>
|
|
<tr><td><b><code>/help</code></b></td><td>
|
|
Liest PRD, INDEX und Spec-Abschnitte und sagt dir, wo das Projekt steht und was als
|
|
Nächstes dran ist — mit dem exakten Kommando. Widersprechen sich Dateien und
|
|
Status-Spalte, gewinnen die Dateien und es sagt dir das.
|
|
</td></tr>
|
|
</table>
|
|
|
|
---
|
|
|
|
## Die vier Sub-Agents
|
|
|
|
`/frontend`, `/backend` und `/qa` führen Interview und Review mit dir im Hauptgespräch
|
|
und geben die eigentliche Dateiarbeit ab. Der Sub-Agent arbeitet in eigenem Kontext und
|
|
liefert einen kompakten Bericht zurück statt der vollen Erzählung.
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
actor Du
|
|
participant S as Skill<br/>(Hauptgespräch)
|
|
participant A as Sub-Agent<br/>(eigener Kontext)
|
|
participant F as Dateien
|
|
|
|
Du->>S: /frontend PROJ-3
|
|
S->>F: Spec + INDEX lesen
|
|
S->>Du: Designfragen, je eine mit Empfehlung
|
|
Du-->>S: Antworten
|
|
S->>F: Status → In Progress
|
|
S->>A: Auftrag: Spec, Designentscheidungen,<br/>Komponentenliste
|
|
A->>F: Komponenten schreiben,<br/>lint / typecheck / build
|
|
A-->>S: Bericht: Dateien, Abweichungen,<br/>offene Fragen
|
|
S->>Du: Bericht + "Schau es dir an"
|
|
Du-->>S: Feedback
|
|
S->>A: Nachbesserung (gleicher Kontext)
|
|
S->>F: Spec-Notizen + Status
|
|
S->>Du: Handoff → /backend oder /qa
|
|
```
|
|
|
|
| Agent | Datei | Werkzeuge | Aufgabe | Hard Rule |
|
|
|-------|-------|-----------|---------|-----------|
|
|
| **Frontend Developer** | `.claude/agents/frontend-dev.md` | Read, Write, Edit, Bash, Glob, Grep | UI umsetzen | shadcn vor Eigenbau; Lint, Typecheck und Build müssen grün sein |
|
|
| **Backend Developer** | `.claude/agents/backend-dev.md` | Read, Write, Edit, Bash, Glob, Grep | Routen, Daten, Auth | **Stoppt und meldet zurück**, wenn der Daten-Layer noch nicht entschieden ist |
|
|
| **QA Engineer** | `.claude/agents/qa-engineer.md` | Read, Write, Edit, Bash, Glob, Grep | Funktion gegen die Spec testen | **Behebt niemals selbst einen Bug**. Spricht **nicht** über Sicherheit |
|
|
| **Security Auditor** | `.claude/agents/security-auditor.md` | Read, Grep, Glob, Bash — **kein Write/Edit** | Sicherheit, adversarial | Hat ein **Veto** über `/deploy`. Findet er Critical oder High: BLOCK |
|
|
|
|
Keiner der vier fasst `features/INDEX.md` an. Tracking gehört der aufrufenden Skill —
|
|
so kann es keine zwei Wahrheiten über den Status geben.
|
|
|
|
### Der Skeptiker
|
|
|
|
`/qa` startet **zwei** Agenten parallel, weil sie verschiedene Fragen beantworten:
|
|
*Tut es, was die Spec verspricht?* und *Ist es sicher genug fürs offene Internet?*
|
|
|
|
Das ist bewusst getrennt. Derselbe Agent, der „Produktionsreif: JA" empfehlen will,
|
|
sollte nicht auch über Sicherheit urteilen — er hat ein Motiv, grün zu melden. Und
|
|
als Unterpunkt neben Acceptance Criteria, Regression und Testschreiben fällt Security
|
|
bei knappem Kontext zuerst hinten runter.
|
|
|
|
Der Security Auditor arbeitet nach drei Prinzipien:
|
|
|
|
- **Unsicher bis zum Beweis des Gegenteils.** Eine plausible Erklärung zählt nicht;
|
|
er muss die Kontrolle im Code gesehen haben und Datei plus Zeile nennen können.
|
|
- **Drei Verdikte, Schweigen ist keines davon:** PASS, FAIL oder **UNGEPRÜFT**.
|
|
Was er nicht prüfen konnte, steht als UNGEPRÜFT in der Spec — ein Audit, das
|
|
Ungeprüftes weglässt, liest sich wie Entwarnung und ist schlimmer als keins.
|
|
- **Kein Write- oder Edit-Werkzeug.** Er *kann* nicht beheben, was er findet. Damit
|
|
ist „niemals selbst fixen" strukturell erzwungen statt nur angewiesen — er kann
|
|
einen Befund nicht still wegpatchen.
|
|
|
|
Seine Checkliste ist `.claude/rules/security.md`, also derselbe Standard, an den sich
|
|
die Entwickler-Agenten halten müssen. Jeder Pflichtpunkt darin braucht ein Verdikt.
|
|
|
|
```mermaid
|
|
flowchart TB
|
|
QA["/qa PROJ-X"] --> A["QA Engineer<br/><i>Tut es, was die Spec verspricht?</i>"]
|
|
QA --> B["Security Auditor<br/><i>Ist es sicher fürs offene Internet?</i>"]
|
|
|
|
A --> AV{"Critical oder<br/>High Bugs?"}
|
|
B --> BV{"Verdikt"}
|
|
|
|
AV -->|ja| R["Status bleibt<br/><b>In Review</b>"]
|
|
BV -->|"BLOCK"| R
|
|
AV -->|nein| G((" "))
|
|
BV -->|"PASS"| G
|
|
|
|
G --> OK["Status <b>Approved</b><br/>→ /deploy"]
|
|
R --> FIX["/frontend oder /backend<br/>beheben, dann erneut /qa"]
|
|
|
|
style B stroke-width:3px
|
|
style R stroke-width:3px
|
|
```
|
|
|
|
Beide müssen grün sein. Ein sauberer Funktionstest hebt ein `BLOCK` nicht auf, und
|
|
`/deploy` verweigert den Start, wenn das Security-Verdikt fehlt oder blockiert.
|
|
|
|
---
|
|
|
|
## Die vier Rules
|
|
|
|
Rules laden sich automatisch, sobald passende Dateien bearbeitet werden. Du musst sie
|
|
nie manuell erwähnen.
|
|
|
|
| Datei | Greift bei | Inhalt |
|
|
|-------|-----------|--------|
|
|
| **`general.md`** | immer aktiv | Feature-Tracking, Status-Flow, Commit-Format, „erst lesen, dann ändern", Write-then-Verify, Human-in-the-Loop, Sprachregel |
|
|
| **`frontend.md`** | `src/components/**`<br/>`src/app/**/page.tsx`<br/>`src/app/**/layout.tsx`<br/>`src/hooks/**` | shadcn-Pflicht, Tailwind-v4-Eigenheiten, semantische Tokens, Server Components, Loading/Error/Empty, Formulare |
|
|
| **`backend.md`** | `src/app/api/**`<br/>`src/lib/**`<br/>`src/server/**`<br/>`drizzle/**`, `prisma/**` | Kein vorgewählter Daten-Layer, Zod-Validierung, Statuscodes, Ownership im Query statt danach, Indizes, Migrationen, Secrets |
|
|
| **`security.md`** | API-Routen, `src/lib/auth*`<br/>`.env*`, `next.config.*`<br/>`Dockerfile`, `compose.yaml` | Passkey-Standard, Autorisierung pro Ressource, Secrets, Security-Header, genehmigungspflichtige Änderungen |
|
|
|
|
---
|
|
|
|
## Kontext-Ebenen
|
|
|
|
Nicht alles wird gleichzeitig geladen — das ist der Grund, warum lange Sessions nicht
|
|
zerfasern.
|
|
|
|
```mermaid
|
|
flowchart TB
|
|
subgraph AUTO["Automatisch"]
|
|
CM["<b>CLAUDE.md</b><br/>jede Session"]
|
|
RU["<b>.claude/rules/</b><br/>sobald passende Dateien<br/>bearbeitet werden"]
|
|
end
|
|
|
|
subgraph DEMAND["Auf Abruf"]
|
|
SK["<b>.claude/skills/</b><br/>beim /kommando"]
|
|
AG["<b>.claude/agents/</b><br/>wenn eine Skill delegiert"]
|
|
SP["<b>features/PROJ-X.md</b><br/>wenn die Skill sie liest"]
|
|
DOC["<b>docs/DEPLOYMENT.md</b><br/>nur wenn referenziert"]
|
|
end
|
|
|
|
CM --> SK
|
|
RU --> SK
|
|
SK --> AG
|
|
SK --> SP
|
|
AG --> SP
|
|
```
|
|
|
|
**Zustand liegt in Dateien, nicht im Gespräch.** Wird der Kontext komprimiert, liest die
|
|
Skill Spec und `git diff` neu ein und macht weiter — ohne Arbeit zu wiederholen oder
|
|
doppelt zu bauen.
|
|
|
|
---
|
|
|
|
## Das Feature-System
|
|
|
|
Alles läuft über `features/INDEX.md` und eine Spec-Datei pro Feature
|
|
(`features/PROJ-X-name.md`). Ein Feature = eine testbare, deploybare Einheit.
|
|
|
|
```mermaid
|
|
stateDiagram-v2
|
|
[*] --> Roadmap: /init
|
|
Roadmap --> Planned: /write-spec
|
|
Planned --> Architected: /architecture
|
|
Architected --> InProgress: /frontend<br/>/backend
|
|
InProgress --> InReview: /qa startet
|
|
InReview --> Approved: keine Critical/High
|
|
InReview --> InProgress: Bugs beheben
|
|
Approved --> Deployed: /deploy
|
|
Deployed --> [*]
|
|
|
|
Planned --> Planned: /refine
|
|
Architected --> Architected: /refine
|
|
```
|
|
|
|
### Aufbau einer Spec
|
|
|
|
Eine Spec wächst über die Phasen hinweg. Vorlage:
|
|
`.claude/skills/write-spec/template.md`
|
|
|
|
| Abschnitt | Gefüllt von | Wozu |
|
|
|-----------|-------------|------|
|
|
| Abhängigkeiten | `/write-spec` | Welche PROJ-X vorher fertig sein müssen |
|
|
| User Stories | `/write-spec` | Als [Rolle] möchte ich [Aktion], um [Ziel] |
|
|
| **Out of Scope** | `/write-spec` | Was bewusst *nicht* gebaut wird — sonst baut es der nächste Durchlauf nach |
|
|
| Acceptance Criteria | `/write-spec` | Angenommen / Wenn / Dann, direkt abtestbar |
|
|
| Edge Cases | `/write-spec` | Nebenläufigkeit, Netzfehler, leere Eingabe, Rechtegrenzen |
|
|
| **Offene Fragen** | `/write-spec` | `- [ ]`, geschlossen in `/refine` mit Datum |
|
|
| **Entscheidungslog** | `/write-spec` + `/architecture` | Produkt- und Tech-Entscheidungen mit Begründung |
|
|
| Tech Design | `/architecture` | Komponentenbaum, Datenmodell, Abhängigkeiten |
|
|
| QA Test Results | `/qa` | Kriterium für Kriterium, Bugs, Security, Urteil |
|
|
| Deployment | `/deploy` | URL, Datum, Commit |
|
|
|
|
Out of Scope und Entscheidungslog sind das Gedächtnis der Vorlage.
|
|
|
|
### Format der Acceptance Criteria
|
|
|
|
```
|
|
- [ ] Angenommen der Nutzer ist angemeldet, wenn er ein leeres Formular abschickt,
|
|
dann erscheint pro Pflichtfeld eine Validierungsmeldung
|
|
```
|
|
|
|
**Sprache:** PRD, Specs und Acceptance Criteria auf Deutsch. Code, Kommentare,
|
|
Commit-Messages und die Skill-Anweisungen selbst auf Englisch.
|
|
|
|
---
|
|
|
|
## Tech-Stack
|
|
|
|
| Ebene | Wahl | Anmerkung |
|
|
|-------|------|-----------|
|
|
| Framework | Next.js 16, App Router | React 19, Server Components als Standard |
|
|
| Sprache | TypeScript `strict` | `@/*` → `src/*` |
|
|
| Styling | Tailwind CSS v4 | CSS-first, **keine `tailwind.config.ts`** |
|
|
| Komponenten | shadcn/ui, Stil `new-york` | 6 vorinstalliert, Rest per CLI |
|
|
| Icons | lucide-react | |
|
|
| Formulare | react-hook-form + Zod | Zod-Schema gilt auch serverseitig |
|
|
| Toasts | sonner | in `layout.tsx` gemountet |
|
|
| Theming | next-themes | Dark Mode über `.dark` |
|
|
| Auth | Better Auth + Passkey-Plugin | nur wenn das Projekt Anmeldung braucht |
|
|
| Daten-Layer | **pro Projekt entschieden** | Vorlage bringt keinen mit |
|
|
| Tests | Vitest + Playwright | strikt getrennte Scopes |
|
|
| Betrieb | Docker Compose, selbst gehostet | App-Container, Proxy extern |
|
|
|
|
### Bewusst nicht enthalten
|
|
|
|
- **Keine Datenbank, kein ORM.** `/architecture` entscheidet pro Projekt. Einzige
|
|
Auflage: muss im Compose-Stack selbst hostbar sein.
|
|
- **Kein Supabase, kein Vercel.** Nicht vergessen, sondern absichtlich ausgebaut.
|
|
In `CLAUDE.md` steht das explizit, damit es nicht durch die Hintertür zurückkommt.
|
|
- **Keine Content-Security-Policy vorkonfiguriert.** Geraten bricht sie Apps; pro
|
|
Projekt mit `Report-Only` anfangen.
|
|
- **Kein Error-Tracking vorinstalliert.** Sentry-Anleitung in `docs/DEPLOYMENT.md`.
|
|
|
|
### UI-Komponenten
|
|
|
|
Sechs sind vorinstalliert:
|
|
|
|
| Komponente | Wofür |
|
|
|------------|-------|
|
|
| `button` | Aktionen, Varianten über CVA |
|
|
| `card` | Container mit Header/Content/Footer |
|
|
| `input` | Textfelder |
|
|
| `label` | Beschriftungen, barrierefrei verknüpft |
|
|
| `form` | react-hook-form-Anbindung inkl. Fehlermeldungen |
|
|
| `sonner` | Toaster, bereits in `layout.tsx` gemountet |
|
|
|
|
Alles Weitere ist ein Kommando entfernt:
|
|
|
|
```bash
|
|
npx shadcn@latest add dialog table tabs --yes
|
|
```
|
|
|
|
**Tailwind v4 ist CSS-first: es gibt keine `tailwind.config.ts`.** Theme-Tokens leben
|
|
in `src/app/globals.css` unter `@theme inline`. Wer aus Gewohnheit eine Config-Datei
|
|
anlegt, wundert sich, warum nichts greift.
|
|
|
|
Nutze semantische Tokens (`bg-background`, `text-muted-foreground`) statt roher
|
|
Palettenwerte (`bg-slate-900`) — sonst bricht der Dark Mode.
|
|
|
|
---
|
|
|
|
## Authentifizierung: Passkeys
|
|
|
|
Sobald ein Projekt Anmeldung braucht, ist der Stack festgelegt: **Better Auth mit
|
|
Passkey-Plugin**. Passkey ist der primäre Weg und wird zuerst angeboten; ein
|
|
Passwort-Flow ist nur der Fallback für Geräte ohne WebAuthn.
|
|
|
|
`/init` legt dafür automatisch „Auth-Grundlage" als **PROJ-1, P0** an. Jedes Feature,
|
|
das angemeldete Nutzer braucht, hängt davon ab.
|
|
|
|
Verbindlich, sobald Auth im Spiel ist:
|
|
|
|
- Passkey-Registrierung wird bei der Anmeldung angeboten, nicht in den Einstellungen versteckt
|
|
- Bestehende Passwort-Nutzer werden nach dem Login zum Passkey eingeladen
|
|
- Passwort-Fallback ist rate-limited und verrät nie, ob ein Konto existiert
|
|
- Produktion braucht HTTPS — localhost ist die einzige Ausnahme
|
|
|
|
> **Die eine Sache, die dich sonst Stunden kostet:** Die WebAuthn-Relying-Party-ID
|
|
> wird aus dem Hostnamen in `NEXT_PUBLIC_APP_URL` abgeleitet. Stimmt der nicht exakt
|
|
> mit der ausgelieferten Domain überein, scheitern *alle* Passkeys — ohne verwertbare
|
|
> Fehlermeldung.
|
|
|
|
`/qa` prüft aktiv, dass der Passkey-Weg der primäre ist. Ein aufgeweichter oder
|
|
fehlender Passkey-Flow gilt als **High**-Befund.
|
|
|
|
---
|
|
|
|
## Tests
|
|
|
|
| Art | Ort | Kommando | Geschrieben von |
|
|
|-----|-----|----------|-----------------|
|
|
| Unit / Integration (Vitest) | neben der Quelle: `useThing.test.ts` neben `useThing.ts` | `npm test` | `/backend`, `/qa` |
|
|
| E2E (Playwright) | `tests/PROJ-X-name.spec.ts` | `npm run test:e2e` | `/qa` |
|
|
|
|
Die Läufe sind strikt getrennt: Vitest scannt ausschließlich `src/`, Playwright
|
|
ausschließlich `tests/`. Zwei Smoke-Tests liegen als Muster bei und belegen, dass die
|
|
Trennung greift.
|
|
|
|
`/qa` schreibt für jedes bestandene Acceptance Criterion einen E2E-Test — daraus wächst
|
|
die Regressions-Suite von selbst.
|
|
|
|
**Port belegt?** `E2E_PORT=3177 npm run test:e2e`. Ohne das testet Playwright
|
|
stillschweigend gegen einen fremden Server, der zufällig auf :3000 läuft.
|
|
|
|
---
|
|
|
|
## Deployment
|
|
|
|
Selbst gehostet über Docker Compose. Der Stack veröffentlicht die App auf Loopback;
|
|
ein Reverse Proxy auf dem Host terminiert TLS.
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
U["Nutzer"] -->|HTTPS| P["Reverse Proxy<br/><i>nginx / Caddy</i><br/>auf dem Host"]
|
|
P -->|"127.0.0.1:3000"| C["app-Container<br/><i>Next.js standalone</i>"]
|
|
C --> H["/api/health"]
|
|
|
|
subgraph SRV["Server"]
|
|
P
|
|
subgraph CMP["docker compose"]
|
|
C
|
|
H
|
|
DB[("Datenbank<br/><i>optional, pro Projekt</i>")]
|
|
end
|
|
end
|
|
|
|
C -.->|"nur wenn gewählt"| DB
|
|
G["git repo"] -->|"./scripts/update.sh"| CMP
|
|
|
|
style DB stroke-dasharray: 5 5
|
|
```
|
|
|
|
```bash
|
|
docker compose up -d --build # Erststart
|
|
./scripts/update.sh # neuen Stand ausrollen
|
|
```
|
|
|
|
### Nur der App-Port geht nach außen
|
|
|
|
Im Stack veröffentlicht **ausschließlich der App-Service** einen Port, und auch den
|
|
nur auf Loopback (`127.0.0.1`). Jeder weitere Service — Datenbank, Cache, Queue,
|
|
Admin-UI — bekommt **keinen `ports:`-Eintrag**. Die Container erreichen sich über
|
|
den Compose-Netzwerknamen, dafür muss nichts veröffentlicht werden:
|
|
|
|
```
|
|
DATABASE_URL=postgres://user:pass@db:5432/app
|
|
```
|
|
|
|
> `ports: "5432:5432"` bindet auf `0.0.0.0` — die Datenbank steht damit im offenen
|
|
> Internet, geschützt nur durch ihr Passwort. Und Docker schreibt eigene
|
|
> iptables-Regeln, die **ufw umgehen**: Der Server sieht abgeschottet aus, während
|
|
> der Port offen ist. Das ist einer der häufigsten Wege, wie selbst gehostete Stacks
|
|
> kompromittiert werden.
|
|
|
|
Die Regel ist an vier Stellen verankert, damit sie beim Vibe-Coden nicht verloren
|
|
geht: `rules/security.md` (lädt automatisch beim Bearbeiten von `compose.yaml`), der
|
|
Backend-Sub-Agent, der QA-Sub-Agent (Verstoß = **Critical**) und der Pre-Flight von
|
|
`/deploy`, der den Deploy abbricht.
|
|
|
|
Prüfen, was wirklich gebunden ist:
|
|
|
|
```bash
|
|
docker compose config | grep -B8 "published:"
|
|
docker compose ps --format "{{.Service}}\t{{.Ports}}"
|
|
```
|
|
|
|
Zugriff zum Debuggen holst du dir per SSH-Tunnel, nicht per veröffentlichtem Port.
|
|
|
|
### Was `update.sh` macht
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A["Start"] --> B{"Arbeitsverzeichnis<br/>sauber?"}
|
|
B -->|nein| X["Abbruch"]
|
|
B -->|ja| C["git fetch + fast-forward"]
|
|
C --> D{"Neuer Commit<br/>oder --force?"}
|
|
D -->|nein| Y["Nichts zu tun"]
|
|
D -->|ja| E["docker compose build"]
|
|
E --> F["docker compose up -d"]
|
|
F --> G{"Healthcheck<br/>innerhalb 120s?"}
|
|
G -->|ja| H["Alte Images aufräumen<br/>✓ Deployed"]
|
|
G -->|nein| I["Letzte 50 Logzeilen ausgeben"]
|
|
I --> J["git reset auf Vorgänger-Commit"]
|
|
J --> K["Neu bauen + starten"]
|
|
K --> L["✓ Rollback"]
|
|
```
|
|
|
|
Ein kaputtes Deployment heilt sich selbst, statt die Seite liegen zu lassen.
|
|
|
|
```bash
|
|
./scripts/update.sh --force # neu bauen ohne neuen Commit
|
|
./scripts/update.sh --branch dev # anderen Branch ausrollen
|
|
```
|
|
|
|
Der Reverse Proxy muss `X-Forwarded-Proto` und `X-Forwarded-Host` durchreichen, sonst
|
|
brechen Redirects und WebAuthn-Origin-Prüfungen. Fertige nginx- und Caddy-Blöcke sowie
|
|
die Anleitung zum Nachrüsten einer Datenbank stehen in
|
|
[docs/DEPLOYMENT.md](docs/DEPLOYMENT.md).
|
|
|
|
Security-Header (`X-Frame-Options`, `X-Content-Type-Options`, `Referrer-Policy`, `HSTS`)
|
|
sind in `next.config.ts` gesetzt und nach dem Deploy im Browser gegenzuprüfen.
|
|
|
|
---
|
|
|
|
## Kommandos
|
|
|
|
```bash
|
|
npm run dev # Dev-Server auf :3000
|
|
npm run build # Produktions-Build (standalone)
|
|
npm run start # Produktions-Server lokal
|
|
npm run lint # ESLint
|
|
npm run typecheck # tsc --noEmit
|
|
npm test # Vitest
|
|
npm run test:watch # Vitest im Watch-Modus
|
|
npm run test:e2e # Playwright
|
|
npm run test:all # beide Suites
|
|
npm run docker:up # Stack bauen und starten
|
|
npm run docker:down # Stack stoppen
|
|
npm run docker:logs # Logs folgen
|
|
```
|
|
|
|
---
|
|
|
|
## Dateibaum
|
|
|
|
```
|
|
CLAUDE.md Wird jede Session geladen: Stack, Struktur, Konventionen
|
|
README.md Dieses Dokument
|
|
.claude/
|
|
settings.json Permissions: Deny für Secrets, Allow für Routine
|
|
rules/
|
|
general.md Immer aktiv: Tracking, Status, Git, Human-in-the-Loop
|
|
frontend.md Bei UI-Dateien
|
|
backend.md Bei API-, lib- und Schema-Dateien
|
|
security.md Bei API-, Auth-, Env- und Deploy-Dateien
|
|
skills/
|
|
init/SKILL.md /init
|
|
write-spec/SKILL.md /write-spec (+ template.md für neue Specs)
|
|
refine/SKILL.md /refine
|
|
architecture/SKILL.md /architecture
|
|
frontend/SKILL.md /frontend
|
|
backend/SKILL.md /backend
|
|
qa/SKILL.md /qa
|
|
deploy/SKILL.md /deploy
|
|
help/SKILL.md /help
|
|
agents/
|
|
frontend-dev.md Sub-Agent für UI
|
|
backend-dev.md Sub-Agent für Server
|
|
qa-engineer.md Sub-Agent für Funktionstests
|
|
security-auditor.md Sub-Agent für Sicherheit — Veto über /deploy
|
|
docs/
|
|
PRD.md Füllt /init im Interview
|
|
DEPLOYMENT.md Docker-Betrieb, Proxy, DB nachrüsten, Troubleshooting
|
|
features/
|
|
INDEX.md Zentrales Status-Tracking
|
|
src/
|
|
app/
|
|
layout.tsx ThemeProvider + Toaster verdrahtet
|
|
page.tsx Startseite mit Workflow-Übersicht
|
|
globals.css Tailwind v4: Theme-Tokens in @theme inline
|
|
api/health/route.ts Liveness-Probe für Docker und update.sh
|
|
components/
|
|
theme-provider.tsx
|
|
ui/ button, card, input, label, form, sonner
|
|
lib/
|
|
utils.ts cn()
|
|
utils.test.ts Vitest-Muster
|
|
test/setup.ts
|
|
tests/smoke.spec.ts Playwright-Muster
|
|
scripts/update.sh Pull, Rebuild, Restart, Rollback
|
|
Dockerfile Dreistufig → standalone, unprivilegierter User
|
|
compose.yaml App-Service, Healthcheck, Log-Rotation
|
|
.env.example Jede benötigte Variable mit Dummy-Wert
|
|
.gitattributes Erzwingt LF — sonst scheitert update.sh auf dem Server
|
|
.dockerignore .gitignore eslint.config.mjs next.config.ts
|
|
playwright.config.ts postcss.config.mjs vitest.config.mts
|
|
components.json tsconfig.json package.json
|
|
```
|
|
|
|
56 Dateien im Repo.
|
|
|
|
---
|
|
|
|
## Stolperfallen
|
|
|
|
Vier Dinge, die nicht offensichtlich sind und je einmal wehtun:
|
|
|
|
| Fall | Was passiert |
|
|
|------|--------------|
|
|
| Ein DB-Service bekommt `ports: "5432:5432"` | Die Datenbank steht im offenen Internet — und Docker umgeht dabei ufw, der Server *wirkt* nur abgeschottet |
|
|
| `NEXT_PUBLIC_APP_URL` weicht von der Domain ab | Alle Passkeys scheitern, ohne verwertbare Fehlermeldung |
|
|
| Port 3000 ist von einem anderen Projekt belegt | Playwright testet still gegen den fremden Server — `E2E_PORT` setzen |
|
|
| Jemand legt eine `tailwind.config.ts` an | Wird in v4 ignoriert; Tokens gehören in `globals.css` |
|
|
| Checkout auf Windows ohne `.gitattributes` | `update.sh` bekommt CRLF und stirbt auf dem Server mit `bad interpreter` |
|
|
|
|
---
|
|
|
|
## Anpassen
|
|
|
|
| Was | Wo |
|
|
|-----|-----|
|
|
| Stack, Konventionen, Kommandos | `CLAUDE.md` |
|
|
| Coding-Standards | `.claude/rules/` |
|
|
| Workflow-Schritte selbst | `.claude/skills/*/SKILL.md` |
|
|
| Aufgabenteilung der Sub-Agents | `.claude/agents/` |
|
|
| Permissions | `.claude/settings.json` |
|
|
| Spec-Aufbau | `.claude/skills/write-spec/template.md` |
|
|
|
|
Ein neues Kommando ist ein neuer Ordner unter `.claude/skills/` mit einer `SKILL.md`
|
|
und `user-invocable: true` im Frontmatter. Ein neuer Sub-Agent ist eine Markdown-Datei
|
|
in `.claude/agents/` mit `name`, `description`, `tools` im Frontmatter.
|
|
|
|
---
|
|
|
|
## Lizenz
|
|
|
|
MIT
|