#!/usr/bin/env bash # Mutantenlauf auf einer Kopie der Kopie, gedacht fuer den Hilfsagenten der Plankritik # (.claude/workflows/plankritik.js). Nie in der uebergebenen Kopie selbst arbeiten: Die Linsen # fahren dort gleichzeitig Tests, und mutanten_lauf.py setzt Dateien zwischenzeitlich zurueck. # # Aufruf: bash mutanten_lauf_kopie.sh # modus ist "voll" oder "muster". Der ganze Lauf ist auf BUDGET_S Sekunden begrenzt (Vorgabe 540), # weil das Bash-Werkzeug eines Hilfsagenten nach hoechstens zehn Minuten endet; eine ueberschrittene # Grenze meldet die Zeile ZEITGRENZE, die bis dahin gefahrenen Mutanten stehen in der Ausgabe. # Beide Pfade sind Windows-Pfade (auch Kurznamen wie PASCAL~1), # die dieses Skript selbst mit wslpath in WSL-Pfade umwandelt. # # Nachbesserung 2026-09-28 (Pruefbericht vom selben Tag, Befunde 1 bis 4, 7): # - Netzsperre: mutanten_lauf.py laeuft in einem eigenen Netz-Namensraum # (unshare --map-current-user --net). Gemessen in WSL Debian als Benutzer claude # (util-linux 2.41.5): UID innen bleibt 1000 wie aussen, nur Schnittstelle lo, jeder # Verbindungsversuch nach aussen (etwa 127.0.0.1:8144, wo das Dashboard lauscht) scheitert. # Fehlt unshare ohne Netz, bricht das Skript vorher ab statt einen ungesperrten Mutantenlauf # still durchzulassen. # - Ungepufferte Ausgabe: python3 -u, damit bei einer Zeitgrenze die bis dahin gefahrenen # Mutanten wirklich in der Ausgabe stehen. # - Zeitlimit je Mutant: ZEITLIMIT_S (Vorgabe 120 s, kleiner als BUDGET_S) geht als --zeitlimit # an mutanten_lauf.py; B1 mit 74 Mutanten brauchte 168 s gesamt, rund 2 s je Mutant. # - Modus-Zeile vor dem Lauf ("modus: voll" / "modus: muster (...)"), damit die Linse weiss, ob # ueberhaupt ein Mutant gefahren ist. # - Aufraeumen bei SIGTERM: timeout -k 15 schickt nach der Frist noch ein SIGKILL hinterher, falls # mutanten_lauf.py nach dem SIGTERM nicht rechtzeitig endet; sein eigener SIGTERM-Handler setzt # die Zieldatei vorher zurueck und raeumt seine Temp-Ordner auf. # - chmod 700 auf die Kopie der Kopie direkt nach dem Kopieren. set -u KOPIE_WIN="${1:-}" PLANORDNER_WIN="${2:-}" MODUS="${3:-}" if [ -z "$KOPIE_WIN" ] || [ -z "$PLANORDNER_WIN" ] || [ -z "$MODUS" ]; then echo "MUTANTEN_LAUF_FEHLER: Aufruf braucht " exit 2 fi if [ "$MODUS" != "voll" ] && [ "$MODUS" != "muster" ]; then echo "MUTANTEN_LAUF_FEHLER: modus muss voll oder muster sein, nicht $MODUS" exit 2 fi BUDGET_S="${BUDGET_S:-540}" ZEITLIMIT_S="${ZEITLIMIT_S:-120}" if ! [ "$ZEITLIMIT_S" -lt "$BUDGET_S" ] 2>/dev/null; then echo "MUTANTEN_LAUF_FEHLER: ZEITLIMIT_S ($ZEITLIMIT_S) muss kleiner sein als BUDGET_S ($BUDGET_S)" exit 2 fi if ! unshare --map-current-user --net true 2>/dev/null; then echo "MUTANTEN_LAUF_FEHLER: unshare ohne Netz nicht moeglich" exit 2 fi KOPIE=$(wslpath -u "$KOPIE_WIN") PLANORDNER=$(wslpath -u "$PLANORDNER_WIN") if [ ! -d "$KOPIE" ]; then echo "MUTANTEN_LAUF_FEHLER: Kopie fehlt: $KOPIE" exit 2 fi if [ ! -d "$PLANORDNER" ]; then echo "MUTANTEN_LAUF_FEHLER: Planordner fehlt: $PLANORDNER" exit 2 fi MUTANTEN_TOML="$PLANORDNER/mutanten.toml" if [ ! -f "$MUTANTEN_TOML" ]; then echo "KEINE_MUTANTEN_TOML" exit 0 fi TMPKOPIE=$(mktemp -d /tmp/plankritik-mut-XXXXXX) trap 'rm -rf "$TMPKOPIE"' EXIT cp -a "$KOPIE/." "$TMPKOPIE/" chmod 700 "$TMPKOPIE" find "$TMPKOPIE" -name __pycache__ -type d -prune -exec rm -rf {} + MUSTER_SCHALTER="" if [ "$MODUS" = "muster" ]; then MUSTER_SCHALTER="--nur-muster" echo "modus: muster (nur Musterpruefung, kein Mutant gefahren)" else echo "modus: voll" fi timeout -k 15 "$BUDGET_S" unshare --map-current-user --net \ python3 -u /mnt/d/Claude/.vault-tools/plan-kopie/mutanten_lauf.py \ --wurzel "$TMPKOPIE" --mutanten "$MUTANTEN_TOML" --zeitlimit "$ZEITLIMIT_S" $MUSTER_SCHALTER EXITCODE=$? if [ "$EXITCODE" -eq 124 ]; then echo "ZEITGRENZE: Mutantenlauf nach $BUDGET_S s abgebrochen; Mutanten ohne Zeile oben sind nicht gefahren" fi echo "EXIT mutanten_lauf: $EXITCODE" exit $EXITCODE