Merge branch 'main' of https://gitea.troeger-net.org/martin/claude-workflow
This commit is contained in:
@@ -103,6 +103,8 @@ Kurzbefehl für den gesamten Ablauf: `/story`
|
||||
| `/story` | `skills/story/SKILL.md` | Voller Lifecycle von Research bis PR |
|
||||
| `/workflow-review` | `skills/workflow-review/SKILL.md` | Opus reviewt alle Skills/Konfig → Umsetzungsplan für Sonnet |
|
||||
| `/implement` | `skills/implement/SKILL.md` | Implementierung direkt aus bestehendem Plan (überspringt Research/Planung) |
|
||||
| `/project-review` | `skills/project-review/SKILL.md` | Domänenunabhängiges Review für Nicht-Software-Dokumente (Bau, Hardware, Analysen) |
|
||||
| `/docker-compose` | `skills/docker-compose/SKILL.md` | Erstellt/aktualisiert eine docker-compose.yaml nach Hausregeln (4 Zielhosts: automation/controller/webserver/localhost, Container-Doku-Recherche, Port-Belegungs-Check, 5 MB Logging, TZ Berlin, Bind-Mounts, .env-Secrets, VPN-Binding für Webserver) |
|
||||
|
||||
## Agenten
|
||||
|
||||
|
||||
+33
-1
@@ -4,6 +4,10 @@
|
||||
|
||||
Alle Texte, Kommentare und Kommunikation auf Deutsch. Technische Begriffe und Code-Bezeichner bleiben im Original.
|
||||
|
||||
## Dokumentenformat
|
||||
|
||||
**Immer AsciiDoc (`.adoc`) statt Markdown** für alle Dokumente (Analysen, Pläne, Berichte, Anfragen usw.). Neue Dokumente als `.adoc` anlegen. Bestehende `.md`-Dateien nur auf ausdrücklichen Wunsch konvertieren. Ausnahmen: von Tools erzwungene Dateien (`CLAUDE.md`, `MEMORY.md`, `README.md` wo vom Workflow verlangt) bleiben Markdown.
|
||||
|
||||
## Entwicklungsprinzipien
|
||||
|
||||
- **Single Responsibility:** Jede Datei, jede Funktion hat genau eine Aufgabe
|
||||
@@ -64,7 +68,16 @@ Niemals direkt auf `main` committen. Alle Aenderungen ueber Feature-Branches und
|
||||
|
||||
## Fehlende Tools
|
||||
|
||||
Wenn ein benötigtes CLI-Tool fehlt: **nicht** mit Workarounds (curl, rohe API-Calls) umgehen. Stattdessen:
|
||||
**Sobald die saubere Lösung ein Standard-CLI-Tool braucht, das fehlt: STOPP.**
|
||||
Zuerst die Installation vorschlagen und auf den User warten — **bevor** irgendeine
|
||||
Alternative gebaut wird. Eine fehlende Abhängigkeit ist ein Stopp-Signal, keine
|
||||
Designfrage. Der User installiert lieber ein Tool, als dass komplizierte
|
||||
Eigenlösungen entstehen.
|
||||
|
||||
Kein Ausweichen auf curl/rohe API-Calls, nachgebaute Login-Flows, HTML-/Token-
|
||||
Parsing oder mehrstufige Eigenkonstruktionen — auch nicht „nur kurz zum Testen".
|
||||
Solche Konstrukte gar nicht erst anfangen; das fehlende Tool ist immer der erste
|
||||
Lösungsweg.
|
||||
|
||||
1. User informieren welches Tool fehlt und den Installations-Befehl nennen
|
||||
2. User führt den Befehl in einer separaten Konsole aus (sudo funktioniert dort)
|
||||
@@ -92,6 +105,25 @@ Bei User-Korrekturen oder neuen Erkenntnissen zuerst klassifizieren, dann am ric
|
||||
|
||||
**Niemals Arbeitsregeln im Memory-System ablegen.** Memory ist für Kontext, nicht für Regeln. Wenn eine User-Korrektur eine wiederkehrende Arbeitsregel beschreibt → CLAUDE.md. Immer.
|
||||
|
||||
## Erläuterungs-Stil
|
||||
|
||||
Wenn der User um eine Erklärung auf **architektonischer, logischer, konzeptioneller, abstrakter oder prinzipieller Ebene** bittet (oder Varianten wie "vom Konzept her", "rein architektonisch"):
|
||||
|
||||
- Den **Mechanismus der zugrundeliegenden Technik in Fachsprache** erklären (Framework-Konzepte, Lifecycle, Semantik) — keine Alltagsmetaphern (Bibliothekar, Postbote, Restaurant).
|
||||
- Das **Prinzip generisch** darlegen, **nicht am konkreten Code festmachen**. Am Ende kurz auf die Anwendung im Projekt zurückbeziehen.
|
||||
- Technische Begriffe (z.B. "detached entity", "managed", "flush", "snapshot", "dirty check") frei verwenden, kurz einführen falls neu im Kontext, aber nicht durch Umschreibungen ersetzen.
|
||||
|
||||
Im Zweifel lieber technischer als metaphorischer. Der User hat solides Fachwissen über die Domäne (z.B. relationale Datenbanken, HTTP, Linux) und möchte framework-/tool-spezifische Konzepte tatsächlich verstehen, nicht durch Metaphern davor abgeschirmt werden.
|
||||
|
||||
## Antwort-Stil (Prägnanz)
|
||||
|
||||
Der User ist Informatiker/Techniker und will präzise Kommunikation mit den wesentlichen Fakten — nichts Ungefragtes.
|
||||
|
||||
- **Antwort zuerst, keine Herleitung.** Ergebnis in 1–3 Sätzen; den Weg dahin nur auf Nachfrage.
|
||||
- **Nur das Gefragte.** Keine ungefragten Optionen, Empfehlungen, Mockups, „wo es hakt"-Exkurse. Höchstens *eine* knappe Schlusszeile als Angebot.
|
||||
- **Umfang an die konkrete Frage koppeln.** „Kann man X?" → ja/nein + Grund. Ein Artefakt (Diagramm, Tabelle, Umbau) nur liefern, wenn es explizit verlangt wurde — dann aber vollständig und **nicht** um ungefragte Zusätze erweitert.
|
||||
- **Belege statt Ausbreitung.** `datei:zeile` statt den Code nachzuerzählen.
|
||||
|
||||
## MCP
|
||||
|
||||
Bei Frameworks/Bibliotheken immer Context-7-Dokumentation abrufen (resolve-library-id / query-docs).
|
||||
|
||||
@@ -18,19 +18,26 @@ IFS=$'\t' read -r current_dir model total_input total_output context_size used_p
|
||||
total_tokens=$(( (total_input + total_output) / 1000 ))
|
||||
context_limit_k=$(( context_size / 1000 ))
|
||||
|
||||
cost_formatted=$(LC_NUMERIC=C awk "BEGIN {
|
||||
cents = int($cost_usd * 100 + 0.5)
|
||||
if (cents < 100) printf \"%d¢\", cents
|
||||
else printf \"\$%.2f\", $cost_usd
|
||||
}")
|
||||
cost_formatted=$(LC_NUMERIC=C awk -v cost="$cost_usd" 'BEGIN {
|
||||
cents = int(cost * 100 + 0.5)
|
||||
if (cents < 100) printf "%d¢", cents
|
||||
else printf "$%.2f", cost
|
||||
}')
|
||||
|
||||
user=$(whoami)
|
||||
host=$(hostname -s)
|
||||
|
||||
# Lange Pfade kürzen, damit die Statusline nie umbricht (sonst gerät der
|
||||
# claude-code-Renderer aus dem Tritt, weil er 1 Zeile am unteren Rand erwartet).
|
||||
display_dir=$current_dir
|
||||
if [ ${#display_dir} -gt 25 ]; then
|
||||
display_dir=".../$(basename "$(dirname "$current_dir")")/$(basename "$current_dir")"
|
||||
fi
|
||||
|
||||
git_branch=""
|
||||
if [ -n "$current_dir" ] && git -C "$current_dir" rev-parse --git-dir > /dev/null 2>&1; then
|
||||
branch=$(git -C "$current_dir" branch --show-current 2>/dev/null)
|
||||
[ -n "$branch" ] && git_branch=" \033[33m($branch)\033[00m"
|
||||
[ -n "$branch" ] && printf -v git_branch ' \033[33m(%s)\033[00m' "$branch"
|
||||
fi
|
||||
|
||||
chroot_prefix=""
|
||||
@@ -38,7 +45,6 @@ if [ -r /etc/debian_chroot ]; then
|
||||
chroot_prefix="($(cat /etc/debian_chroot))"
|
||||
fi
|
||||
|
||||
printf "${chroot_prefix}\033[32m${user}@${host}\033[00m:\033[34m${current_dir}\033[00m${git_branch}"
|
||||
printf " \033[90m|\033[00m \033[36m${model}\033[00m"
|
||||
printf " \033[90m|\033[00m \033[35m${total_tokens}K/${context_limit_k}K (${used_percent}%%)\033[00m"
|
||||
printf " \033[90m|\033[00m \033[33m${cost_formatted}\033[00m"
|
||||
printf '%s\033[32m%s@%s\033[00m:\033[34m%s\033[00m%s \033[90m|\033[00m \033[36m%s\033[00m \033[90m|\033[00m \033[35m%dK/%dK (%d%%)\033[00m \033[90m|\033[00m \033[33m%s\033[00m' \
|
||||
"$chroot_prefix" "$user" "$host" "$display_dir" "$git_branch" \
|
||||
"$model" "$total_tokens" "$context_limit_k" "$used_percent" "$cost_formatted"
|
||||
|
||||
@@ -0,0 +1,353 @@
|
||||
---
|
||||
description: Erstellt oder aktualisiert eine docker-compose.yaml nach den Best Practices dieses Workflows — aktuelle Compose-Syntax, explizite Konfiguration, Secrets in .env, optionales VPN-Binding für den Webserver. Aktivieren wenn ein neuer Container-Stack aufgesetzt oder ein bestehender überarbeitet werden soll. Nicht verwenden für reine Image-Updates, einzelne docker-Befehle oder Dockerfile-Änderungen.
|
||||
model: claude-opus-4-6
|
||||
---
|
||||
|
||||
# /docker-compose — Compose-Stack nach Hausregeln
|
||||
|
||||
Zielort: ein Verzeichnis unter `/docker/<name>/` auf einem der Hosts oder lokal. Der Skill liefert eine `docker-compose.yaml` plus passende `.env`-Vorlage.
|
||||
|
||||
## Phase 0: Aufgabenklärung
|
||||
|
||||
Stelle dem User folgende Fragen, sofern nicht schon im Request beantwortet:
|
||||
|
||||
1. **Name des Stacks** (Verzeichnisname unter `/docker/`)
|
||||
2. **Welche Services** sind enthalten (Image + Zweck)?
|
||||
3. **Zielhost** — eine der folgenden vier Optionen, per `AskUserQuestion`:
|
||||
|
||||
| Ziel | SSH-Pfad | Netzwerk-Kontext |
|
||||
|---|---|---|
|
||||
| `automation` | `ssh automation:/docker/<name>/` | internes LAN, kein VPN-Binding |
|
||||
| `controller` | `ssh controller:/docker/<name>/` | internes LAN, kein VPN-Binding |
|
||||
| `webserver` | `ssh waldperle:/docker/<name>/` | öffentlich, interne Ports an VPN `10.7.0.1` binden |
|
||||
| `localhost` | aktuelles Arbeitsverzeichnis | kein Bind-Prefix |
|
||||
|
||||
4. **Reverse-Proxy** — soll der Stack über `nginx-proxy-manager` ans Web? Dann `reverse-proxy`-Netz nötig.
|
||||
5. **Persistente Daten** — welche Verzeichnisse müssen über Restarts/Recreates überleben (Datenbankdaten, Configs, Uploads, Logs)?
|
||||
6. **Gewünschte Host-Ports** — welche Ports sollen extern erreichbar sein? (werden in Phase 1.5 gegen Belegung geprüft)
|
||||
|
||||
Bei `webserver` zusätzlich:
|
||||
|
||||
> "Sollen die Ports nur über das VPN (`10.7.0.1`) erreichbar sein oder auch öffentlich (z.B. `80`, `443`)?"
|
||||
|
||||
Standardregel: **alle Ports an `10.7.0.1` binden**, nur explizit öffentliche (HTTP/HTTPS für reverse-proxy) bleiben ohne Bind-IP.
|
||||
|
||||
## Phase 0.5: Container-Recherche (Pflicht vor Generierung)
|
||||
|
||||
Für **jedes** Image im Stack die aktuelle Dokumentation konsultieren, bevor die Compose-Datei geschrieben wird. Ziel: korrekte aktuelle Konfiguration, bekannte Breaking Changes berücksichtigen, deprecated Variablen vermeiden.
|
||||
|
||||
### Quellen (in dieser Reihenfolge)
|
||||
|
||||
1. **Context7** (bevorzugt für offizielle Images):
|
||||
```
|
||||
mcp__plugin_context7_context7__resolve-library-id → Library-ID finden
|
||||
mcp__plugin_context7_context7__query-docs → gezielte Frage stellen
|
||||
```
|
||||
2. **Offizielle Docker-Hub-Seite** via `WebFetch`:
|
||||
```
|
||||
https://hub.docker.com/_/<image> # Official Images
|
||||
https://hub.docker.com/r/<vendor>/<image> # Community Images
|
||||
```
|
||||
3. **GitHub-Releases / CHANGELOG** via `WebFetch` — für Breaking Changes der aktuellen Major-Version.
|
||||
4. **WebSearch** als Fallback, wenn die obigen Quellen leer bleiben.
|
||||
|
||||
### Was muss konkret geprüft werden?
|
||||
|
||||
Pro Image diese Punkte abklären:
|
||||
|
||||
- **Aktuelle Major-Version** (`:latest` zeigt auf welche?)
|
||||
- **Breaking Changes** seit der vorherigen Major-Version (z.B. PostgreSQL-Daten-Migration zwischen Major-Versionen, Keycloak `start-dev` vs `start --optimized`, MySQL→MariaDB-Replacements)
|
||||
- **Pflicht-Env-Vars** der aktuellen Version (z.B. Auth-Defaults wurden in vielen Images entfernt — `POSTGRES_PASSWORD` ist Pflicht, kein Standard mehr)
|
||||
- **Deprecated Env-Vars** — alte Namen vermeiden
|
||||
- **Default-Ports** und ob sie sich geändert haben
|
||||
- **Volume-Pfade** des Images — wo erwartet das Image persistente Daten?
|
||||
- **Health-Check-Kommando** — was empfiehlt der Maintainer?
|
||||
- **User/UID-Verhalten** — läuft das Image als Root, gibt es ein `PUID/PGID`-Pattern, oder `user:`-Override möglich?
|
||||
|
||||
### Output dieser Phase
|
||||
|
||||
Bevor weitergegangen wird, dem User eine **Zusammenfassung pro Image** zeigen:
|
||||
|
||||
```
|
||||
postgres:latest → aktuell 17.x
|
||||
Breaking: pg_dump-Format zwischen Major-Versionen inkompatibel — Major-Upgrade
|
||||
erfordert pg_dumpall/restore (siehe Release-Notes)
|
||||
Pflicht: POSTGRES_PASSWORD
|
||||
Empfohlen: POSTGRES_DB, POSTGRES_USER, Healthcheck pg_isready
|
||||
Volume: /var/lib/postgresql/data
|
||||
```
|
||||
|
||||
Bei Major-Sprung gegenüber einem bestehenden Stack: User explizit warnen und Migrationsschritt vorschlagen, bevor `:latest` gepinnt bleibt.
|
||||
|
||||
### Im Compose-Header dokumentieren
|
||||
|
||||
Header-Kommentar in der `docker-compose.yaml`:
|
||||
|
||||
```yaml
|
||||
# Stack: <name>
|
||||
# Recherchiert: <YYYY-MM-DD>
|
||||
# Images:
|
||||
# postgres:latest → 17.x (Breaking: Major-Upgrade braucht pg_dumpall)
|
||||
# <image>:latest → x.y (Hinweise/Breaking)
|
||||
```
|
||||
|
||||
So bleibt nachvollziehbar, gegen welchen Wissensstand der Stack designed wurde.
|
||||
|
||||
## Phase 1.5: Port-Belegungs-Analyse (Pflicht vor Generierung)
|
||||
|
||||
Bevor irgendein Port in die `docker-compose.yaml` geschrieben wird, **beide** Prüfungen auf dem Zielhost ausführen:
|
||||
|
||||
### Prüfung 1: Alle bestehenden Compose-Files
|
||||
|
||||
```bash
|
||||
# Bei automation/controller/webserver via SSH:
|
||||
ssh <host> "grep -rhE '^\s*-\s*\"?([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:)?[0-9]+:[0-9]+' /docker/ 2>/dev/null \
|
||||
| sed -E 's/.*\"?([0-9.]+:)?([0-9]+):.*/\2/' | sort -u"
|
||||
|
||||
# Bei localhost: lokal ausführen (Pfad ggf. anpassen)
|
||||
grep -rhE '^\s*-\s*\"?([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:)?[0-9]+:[0-9]+' . 2>/dev/null \
|
||||
| sed -E 's/.*\"?([0-9.]+:)?([0-9]+):.*/\2/' | sort -u
|
||||
```
|
||||
|
||||
### Prüfung 2: Aktive Listener auf dem Host
|
||||
|
||||
```bash
|
||||
ssh <host> "ss -tlnp 2>/dev/null | awk 'NR>1 {split(\$4, a, \":\"); print a[length(a)]}' | sort -u"
|
||||
# Bei localhost:
|
||||
ss -tlnp 2>/dev/null | awk 'NR>1 {split($4, a, ":"); print a[length(a)]}' | sort -u
|
||||
```
|
||||
|
||||
### Konfliktauflösung
|
||||
|
||||
Vergleiche die in Phase 0 gewünschten Host-Ports mit beiden Listen:
|
||||
|
||||
- **Frei in beiden:** Port übernehmen.
|
||||
- **Belegt in Prüfung 1 (Compose):** zeigen, welcher Stack/Service belegt — User fragen, ob Konflikt akzeptiert ist (selten korrekt) oder ein anderer Port gewählt werden soll.
|
||||
- **Belegt in Prüfung 2 (Listener):** zeigen welcher Prozess — alternativen Port vorschlagen.
|
||||
- **Vorschlag bei Konflikt:** nächste freie Port-Nummer aus passendem Bereich (Web-UIs `8000–8999`, DBs `5400+`, Custom `3000–3999`).
|
||||
|
||||
Erst wenn alle gewünschten Ports konfliktfrei sind, weiter zu Phase 3.
|
||||
|
||||
**Hinweis für `webserver`:** das VPN-IP-Prefix `10.7.0.1` ändert nichts an der Belegungsprüfung — ein bereits gebundener Port ist gebunden, egal an welche IP.
|
||||
|
||||
## Phase 1: Pflichtregeln (nicht verhandelbar)
|
||||
|
||||
Diese Regeln gelten für jeden generierten Stack:
|
||||
|
||||
### Compose-Syntax
|
||||
|
||||
- **Kein `version:`-Key.** Compose v2 ignoriert ihn — nicht aufnehmen.
|
||||
- **`name: <stack>`** als oberstes Feld (Project-Name explizit setzen).
|
||||
- **YAML-Mapping-Stil konsistent** pro Datei (entweder Listen-Stil `- KEY=VAL` oder Map-Stil `KEY: VAL` — nicht mischen innerhalb eines Services).
|
||||
|
||||
### Pro Service
|
||||
|
||||
- **`container_name: <name>`** — explizit, stabile Namen für Logs/Scripts.
|
||||
- **`hostname: <name>`** — für interne Service-Adressierung in geteilten Netzwerken.
|
||||
- **`restart: unless-stopped`** als Default. `always` nur wenn der Container nach einem manuellen `docker stop` von alleine wieder hochkommen soll.
|
||||
- **`environment:`**
|
||||
- `TZ=Europe/Berlin` ist Pflicht.
|
||||
- Alle relevanten Env-Variablen explizit auflisten (keine impliziten Defaults aus dem Image — der Image-Default kann sich ändern).
|
||||
- Sensible Werte (Passwörter, Tokens, Secrets, API-Keys) ausschließlich als `${VARIABLE}` referenzieren — Werte in `.env`.
|
||||
- Keine Inline-Defaults vom Typ `${VAR:-default}` für Secrets. Für nicht-sensible Werte erlaubt, aber sparsam.
|
||||
- **`logging:`**
|
||||
```yaml
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "5M"
|
||||
max-file: "3"
|
||||
```
|
||||
5 MB pro Datei × 3 Dateien = max. 15 MB pro Container.
|
||||
- **`labels:`**
|
||||
```yaml
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=false"
|
||||
```
|
||||
Default `false` (entspricht globaler CLAUDE.md). Auf `true` setzen, wenn der Container über `:latest` läuft und automatische Updates erwünscht sind.
|
||||
- **`user: "1000:1000"`** als Default. Ausnahmen nur, wenn das Image Root erfordert (z.B. NPM Port 80/443) — dann mit Kommentar warum.
|
||||
- **Volumes als relative Bind-Mounts** (`./data:/data`). Niemals absolute Pfade ausserhalb von `./`, niemals named volumes für Datenpersistenz (Backup-Strategie hängt am Bind-Mount).
|
||||
- **Read-only mounten** wo möglich (`./config:/etc/app/config:ro`).
|
||||
|
||||
### Ports
|
||||
|
||||
- **Immer als gequoteter String:** `"8080:80"`. Unquoted YAML kann `5060:5060` als Sexagesimalzahl interpretieren.
|
||||
- **Pro Zielhost:**
|
||||
- `automation`, `controller`, `localhost`: kein IP-Prefix (LAN-intern erreichbar).
|
||||
- `webserver` (waldperle): alle internen Ports an `10.7.0.1` binden — `"10.7.0.1:8080:80"`. Nur explizit öffentliche Ports (NPM 80/443) ohne IP-Prefix.
|
||||
- **Belegung muss in Phase 1.5 geprüft sein** — kein Port ohne vorherige Konfliktprüfung.
|
||||
|
||||
### Netzwerke
|
||||
|
||||
- **Externe Standard-Netze:**
|
||||
- `reverse-proxy` — für alle Container, die über NPM ans Web sollen
|
||||
- `public` — für Container mit direktem Public-Exposure
|
||||
- `openhab` — Smart-Home-Stack (nur `automation`)
|
||||
- Externe Netze in der `networks:`-Sektion immer mit `external: true` markieren.
|
||||
- Stack-interne Netze ohne `external` deklarieren (oder weglassen — Compose erzeugt dann `<name>_default`).
|
||||
|
||||
### Multi-Service-Stacks
|
||||
|
||||
- **Healthchecks für DB/Cache:**
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
```
|
||||
- **`depends_on` mit `condition: service_healthy`** wo Healthchecks existieren — verhindert Race-Conditions beim Start.
|
||||
|
||||
### Image-Versionen
|
||||
|
||||
- **`:latest`** ist der Default (User-Wunsch: immer neueste Version) — aber nur, wenn die in Phase 0.5 dokumentierte Major-Version mit der Stack-Konfiguration kompatibel ist.
|
||||
- Bei `:latest` + Watchtower-Enable=`true` → automatische Updates. Bei Datenbanken nur, wenn das Image rolling-kompatibel ist (oft nicht — siehe PostgreSQL-Hinweis aus Phase 0.5).
|
||||
- Bei kritischen Stacks (Datenbanken, Auth) Major-Version pinnen (z.B. `postgres:17`) und Major-Upgrades bewusst durchführen.
|
||||
|
||||
### Security-Hardening (für exposed Services empfohlen)
|
||||
|
||||
```yaml
|
||||
cap_drop:
|
||||
- ALL
|
||||
security_opt:
|
||||
- no-new-privileges=true
|
||||
```
|
||||
|
||||
Aufnehmen für: Web-UIs, REST-APIs, alles was Ports öffnet. Nicht für DBs/Caches im internen Netz (würde dort oft brechen).
|
||||
|
||||
### `.env`-Datei
|
||||
|
||||
- Liegt im selben Verzeichnis wie die `docker-compose.yaml`.
|
||||
- Wird per `.gitignore` ausgeschlossen.
|
||||
- Eine `.env.example` ohne Werte mitliefern — als Vorlage.
|
||||
- Format:
|
||||
```
|
||||
# Pflicht
|
||||
DB_USER=
|
||||
DB_PASSWORD=
|
||||
# ...
|
||||
```
|
||||
|
||||
## Phase 2: VPN-Binding für Webserver-Stacks
|
||||
|
||||
Wenn der User in Phase 0 als Zielhost `webserver` (waldperle) gewählt hat:
|
||||
|
||||
1. **Bind-Adresse abfragen** falls nicht bekannt:
|
||||
|
||||
> "Welche VPN-Interface-IP soll gebunden werden? (Standard `waldperle`: `10.7.0.1`)"
|
||||
|
||||
2. **Pro Service-Port entscheiden:**
|
||||
- Public (im Internet erreichbar, typisch Reverse-Proxy 80/443): ohne IP-Prefix.
|
||||
- Alle anderen Ports (Web-UIs, DBs, Admin-Interfaces, Metrics): an die VPN-IP binden.
|
||||
|
||||
3. **Begründung im Kopf des Compose-Files als Kommentar dokumentieren:**
|
||||
|
||||
```yaml
|
||||
# Webserver-Stack — interne Ports nur via VPN (10.7.0.1) erreichbar.
|
||||
# Nur 80/443 (Reverse-Proxy) sind öffentlich exponiert.
|
||||
```
|
||||
|
||||
## Phase 3: Generierung
|
||||
|
||||
### 3a: Verzeichnis-Layout
|
||||
|
||||
```
|
||||
<stack-name>/
|
||||
├── docker-compose.yaml
|
||||
├── INSTALL.md (Schritt-für-Schritt-Anleitung)
|
||||
├── .env (lokal, nicht in Git)
|
||||
├── .env.example (Vorlage in Git)
|
||||
├── .gitignore (mind. .env, dazu Daten-Verzeichnisse)
|
||||
└── <persistente Verzeichnisse, z.B. ./data, ./db, ./config>
|
||||
```
|
||||
|
||||
### 3b: Datei erzeugen
|
||||
|
||||
Generiere die `docker-compose.yaml` nach den Regeln aus Phase 1 + 2. Vor dem Schreiben prüfen, ob die Datei schon existiert — bei Update nur die nötigen Felder anfassen, vorhandene Kommentare/Strukturen erhalten.
|
||||
|
||||
### 3c: Persistente Verzeichnisse anlegen
|
||||
|
||||
Nur als Hinweis an den User — nicht selbst per `mkdir` ausführen. Die konkreten Befehle landen in der `INSTALL.md` (3e).
|
||||
|
||||
### 3d: `.env.example` erzeugen
|
||||
|
||||
Liste alle in der Compose-Datei referenzierten `${VARIABLE}` mit leerem Wert. Sortierung: erst Pflicht, dann optional. Jede Variable mit Einzeiler-Kommentar, wozu sie dient.
|
||||
|
||||
### 3e: `INSTALL.md` erzeugen (Pflicht)
|
||||
|
||||
Die `INSTALL.md` enthält **nur stack-spezifische Inhalte** — alles, was der Leser nicht aus der allgemeinen Docker-Compose-Kenntnis weiss. Allgemeines (was Docker ist, dass `docker compose up -d` startet, dass `.env` aus `.env.example` kopiert wird) **nicht aufnehmen**.
|
||||
|
||||
#### Was rein muss (sofern für den Stack zutreffend)
|
||||
|
||||
- **Header:** Stack-Name, Zielhost, Recherche-Datum (aus Phase 0/0.5)
|
||||
- **Externe Netzwerke** dieses Stacks: konkrete Namen, die existieren müssen (`reverse-proxy`, `public`, `openhab`, …)
|
||||
- **Persistente Verzeichnisse** dieses Stacks: exakte Pfade, erforderliche UID/GID, Sonderfälle (z.B. PostgreSQL braucht UID des Postgres-Users)
|
||||
- **Pflicht-Env-Vars** dieses Stacks: was der Wert bedeutet, Format-Constraints, Generierungs-Hinweis (z.B. "32-Byte-Hex", "openssl rand -hex 32")
|
||||
- **Initial-Zugang**: Default-Admin-Login, Setup-Wizard-URL, erstes Token aus den Logs ziehen — was dieses Image konkret macht
|
||||
- **Reverse-Proxy-Einstellungen** für DIESEN Service: Ziel-Container + Port, Pfad-Besonderheiten, WebSocket-Pflicht, Trust-Proxy-Hops, spezielle Header
|
||||
- **Ports und Erreichbarkeit** dieses Stacks: konkrete URLs, bei `webserver` mit `10.7.0.1` und Public-Variante
|
||||
- **Backup-relevante Pfade** und stack-spezifische Pre-Backup-Schritte (z.B. `pg_dumpall` vor dem Volume-Backup)
|
||||
- **Update-Besonderheiten** dieses Images (aus Phase 0.5): Major-Migrations-Skripte, deprecated Env-Vars die ersetzt werden müssen, Reihenfolge bei Multi-Service-Updates
|
||||
- **Stack-spezifische Healthcheck-/Troubleshooting-Hinweise**: bekannte Fehler des Images, übliche Logmeldungen die Warnung sind aber keine Probleme, container-spezifische Reset-Schritte
|
||||
|
||||
#### Was raus bleibt
|
||||
|
||||
- Wie man Docker installiert
|
||||
- Generische `docker compose`-Befehle ohne stack-spezifischen Kontext
|
||||
- "cp .env.example .env" und ähnliche Standardschritte
|
||||
- Allgemeine Compose-Theorie
|
||||
|
||||
#### Quellen für den Inhalt
|
||||
|
||||
- Phase 0.5 (Container-Recherche) — Breaking Changes, Pflicht-Env-Vars, Volume-Pfade
|
||||
- Phase 1.5 / 2 — Ports, VPN-Binding
|
||||
- Compose-Datei selbst — Bind-Mounts, Netzwerke, depends_on-Reihenfolge
|
||||
- Bei Lücken: erneut Doku des Images via Context7/WebFetch konsultieren, nicht raten
|
||||
|
||||
Bei Update eines bestehenden Stacks: existierende `INSTALL.md` lesen, gezielt ergänzen — nicht überschreiben, keine Sektionen ohne neuen Inhalt einfügen.
|
||||
|
||||
## Phase 4: Validierung
|
||||
|
||||
```bash
|
||||
# Auf dem Zielhost (automation/controller/webserver via SSH, oder lokal):
|
||||
docker compose -f <pfad>/docker-compose.yaml config
|
||||
```
|
||||
|
||||
Erfolgreich heisst: keine Warnings, kein "version is obsolete", alle `${VAR}` auflösbar (mit gefüllter `.env`).
|
||||
|
||||
Bei Fehlern: korrigieren, erneut validieren.
|
||||
|
||||
## Phase 5: Test-Empfehlung
|
||||
|
||||
```bash
|
||||
# Trockenlauf
|
||||
docker compose -f <pfad>/docker-compose.yaml config
|
||||
|
||||
# Start
|
||||
cd <pfad> && docker compose up -d
|
||||
|
||||
# Logs prüfen
|
||||
docker compose logs -f <service>
|
||||
|
||||
# Status
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
Warte auf User-Rückmeldung, ob der Stack erfolgreich gestartet ist.
|
||||
|
||||
## Phase 6: Abschluss via /ship
|
||||
|
||||
Wenn die Compose-Datei in einem Git-Repo liegt: `/ship` aufrufen.
|
||||
|
||||
Liegt sie nur lokal auf dem Host (z.B. direkt unter `/docker/<name>/` auf `automation`/`waldperle`, ohne Git): User erinnern, die Konfiguration manuell ins Backup aufzunehmen.
|
||||
|
||||
## Pitfalls — explizite Verbote
|
||||
|
||||
- Kein `version:`-Key (obsolet).
|
||||
- Keine impliziten Image-Defaults für relevante Konfiguration.
|
||||
- Keine ungequoteten Port-Mappings.
|
||||
- Keine absoluten Pfade in Bind-Mounts.
|
||||
- Keine Secrets im Klartext in der Compose-Datei.
|
||||
- Keine named Volumes für persistente Daten.
|
||||
- Auf `webserver` (waldperle): kein ungebundener interner Port — sonst public.
|
||||
- Keine Port-Angabe ohne vorherige Belegungs-Analyse aus Phase 1.5.
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
description: Domänenunabhängiges Review für beliebige Projektdokumente — Bauanleitungen, Analysen, Hardware-Pläne, Konzepte. Erkennt die Domäne automatisch, leitet passende Prüfkategorien ab und lässt den User wählen. Aktivieren wenn ein nicht-Software-Dokument auf Vollständigkeit, Korrektheit und Risiken geprüft werden soll. Nicht verwenden für Software-Pläne (dafür /plan-review) oder Code-Reviews (dafür /code-review).
|
||||
model: claude-opus-4-6
|
||||
---
|
||||
|
||||
# /project-review — Domänenunabhängiges Dokument-Review
|
||||
|
||||
Argument: Pfad zu einer Datei oder einem Verzeichnis. Ohne Argument: User fragen.
|
||||
|
||||
## Phase 1 — Dokument einlesen und Domäne erkennen
|
||||
|
||||
1. Lies das Zieldokument (bei Verzeichnis: alle `.md`-Dateien darin).
|
||||
2. Lies eine vorhandene `CLAUDE.md` im selben Verzeichnis oder Elternverzeichnis (falls vorhanden).
|
||||
3. Bestimme die **Domäne** anhand des Inhalts. Beispiele:
|
||||
|
||||
| Signal im Dokument | Domäne |
|
||||
|---|---|
|
||||
| Schaltpläne, Verdrahtung, Sensoren, 230V, Relais | Elektrotechnik / Hardware |
|
||||
| Heizlast, COP, Wärmepumpe, Pufferspeicher, Hydraulik | Haustechnik / Thermodynamik |
|
||||
| Statik, Fundament, Beton, Bewehrung | Bau / Konstruktion |
|
||||
| Rezepte, Zutaten, Garzeiten | Kochen |
|
||||
| Workflow, Prozess, Organisation | Projektmanagement |
|
||||
|
||||
Nenne die erkannte Domäne dem User in einem Satz.
|
||||
|
||||
## Phase 2 — Prüfkategorien ableiten und zur Auswahl stellen
|
||||
|
||||
Leite **4–8 Prüfkategorien** aus Domäne und Dokumentinhalt ab. Jede Kategorie besteht aus:
|
||||
- **Name** (kurz, z.B. "Elektrosicherheit")
|
||||
- **Beschreibung** (ein Satz, was geprüft wird)
|
||||
- **Typische Prüfpunkte** (2–4 konkrete Fragen)
|
||||
|
||||
Die Kategorien sollen das Dokument vollständig abdecken. Typische Muster:
|
||||
|
||||
**Immer dabei (domänenübergreifend):**
|
||||
- Vollständigkeit — fehlen Abschnitte, Schritte, Angaben?
|
||||
- Korrektheit — sind Fakten, Berechnungen, Angaben nachvollziehbar und widerspruchsfrei?
|
||||
- Risiken — was kann schiefgehen, was fehlt als Fallback?
|
||||
|
||||
**Domänenspezifisch ableiten**, z.B.:
|
||||
- Elektrotechnik: Sicherheit (Absicherung, Schutzleiter, Spannungsfreiheit), Komponentenwahl, Diagnose
|
||||
- Haustechnik: Dimensionierung, Regelstrategie, Normen/Förderung
|
||||
- Bau: Statik, Materialwahl, Bauvorschriften
|
||||
|
||||
Stelle die Kategorien dem User mit `AskUserQuestion` zur Auswahl (multiSelect). Vorauswahl: alle.
|
||||
|
||||
## Phase 3 — Review durchführen
|
||||
|
||||
Prüfe das Dokument anhand der gewählten Kategorien. Für jede Kategorie:
|
||||
|
||||
1. Lies die relevanten Abschnitte nochmals gezielt
|
||||
2. Prüfe jeden der typischen Prüfpunkte
|
||||
3. Bewerte: OK / Risiko / Blocker
|
||||
|
||||
**Bewertungsmaßstab:**
|
||||
- **OK**: Korrekt und vollständig dokumentiert
|
||||
- **Risiko**: Funktioniert vermutlich, aber Lücke oder Ungenauigkeit — bewusst abwägen
|
||||
- **Blocker**: Fehler, fehlende sicherheitskritische Information, oder Widerspruch — vor Umsetzung klären
|
||||
|
||||
## Ausgabe-Format
|
||||
|
||||
```
|
||||
## Project-Review: [Dokumenttitel]
|
||||
|
||||
**Domäne:** [erkannte Domäne]
|
||||
**Dokument:** [Dateipfad(e)]
|
||||
**Geprüfte Kategorien:** [Liste]
|
||||
|
||||
---
|
||||
|
||||
### [Kategorie 1]
|
||||
|
||||
#### ✅ OK
|
||||
- [Befund mit Verweis auf Abschnitt/Zeile]
|
||||
|
||||
#### ⚠️ Risiko
|
||||
- [Problem] — **Empfehlung:** [konkreter Vorschlag]
|
||||
|
||||
#### ❌ Blocker
|
||||
- [Problem] — **Muss geklärt werden:** [was fehlt]
|
||||
|
||||
---
|
||||
|
||||
### [Kategorie 2]
|
||||
[...]
|
||||
|
||||
---
|
||||
|
||||
## Gesamtbewertung
|
||||
|
||||
| Kategorie | Ergebnis |
|
||||
|---|---|
|
||||
| [Name] | ✅ / ⚠️ / ❌ |
|
||||
|
||||
**Empfehlung:** [Umsetzung freigeben / Dokument zuerst anpassen — mit konkreten Punkten]
|
||||
```
|
||||
|
||||
## Regeln
|
||||
|
||||
- Nur echte Probleme als Blocker — keine "könnte man noch ergänzen"-Punkte
|
||||
- Konkrete Empfehlungen statt vager Hinweise
|
||||
- Bei Berechnungen: Nachrechnen und Ergebnis angeben
|
||||
- Bei Normen/Vorschriften: nur nennen wenn du dir sicher bist, sonst als "prüfenswert" markieren
|
||||
- Keine Lobhudelei — kurz bestätigen was stimmt, Fokus auf Findings
|
||||
Reference in New Issue
Block a user