This commit is contained in:
2026-07-05 10:37:41 +02:00
5 changed files with 508 additions and 11 deletions
+2
View File
@@ -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
View File
@@ -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).
+16 -10
View File
@@ -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"
+353
View File
@@ -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.
+104
View File
@@ -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