diff --git a/.vault-tools/plan-kopie/LIESMICH.md b/.vault-tools/plan-kopie/LIESMICH.md index c747fe2..50b418f 100644 --- a/.vault-tools/plan-kopie/LIESMICH.md +++ b/.vault-tools/plan-kopie/LIESMICH.md @@ -5,7 +5,7 @@ title: "Planwerkzeuge: Plan auf eine Kopie anwenden und jede Welle dagegen vergl summary: "Skripte für die Standardschleife bei Plänen mit ausgeschriebenem Code — schlanke Kopie bauen, Plan auf die Kopie anwenden, je Welle Menge und Bytes vergleichen, Aus-wird-Blöcke prüfen, Codeblöcke auf unsichtbare Zeichen absuchen, Escape-Folgen per Codepunkt setzen. Entstanden 2026-09-17 in der Trading Plattform (Teil 3b)." tags: [vault-tools, codeworkflow] created: 2026-09-17 -updated: 2026-10-09 +updated: 2026-10-13 --- # Planwerkzeuge für die Kopie mit angewendetem Plan @@ -26,6 +26,8 @@ Die Regel dazu steht in [[AI/Regelwerk/codeworkflow|codeworkflow.md]] („Die Ko 8. **Mutanten fahren (seit 2026-09-27):** `mutanten_lauf.py --wurzel --mutanten /mutanten.toml [--nur ID,…] [--nur-muster] [--bericht ] [--zeitlimit ]`, in WSL mit einem Python ab 3.11. Die Mutanten stehen nicht mehr als Prosa im Plan, sondern in `mutanten.toml` im Planordner. Das Format, die Status und die Abdichtung stehen in `mutanten_lauf_ENTWURF.md`. Das Skript verweigert eine Wurzel mit `.git` und jeden Pfad aus `ohne`. Seit 2026-10-06 sucht es die pytest-Konfiguration wie pytest selbst, aber nie oberhalb der Wurzel, und gibt sie mit `-c` vor; eine fremde `pyproject.toml` im Windows-Temp stört damit nicht mehr, und ein Bedienfehler von pytest steht mit dem Ende der pytest-Ausgabe im Befund statt als `FEHLT` je Test (Entwurf, Abschnitt 3, „Konfiguration“ und „Abbruch von pytest“). Ob die Suche pytest selbst entspricht, prüft `mutanten_lauf_probe_konfig.py ` mit dem Python eines Venvs, in dem pytest liegt (26 Aufbauten, Exit 0 ohne Abweichung). Es fährt zuerst die Grundlinie, dann je Mutant nur die genannten Tests, setzt die Datei zurück und prüft das per SHA-256. Je Test gibt es die Fehlermeldung mit aus; ob ein Test an der gemeinten Stelle fällt, liest man dort. `--nur-muster` prüft nur, ob jedes Muster genau einmal dasteht, ohne pytest. Die Kopie legt man in WSL unter `/tmp` an (`cp -a`, danach `__pycache__` löschen). Auf `/mnt/d` läuft es auch, nur langsamer. **Aus der Git-Bash ein Skript nie mit `wsl … bash /mnt/d/…` starten:** Die Git-Bash macht aus dem Pfad einen Windows-Pfad. Aus der PowerShell starten oder `MSYS_NO_PATHCONV=1` davorsetzen. Ein SIGTERM auf `mutanten_lauf.py` selbst (etwa `timeout -k` oder ein Hilfsagent, der abgebrochen wird) löst dort einen eigenen Handler aus: Der laufende Mutant wird per SHA-Probe zurückgesetzt, die Temp-Ordner werden geräumt, und die Ausgabe trägt `ABBRUCH: SIGTERM` (Treiber-Fall 35, `mutanten_lauf_treiber.py`). **Die Plankritik fährt die Mutanten selbst (seit 2026-09-27, nachgebessert 2026-09-28):** Liegt `mutanten.toml` im Planordner, startet `plankritik.js` in der Vorbereitung `mutanten_lauf_kopie.sh`. Das Skript kopiert die Kopie mit angewendetem Plan nach `/tmp` in WSL, fährt dort den Läufer in einem eigenen Netz-Namensraum (`unshare --map-current-user --net`, gemessen in WSL Debian als Benutzer `claude`: UID innen bleibt 1000, nur Schnittstelle `lo`, jede Verbindung nach außen scheitert; seit 2026-10-09 mit `lo` oben, siehe Nachtrag unten) und gibt die Ausgabe an die Linse `arithmetik`. Fehlt `unshare` ohne Netz, fehlen `ip`, `setpriv` oder `python3` oder kommt `lo` im Namensraum nicht hoch, bricht das Skript vorher mit `MUTANTEN_LAUF_FEHLER` ab, statt ungesperrt oder ohne `lo` zu laufen. Der Arg `mutanten` wählt `voll` (Vorgabe), `muster` oder `aus`; die Rückgabe trägt dazu `mutanten_status` (`aus`, `ohne_kopie`, `keine_toml`, `ohne_ergebnis` oder `exit_`) und `mutanten_modus` (`voll`, `muster`, `aus`). Der Wrapper gibt vor dem Lauf eine Zeile `modus: voll` oder `modus: muster (…)` aus; bei `modus: muster` weiß die Linse, dass kein Mutant gefahren ist. Der ganze Lauf hat 540 s (`BUDGET_S`, `timeout -k 15`), weil das Bash-Werkzeug eines Hilfsagenten nach zehn Minuten endet; eine überschrittene Grenze meldet die Zeile `ZEITGRENZE`. Je Mutant gilt zusätzlich ein Zeitlimit `ZEITLIMIT_S` (Vorgabe 120 s, muss kleiner sein als `BUDGET_S`): B1 mit 74 Mutanten brauchte 168 s gesamt, rund 2 s je Mutant. Bei Exit 2 oder 3 des Läufers sagt die Linse, der Lauf sei vor oder während der Mutanten gescheitert. Pfade übergibt man mit Vorwärtsschrägstrichen: `wsl -d Debian -- …` reicht die Befehlszeile an die Linux-Shell, und die verschluckt Backslashes. +9. **Korrektur als Ersetzungsliste (seit 2026-10-13):** `ersetzungen_einsetzen.py [--pruefen]` setzt eine Liste aus Abschnitten `## Ersetzung N — Titel` mit `Datei:`, `Alt:` und `Neu:` (Blöcke zwischen `~~~~text` und `~~~~`) der Reihe nach ein. Jeder Alt-Text muss zum Zeitpunkt seiner Ersetzung genau einmal in seiner Datei stehen, sonst schreibt das Skript gar nichts. Erst mit `--pruefen`, dann ohne; danach Schritt 4 und eine frisch gebaute Kopie. Die Korrekturdatei liest das Skript relativ zum aktuellen Ordner, nicht zum Planordner. Belegt am 2026-10-13 im Paket FP der Trading Plattform: 129 Ersetzungen in sechs Dateien, Prüflauf und Einsetzen ohne Befund. + ## Was belegt ist Seit 2026-09-27 gibt es `mutanten_lauf.py` und `mutanten_lauf_treiber.py` (Punkt 3 der Arbeitsweise vom 2026-09-26). Der Treiber fährt die 35 Angriffsfälle aus dem Entwurf auf einem kleinen Projekt unter `/tmp` und prüft nach jedem Fall, dass das Projekt byte-gleich zurückgesetzt ist. Am selben Tag hat der Koordinator neun Fehler in eine Kopie des Läufers eingebaut, und der Treiber hat jeden an seinem Fall gefangen: diff --git a/.vault-tools/plan-kopie/ersetzungen_einsetzen.py b/.vault-tools/plan-kopie/ersetzungen_einsetzen.py new file mode 100644 index 0000000..54ec7b0 --- /dev/null +++ b/.vault-tools/plan-kopie/ersetzungen_einsetzen.py @@ -0,0 +1,81 @@ +"""Setzt eine Ersetzungsliste eines Planers in die Taskdateien ein, alles oder nichts. + +Format je Abschnitt (siehe Auftrag an den Planer): + ## Ersetzung N — Titel + Datei: + Alt: + ~~~~text + ... + ~~~~ + Neu: + ~~~~text + ... + ~~~~ +Jeder Alt-Text muss zum Zeitpunkt seiner Ersetzung genau einmal in der Datei stehen. Bricht beim ersten +Verstoss ab, ohne eine Datei zu schreiben. Mit --pruefen nur pruefen, nicht schreiben. +Aufruf: python ersetzungen_einsetzen.py [--pruefen] +""" + +import sys +from pathlib import Path + +ordner = Path(sys.argv[1]) +korrektur = Path(sys.argv[2]) +nur_pruefen = "--pruefen" in sys.argv[3:] + +zeilen = korrektur.read_text(encoding="utf-8").split("\n") +ersetzungen = [] +i = 0 +while i < len(zeilen): + if not zeilen[i].startswith("## Ersetzung "): + i += 1 + continue + titel = zeilen[i] + i += 1 + datei = alt = neu = None + while i < len(zeilen) and not zeilen[i].startswith("## Ersetzung "): + z = zeilen[i] + if z.startswith("Datei:"): + datei = z.split(":", 1)[1].strip().strip("`") + elif z.strip() in ("Alt:", "Neu:"): + art = z.strip()[:-1] + if i + 1 >= len(zeilen) or not zeilen[i + 1].startswith("~~~~"): + sys.exit(f"{titel}: nach {art}: fehlt die Zeile ~~~~text") + j = i + 2 + inhalt = [] + while j < len(zeilen) and zeilen[j].rstrip() != "~~~~": + inhalt.append(zeilen[j]) + j += 1 + if j >= len(zeilen): + sys.exit(f"{titel}: {art}-Block ohne schliessende ~~~~") + text = "\n".join(inhalt) + if art == "Alt": + alt = text + else: + neu = text + i = j + i += 1 + if datei is None or alt is None or neu is None: + sys.exit(f"{titel}: Datei, Alt oder Neu fehlt") + ersetzungen.append((titel, datei, alt, neu)) + +texte: dict[str, str] = {} +for titel, datei, alt, neu in ersetzungen: + if datei not in texte: + pfad = ordner / datei + if not pfad.is_file(): + sys.exit(f"{titel}: Datei {datei} gibt es nicht") + texte[datei] = pfad.read_text(encoding="utf-8") + n = texte[datei].count(alt) + if n != 1: + sys.exit(f"{titel}: Alt-Text steht {n}-mal in {datei} statt einmal; nichts geschrieben") + texte[datei] = texte[datei].replace(alt, neu) + print(f"ok {titel} ({datei})") + +print(f"{len(ersetzungen)} Ersetzungen in {len(texte)} Datei(en) geprueft") +if nur_pruefen: + print("nur geprueft, nichts geschrieben") +else: + for datei, text in texte.items(): + (ordner / datei).write_text(text, encoding="utf-8", newline="\n") + print("geschrieben:", ", ".join(sorted(texte))) diff --git a/AI/Regelwerk/codeworkflow.md b/AI/Regelwerk/codeworkflow.md index 99cfc42..1e92884 100644 --- a/AI/Regelwerk/codeworkflow.md +++ b/AI/Regelwerk/codeworkflow.md @@ -1,11 +1,11 @@ --- type: resource -status: reference +status: pending-review title: Codeworkflow summary: Arbeitsweise für Code-Projekte — Standardschleife, Planaufteilung je Task, Subagenten und ihre Modellstufen und Effort, Gegenprüfung, Lauf mit echten Daten, Aufgabenmanagement. tags: [regelwerk, codeworkflow, code-projekte] created: 2026-08-03 -updated: 2026-10-09 +updated: 2026-10-13 --- # Codeworkflow @@ -83,7 +83,7 @@ Für jede Prüfung, die eine **offene Menge** einschränkt (verbotene Aktionen, Faustregel: Lässt sich die Nachbesserung in einem Auftrag beschreiben, der für sich steht — geänderte Planstelle, Fehlerbild, erwartete Fehlermeldung —, dann ein frischer Subagent. Fortsetzen nur, wenn der Verlauf selbst gebraucht wird, etwa bei einer Fehlersuche über mehrere Runden. -**Nachbesserung nach einer ausgeführten Welle als Delta, nicht als Neufassung.** Der `planer` schreibt dann neue Taskdateien mit `Aus:`/`wird:`-Blöcken und für ältere Tasks eine Folgenliste. Die Regel für ihn steht in `.claude/agents/planer.md`, weil er das Regelwerk nicht lädt. Dein Teil: Die Plankritik liest nur die neuen Tasks und die Folgenliste (`materialisieren.tasks`). Nach dem Anwenden läuft `plan_bloecke.py nach` auf einer frisch gebauten Kopie. Die Folgenliste setzt ein Executer ein, danach prüfst du die Zeilenenden mit `git ls-files --eol` oder `file`, nicht mit `grep`. Und die `spec.json` des Rot-Nachweises bekommt die neuen Tasks. Gemessen im Hub am 2026-09-24: rund 20 Minuten je Runde schneller als die Neufassung, drei gegen drei Runden, nicht kontrolliert. Am 2026-09-23 waren beim Neuschreiben 10 von 17 Befunden der Nachprüfung durch das Umschreiben selbst entstanden. Für die Korrektur nach der Plankritik vor der ersten Ausführung ist das Delta nicht belegt. +**Nachbesserung nach einer ausgeführten Welle als Delta, nicht als Neufassung.** Der `planer` schreibt dann neue Taskdateien mit `Aus:`/`wird:`-Blöcken und für ältere Tasks eine Folgenliste. Die Regel für ihn steht in `.claude/agents/planer.md`, weil er das Regelwerk nicht lädt. Dein Teil: Die Plankritik liest nur die neuen Tasks und die Folgenliste (`materialisieren.tasks`). Nach dem Anwenden läuft `plan_bloecke.py nach` auf einer frisch gebauten Kopie. Die Folgenliste setzt ein Executer ein, danach prüfst du die Zeilenenden mit `git ls-files --eol` oder `file`, nicht mit `grep`. Und die `spec.json` des Rot-Nachweises bekommt die neuen Tasks. Gemessen im Hub am 2026-09-24: rund 20 Minuten je Runde schneller als die Neufassung, drei gegen drei Runden, nicht kontrolliert. Am 2026-09-23 waren beim Neuschreiben 10 von 17 Befunden der Nachprüfung durch das Umschreiben selbst entstanden. Auch die Korrektur nach der Plankritik vor der ersten Ausführung geht als Delta: Der `planer` liefert eine Ersetzungsliste im Format von `.vault-tools/plan-kopie/ersetzungen_einsetzen.py` (Gebrauch in `LIESMICH.md` dort, Schritt 9) und schreibt keine Datei neu; du setzt sie nach dem Prüflauf ein und baust die Kopie neu. Beleg vom 2026-10-13 („Trading Plattform“, Paket FP, vier Taskdateien mit je rund 1300 Zeilen): 129 Ersetzungen in sechs Dateien, Prüflauf ohne Befund, jede der 73 roten Zeilen des Plans beim ersten Messen wörtlich; nicht kontrolliert. **Die Nachbesserung folgt derselben Reihenfolge wie der Task selbst:** erst die neuen Tests, Fehlschlag im Wortlaut beobachten lassen, dann die Implementierung. Wer beides gleichzeitig schreiben lässt, bekommt einen grünen Lauf ohne Beleg, dass die neuen Tests je etwas gemessen haben.