feat: scripts/smoke_test.sh + AGENTS.md R4 verankert

Ein Aufruf startet den Server temporaer auf eigenem Port, prueft alle
bekannten Endpunkte und raeumt sich selbst auf. Agenten muessen nichts
ueber Hintergrund-Jobs, nohup oder Port-Cleanup wissen.

- scripts/smoke_test.sh: trap-basierter Cleanup, setsid-unabhaengig
  via prozessgruppen-fremdem pkill-Fallback, eindeutiger Marker-Name
  in sys.argv[0], 20x100ms Wartezeit aufs Port-Binding.
- AGENTS.md R4: verweist statt auf manuelle curl-Aufrufe auf
  ./scripts/smoke_test.sh; TL;DR und Einstiegs-Block aktualisiert.

Verifiziert: exit 0, Port nach Durchlauf frei (kein Leak).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michael 2026-04-24 16:46:13 +02:00
parent 7b471abbf0
commit 3f35cff338
2 changed files with 84 additions and 3 deletions

View file

@ -8,7 +8,7 @@ Diese Datei richtet sich an **Coding-Agenten (Claude, Codex u.ä.), die in diese
1. Erst lesen (Zeilen der Zielstelle nennen), dann ändern. 1. Erst lesen (Zeilen der Zielstelle nennen), dann ändern.
2. Erst ausführen, dann berichten. Prosa ohne Tool-Call ≠ Ausführung. 2. Erst ausführen, dann berichten. Prosa ohne Tool-Call ≠ Ausführung.
3. UI-/Server-Änderung → Server starten + `curl` → Ergebnis zitieren. 3. UI-/Server-Änderung → `./scripts/smoke_test.sh` → Ausgabe zitieren.
4. Nach Refactor: `grep -c <name>` = 1. Sonst nicht fertig. 4. Nach Refactor: `grep -c <name>` = 1. Sonst nicht fertig.
5. Doku beschreibt Code, nicht die Arbeit am Code. 5. Doku beschreibt Code, nicht die Arbeit am Code.
@ -41,12 +41,16 @@ Für jede angekündigte Änderung muss **mindestens eines** dieser Artefakte vor
Fehlen diese Belege, ist die Aufgabe nicht erledigt — die Tool-Aufrufe absetzen und erneut prüfen. Fehlen diese Belege, ist die Aufgabe nicht erledigt — die Tool-Aufrufe absetzen und erneut prüfen.
### R4 UI-/Server-Änderungen gegen das laufende System prüfen ### R4 UI-/Server-Änderungen gegen das laufende System prüfen
Bei Änderungen an `web/serve.py` oder `web/index.html`: Server starten, betroffene Endpunkte per `curl` testen, und das Ergebnis im Bericht **zitieren**. Beispiel: Bei Änderungen an `web/serve.py` oder `web/index.html`: führe den Smoke-Test aus und zitiere die Ausgabe im Bericht:
```bash ```bash
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8081/templates.json ./scripts/smoke_test.sh
``` ```
Das Skript startet den Server temporär auf einem eigenen Port (Default 8082), prüft alle bekannten Endpunkte und räumt sich selbst auf. Du musst nichts über Hintergrund-Jobs, `&`, `nohup` oder Port-Cleanup wissen. Exit-Code 0 = alle 200; sonst 1.
Für Änderungen, die der Smoke-Test nicht abdeckt (neuer Endpunkt, geänderte Response, JSON-Edit-Flow im Browser), zusätzlich gezielten `curl`-Aufruf oder Browser-Check ergänzen.
„Sieht korrekt aus" ohne ausgeführten Test zählt nicht. „Sieht korrekt aus" ohne ausgeführten Test zählt nicht.
### R5 Nichts in die Doku schreiben, was nicht existiert ### R5 Nichts in die Doku schreiben, was nicht existiert
@ -66,6 +70,7 @@ Ein Commit = eine logische Änderung. Commit-Message im Stil der bestehenden His
```bash ```bash
python3 web/serve.py # startet Server auf http://localhost:8081 python3 web/serve.py # startet Server auf http://localhost:8081
./scripts/smoke_test.sh # temporärer Server + Endpunkt-Check + Cleanup (fuer R4)
./scripts/cleanup_server.sh # beendet hängende Instanzen ./scripts/cleanup_server.sh # beendet hängende Instanzen
python3 scripts/validate.py # validiert Template-Struktur python3 scripts/validate.py # validiert Template-Struktur
``` ```

76
scripts/smoke_test.sh Executable file
View file

@ -0,0 +1,76 @@
#!/bin/bash
# Startet web/serve.py temporaer, testet die wichtigsten Endpunkte,
# raeumt den Prozess auch bei Abbruch auf. Ein einziger Aufruf; der
# Agent muss nichts ueber Hintergrund-Jobs wissen.
#
# Nutzung: ./scripts/smoke_test.sh # Port 8082
# ./scripts/smoke_test.sh 9000 # anderer Port
# Exit-Code: 0 = alle Endpunkte 200, sonst 1.
set -u
PORT="${1:-8082}"
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
LOG="/tmp/smoke.$PORT.log"
EXIT_CODE=1
MARKER="SMOKE_TEST_SERVER_$PORT"
# Vorherige Instanzen dieses Smoke-Tests beenden
pkill -f "$MARKER" 2>/dev/null || true
sleep 0.1
# Server im Hintergrund starten (exec → $! ist der Python-Prozess)
cd "$ROOT"
python3 -c "
import sys; sys.argv[0] = '$MARKER'
sys.path.insert(0, 'web')
import http.server, socketserver
from serve import Handler
port = $PORT
print(f'Serving on http://localhost:{port}', flush=True)
with socketserver.TCPServer(('', port), Handler) as httpd:
httpd.serve_forever()
" > "$LOG" 2>&1 &
PID=$!
cleanup() {
kill "$PID" 2>/dev/null || true
wait "$PID" 2>/dev/null || true
# Defensiv: falls der Kill-Signal den Python-Prozess nicht trifft
pkill -f "$MARKER" 2>/dev/null || true
exit "$EXIT_CODE"
}
trap cleanup EXIT INT TERM
# Auf Port-Binding warten (max. 2s)
for i in $(seq 1 20); do
if curl -sf -o /dev/null "http://localhost:$PORT/"; then break; fi
sleep 0.1
done
# Wenn nicht erreichbar: Log ausgeben und fail
if ! curl -sf -o /dev/null "http://localhost:$PORT/"; then
echo "FAIL: Server nicht erreichbar auf Port $PORT"
echo "--- $LOG ---"
cat "$LOG"
EXIT_CODE=1
exit 1
fi
# Endpunkte testen
FAIL=0
for p in "/" "/index.html" "/templates.json" \
"/templates/system/commit_analysis.json" \
"/templates/system/code_reviewer.json" \
"/templates/system/summarizer.json" \
"/templates/user/email_draft.md" \
"/templates/user/brainstorming.md"; do
code=$(curl -s -o /dev/null -w '%{http_code}' "http://localhost:$PORT$p")
status="ok"
[ "$code" = "200" ] || { status="FAIL"; FAIL=1; }
printf '%-50s %s %s\n' "$p" "$code" "$status"
done
EXIT_CODE=$FAIL
if [ "$FAIL" -eq 0 ]; then
echo "--- alle $( (echo "/" "/index.html" "/templates.json" "/templates/system/commit_analysis.json" "/templates/system/code_reviewer.json" "/templates/system/summarizer.json" "/templates/user/email_draft.md" "/templates/user/brainstorming.md") | wc -w ) Endpunkte OK ---"
fi
exit $FAIL