Erste Fassung: Claude-Werkzeuge aus dem Vault

.vault-tools (ohne trading-plattform-pruefung und watermarks-remover),
.claude/agents, .claude/skills, .claude/workflows, .claude/settings.json.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Pascal MichelandClaude Opus 5.5 committed 2026-10-14 11:27:59 +02:00
commit fa1c26b407
137 files changed
+43926

No files matched your search

@@ -0,0 +1,634 @@
---
type: plan
title: "Mutantenläufer: Nachbesserung 2 (Aufräumliste der zweiten Prüfung)"
status: active
summary: "Delta zur Nachbesserung 1: Kopfzeile je Interpreter mit eigenem Fall, Kurzfassung bei Rückgabewert 3 aus der letzten INTERNALERROR-Zeile, lange Fassung im Bericht, Abschnittszeile mit splitlines, Probe mit Selbstprüfung und zwei neuen Aufbauten, Treiber-Fall 46, zwei Lücken benannt."
tags: [plan, vault-tools, pruefwerkzeuge]
created: 2026-10-06
updated: 2026-10-06
modell: sonnet
effort: low
modell_begruendung: "Jeder Block steht wörtlich da; Entwurf und LIESMICH tragen typografische Zeichen, die Haiku still ersetzt."
---
Übersicht: [LIESMICH der Planwerkzeuge](../../LIESMICH.md) · Vorgänger: [Nachbesserung 1](2026-10-06-pytest-konfiguration-nachbesserung-1.md)
# Mutantenläufer: Nachbesserung 2
## Warum so
Der zweite `pruefer` fand am 2026-10-06 keinen Verhaltensfehler mit Wirkung auf die Projekte, nur eine Aufräumliste, und hielt eine weitere volle Prüfrunde für entbehrlich. Umgesetzt:
| Befund | Hier |
|---|---|
| Kopfzeile je Interpreter ohne eigenen Fall (wichtig) | Fall 44 zählt genau zwei Kopfzeilen, je eine je Interpreter |
| Kurzfassung bei Rückgabewert 3 ohne Fall | Fall 46 (Mutant lässt `conftest.py` werfen). Dabei gefunden: „letzte Zeile“ war die Summenzeile `no tests ran …`; jetzt die letzte `INTERNALERROR>`-Zeile |
| `;` als Kommentarzeichen ohne Fall | Aufbau `tox_abschnitt_mit_semikolon` in der Probe |
| iniconfig trennt Zeilen mit `splitlines` | Läufer auch; Aufbau `tox_seitenvorschub_vor_abschnitt` |
| Lange Fassung unter einem Mutanten verloren | Bericht trägt beim Test `ausgabe`; Fälle 40 und 46 prüfen es |
| Probe prüft ihre Voraussetzung nicht | Selbstprüfung: ohne `-c` Rückgabewert 0 (außer `knoten_fehl*`), mit `conftest.py` im Aufbau mindestens eines geladen |
| `rootdir` unter der Wurzel, Zeilentrenner | als Lücken benannt |
| `safe_exists` fängt auch `ValueError` | `_existiert` ebenso |
| LIESMICH: Rohdaten `08-gegenprobe2/`, Satz zur Nachbesserung | nachgezogen |
**Gemessen vom Koordinator am 2026-10-06** (Rohdaten `AI/secondbrain/linux-haertung-after-install-memory/2026-10-06-c1-abschluss/roh/09-*`): neuer Stand 46 von 46; Stand nach Nachbesserung 1 rot bei 40, 43, 46; Varianten rot genau an ihrem Fall (eine Kopfzeile → 44 an beiden Stellen; erste statt letzte `INTERNALERROR>`-Zeile → 46; ohne den Zweig für 3 → 46; kaputtes `conftest.py` der ersten Probe → 43; ohne `;` → 43).
## Zieldateien
Pfade relativ zu `D:/Claude/.vault-tools/plan-kopie/`:
- `mutanten_lauf.py` (geändert)
- `mutanten_lauf_treiber.py` (geändert)
- `mutanten_lauf_probe_konfig.py` (geändert)
- `mutanten_lauf_ENTWURF.md` (geändert)
- `LIESMICH.md` (geändert)
## Verbote
- Nur die fünf Zieldateien ändern. Kein `git add`, kein `git commit`.
- Jeden Block byte-genau übernehmen; der ganze Aus-Block als `old_string`, der ganze wird-Block als `new_string`. Steht ein Aus-Block nicht genau einmal da oder weicht eine Ausgabe ab: anhalten und melden.
- `python -m py_compile` legt `__pycache__` an; den Ordner danach löschen.
## Schritt 1 — Tests zuerst: Treiber und Probe
### `mutanten_lauf_treiber.py`, Stelle 1 von 9
Aus:
```python
#!/usr/bin/env python3
"""Treiber fuer mutanten_lauf.py: 45 Angriffsfaelle auf einer Wegwerfkopie.
```
wird:
```python
#!/usr/bin/env python3
"""Treiber fuer mutanten_lauf.py: 46 Angriffsfaelle auf einer Wegwerfkopie.
```
### `mutanten_lauf_treiber.py`, Stelle 2 von 9
Aus:
```python
Interpretergruppen an erster und an letzter Stelle, ein ueberlanger Knoten.
"""
```
wird:
```python
Interpretergruppen an erster und an letzter Stelle, ein ueberlanger Knoten.
Fall 46 (Nachbesserung 2): Rueckgabewert 3 unter einem Mutanten, Kurzfassung in
der Mutantenzeile und lange Fassung im Bericht.
"""
```
### `mutanten_lauf_treiber.py`, Stelle 3 von 9
Aus:
```python
import hashlib
import os
```
wird:
```python
import hashlib
import json
import os
```
### `mutanten_lauf_treiber.py`, Stelle 4 von 9
Aus:
```python
))
ergebnis = starte_laeufer(wurzel, toml)
mutantenzeile = [z for z in ergebnis.stdout.split("\n") if z.startswith("M40 ")]
ok = (
```
wird:
```python
))
bericht = rahmen / "bericht40.json"
ergebnis = starte_laeufer(wurzel, toml, bericht_pfad=bericht)
mutantenzeile = [z for z in ergebnis.stdout.split("\n") if z.startswith("M40 ")]
try:
ausgabe_bericht = json.loads(bericht.read_text(encoding="utf-8"))["mutanten"][0]["tests"][0].get("ausgabe", "")
except (OSError, ValueError, KeyError, IndexError):
ausgabe_bericht = ""
ok = (
```
### `mutanten_lauf_treiber.py`, Stelle 5 von 9
Aus:
```python
and "--gibt-es-nicht" in mutantenzeile[0]
)
```
wird:
```python
and "--gibt-es-nicht" in mutantenzeile[0]
and "Ende der Ausgabe:" in ausgabe_bericht
)
```
### `mutanten_lauf_treiber.py`, Stelle 6 von 9
Aus:
```python
ausgabe = ergebnis.stdout + ergebnis.stderr
ok = (
```
wird:
```python
ausgabe = ergebnis.stdout + ergebnis.stderr
kopfzeilen = [z for z in ergebnis.stdout.split("\n") if z.startswith("pytest-konfiguration (Grundlinie, ")]
ok = (
```
### `mutanten_lauf_treiber.py`, Stelle 7 von 9
Aus:
```python
and "test_f1_positiv ist" not in ausgabe
)
```
wird:
```python
and "test_f1_positiv ist" not in ausgabe
and len(kopfzeilen) == 2
and sum(1 for z in kopfzeilen if z.startswith("pytest-konfiguration (Grundlinie, {0}): ".format(INTERPRETER))) == 1
and sum(1 for z in kopfzeilen if z.startswith("pytest-konfiguration (Grundlinie, {0}): ".format(zweit_python))) == 1
)
```
### `mutanten_lauf_treiber.py`, Stelle 8 von 9
Aus:
```python
FAELLE = [
```
wird:
```python
def fall_46(basis, projektpfad):
rahmen, wurzel, dateien = baue_rahmen(eigene_konfig=True, fremde_konfig=False, fremdes_conftest=False)
try:
(wurzel / "conftest.py").write_text("X = 1\n", encoding="utf-8")
dateien = dateien + [wurzel / "conftest.py"]
snap = snapshot(dateien)
toml = rahmen / "mutanten.toml"
schreibe_toml(toml, kopf() + (
"[[mutant]]\n"
"id = \"M46\"\n"
"datei = \"conftest.py\"\n"
"aus = '''\n"
"X = 1\n"
"'''\n"
"wird = '''\n"
"def pytest_collection_modifyitems(items):\n"
" raise RuntimeError(\"intern Fall 46\")\n"
"'''\n"
"rot_bei = [\"tests/test_modul.py::test_f1_positiv\"]\n"
))
bericht = rahmen / "bericht46.json"
ergebnis = starte_laeufer(wurzel, toml, bericht_pfad=bericht)
mutantenzeile = [z for z in ergebnis.stdout.split("\n") if z.startswith("M46 ")]
try:
ausgabe_bericht = json.loads(bericht.read_text(encoding="utf-8"))["mutanten"][0]["tests"][0].get("ausgabe", "")
except (OSError, ValueError, KeyError, IndexError):
ausgabe_bericht = ""
ok = (
ergebnis.returncode == 1
and len(mutantenzeile) == 1
and "M46 UNKLAR" in mutantenzeile[0]
and "pytest-Abbruch mit internem Fehler (Rueckgabewert 3): " in mutantenzeile[0]
and "intern Fall 46" in mutantenzeile[0]
and "Ende der Ausgabe:" in ausgabe_bericht
)
melde("Fall 46", ok, "exit={0}\n{1}{2}".format(ergebnis.returncode, ergebnis.stdout, ergebnis.stderr))
pruefe_unveraendert(dateien, snap, "Fall 46")
finally:
shutil.rmtree(rahmen, ignore_errors=True)
FAELLE = [
```
### `mutanten_lauf_treiber.py`, Stelle 9 von 9
Aus:
```python
fall_31, fall_32, fall_33, fall_34, fall_35, fall_36, fall_37, fall_38, fall_39, fall_40,
fall_41, fall_42, fall_43, fall_44, fall_45,
]
```
wird:
```python
fall_31, fall_32, fall_33, fall_34, fall_35, fall_36, fall_37, fall_38, fall_39, fall_40,
fall_41, fall_42, fall_43, fall_44, fall_45, fall_46,
]
```
### `mutanten_lauf_probe_konfig.py`, Stelle 1 von 3
Aus:
```python
prueft Treiber-Fall 41.
```
wird:
```python
prueft Treiber-Fall 41.
Die Probe prueft ihre eigene Voraussetzung mit: Ohne -c im sauberen Rahmen
muss pytest mit 0 enden (ausser bei den Aufbauten knoten_fehl*), und wo der
Aufbau ein conftest.py hat, muss eines geladen sein. Sonst endeten beide
Laeufe gleich kaputt, und "gleich" hiesse nichts.
```
### `mutanten_lauf_probe_konfig.py`, Stelle 2 von 3
Aus:
```python
"tox_abschnitt_mit_kommentar": ({"tox.ini": "[pytest] # k\nmarkers = x\n", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
"setup_cfg_tool_pytest": ({"setup.cfg": "[tool:pytest]\n", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
```
wird:
```python
"tox_abschnitt_mit_kommentar": ({"tox.ini": "[pytest] # k\nmarkers = x\n", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
"tox_abschnitt_mit_semikolon": ({"tox.ini": "[pytest] ; k\nmarkers = x\n", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
"tox_seitenvorschub_vor_abschnitt": ({"tox.ini": "[tox]" + chr(12) + "[pytest]\n", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
"setup_cfg_tool_pytest": ({"setup.cfg": "[tool:pytest]\n", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
```
### `mutanten_lauf_probe_konfig.py`, Stelle 3 von 3
Aus:
```python
gleich_lauf = ohne_c == mit_c
if not (gleich_suche and gleich_lauf):
abweichungen += 1
print("{0:34s} suche={1} lauf={2}".format(
name, "gleich" if gleich_suche else "ABWEICHUNG", "gleich" if gleich_lauf else "ABWEICHUNG"))
if not gleich_suche:
```
wird:
```python
gleich_lauf = ohne_c == mit_c
voraussetzung = name.startswith("knoten_fehl") or ohne_c[0] == 0
if any(rel.endswith("conftest.py") for rel in dateien) and not ohne_c[2]:
voraussetzung = False
if not (gleich_suche and gleich_lauf and voraussetzung):
abweichungen += 1
print("{0:34s} suche={1} lauf={2} voraussetzung={3}".format(
name, "gleich" if gleich_suche else "ABWEICHUNG", "gleich" if gleich_lauf else "ABWEICHUNG",
"ok" if voraussetzung else "VERLETZT"))
if not gleich_suche:
```
### Erwarteter Fehlschlag vor der Umsetzung
In der Git-Bash:
```bash
# Laeufer im jetzigen Stand, neuer Treiber und neue Probe in einen leeren Rot-Ordner
R=/c/Users/PASCAL~1/AppData/Local/Temp/mlauf-rot3
rm -rf "$R" && mkdir -p "$R"
cp /d/Claude/.vault-tools/plan-kopie/mutanten_lauf.py /d/Claude/.vault-tools/plan-kopie/mutanten_lauf_treiber.py /d/Claude/.vault-tools/plan-kopie/mutanten_lauf_probe_konfig.py "$R/"
```
In der PowerShell:
```powershell
# Treiber in WSL gegen den Laeufer vor Nachbesserung 2
wsl -d Debian -- timeout 1500 /home/claude/venvs/trading/bin/python /mnt/c/Users/PASCAL~1/AppData/Local/Temp/mlauf-rot3/mutanten_lauf_treiber.py *> "C:\Users\PASCAL~1\AppData\Local\Temp\mlauf-rot3\ausgabe.txt"
"exit=$LASTEXITCODE"
Select-String -Path "C:\Users\PASCAL~1\AppData\Local\Temp\mlauf-rot3\ausgabe.txt" -Pattern "FEHLER:|gerechnete"
```
Erwartet: `exit=1`, und die Treffer enden genau auf diese vier Zeilen:
```
Fall 40 FEHLER: exit=1
Fall 43 FEHLER: exit=1
Fall 46 FEHLER: exit=1
gerechnete Fehler: 3
```
## Schritt 2 — Umsetzung
### `mutanten_lauf.py`, Stelle 1 von 7
Aus:
```python
Konfiguration in der Wurzel ist config.inipath die leere Datei, ini-Werte vom
Typ paths aus -o oder PYTEST_ADDOPTS gelten dann relativ zum Plugin-Ordner.
```
wird:
```python
Konfiguration in der Wurzel ist config.inipath die leere Datei, ini-Werte vom
Typ paths aus -o oder PYTEST_ADDOPTS gelten dann relativ zum Plugin-Ordner;
iniconfig trennt Zeilen mit splitlines, der Laeufer auch. Liegt rootdir unter
der Wurzel (Konfiguration oder setup.py in einem Unterordner der Tests),
bildet pytest die Knotennamen relativ dazu, und rot_bei passt nicht mehr: Die
Grundlinie meldet FEHLT, die Kopfzeile nennt die Datei.
```
### `mutanten_lauf.py`, Stelle 2 von 7
Aus:
```python
return True
for zeile in text.split("\n"):
zeile = zeile.rstrip()
```
wird:
```python
return True
for zeile in text.splitlines():
zeile = zeile.rstrip()
```
### `mutanten_lauf.py`, Stelle 3 von 7
Aus:
```python
return pfad.exists()
except OSError:
return False
```
wird:
```python
return pfad.exists()
except (OSError, ValueError):
return False
```
### `mutanten_lauf.py`, Stelle 4 von 7
Aus:
```python
"""Die Zeile, die die Ursache nennt: bei 4 die erste mit "error" ausser der
Gebrauchszeile von argparse, bei 3 und ohne Treffer die letzte."""
if rueckgabe == 4:
```
wird:
```python
"""Die Zeile, die die Ursache nennt: bei 4 die erste mit "error" ausser der
Gebrauchszeile von argparse, bei 3 die letzte mit INTERNALERROR> (danach
folgt noch die Summenzeile, Treiber-Fall 46), ohne Treffer die letzte."""
if rueckgabe == 4:
```
### `mutanten_lauf.py`, Stelle 5 von 7
Aus:
```python
return zeile.strip()
return ende[-1].strip() if ende else "(keine Ausgabe)"
```
wird:
```python
return zeile.strip()
if rueckgabe == 3:
interne = [zeile for zeile in ende if zeile.startswith("INTERNALERROR>")]
if interne:
return interne[-1].strip()
return ende[-1].strip() if ende else "(keine Ausgabe)"
```
### `mutanten_lauf.py`, Stelle 6 von 7
Aus:
```python
ergebnis, meldung = ergebnis_bestimmen(knoten, reports, deselected)
if ergebnis == "FEHLT":
```
wird:
```python
ergebnis, meldung = ergebnis_bestimmen(knoten, reports, deselected)
abbruch = None
if ergebnis == "FEHLT":
```
### `mutanten_lauf.py`, Stelle 7 von 7
Aus:
```python
luecke = True
test_ergebnisse.append({"eintrag": eintrag, "knoten": knoten, "ergebnis": ergebnis, "meldung": meldung})
```
wird:
```python
luecke = True
eintrag_bericht = {"eintrag": eintrag, "knoten": knoten, "ergebnis": ergebnis, "meldung": meldung}
if ergebnis == "FEHLT" and abbruch:
eintrag_bericht["ausgabe"] = abbruch["lang"]
test_ergebnisse.append(eintrag_bericht)
```
### `mutanten_lauf_ENTWURF.md`, Stelle 1 von 3
Aus:
```markdown
- Die Kopfzeilen `pytest-konfiguration (Grundlinie, <python>): …` nennen je Interpreter, was für seinen Aufruf in der Grundlinie gilt.
- Gegen pytest selbst geprüft wird das mit `mutanten_lauf_probe_konfig.py` (Treiber-Fall 43): 24 Aufbauten, je `pytest_konfig_bestimmen` gegen `determine_setup` und ein echtes `pytest --co` ohne `-c` gegen einen Lauf mit den Argumenten des Läufers unter fremder Konfiguration.
- Benannte Lücken: Eine `tox.ini` oder ein `setup.cfg`, die iniconfig nicht lesen kann, übergeht der Läufer, pytest bricht dort mit 4 ab; eine nicht lesbare `pyproject.toml` oder `pytest.toml` gilt dagegen als Treffer, damit pytest den Fehler selbst meldet. Ein `setup.cfg` mit `[pytest]`, bei pytest ein Fehler, gilt hier als keine Konfiguration. Ohne Konfiguration in der Wurzel ist `config.inipath` die leere Datei; ini-Werte vom Typ `paths`, die über `-o` oder `PYTEST_ADDOPTS` kommen, gelten dann relativ zum Plugin-Ordner.
**Abbruch von pytest (seit 2026-10-06):** Endet ein pytest-Aufruf mit Rückgabewert 3 (`INTERNAL_ERROR`) oder 4 (`USAGE_ERROR`, auch bei einem Testknoten, den es nicht gibt), hält der Läufer die letzten 20 nicht leeren Zeilen von stdout und stderr fest. In der Grundlinie wird daraus je Interpreter ein Befund `Grundlinie: pytest-Bedienfehler (Rueckgabewert 4) mit <python>; Ende der Ausgabe: …` (bei 3: `pytest-Abbruch mit internem Fehler`) statt einer `FEHLT`-Zeile je Test; der Bericht führt die Tests mit `FEHLT` und der Meldung „pytest abgebrochen, siehe Befunde“. Unter einem Mutanten tragen die Tests ohne Bericht und ohne Sammelfehler eine kurze Fassung als Meldung, die Ursache vorn, damit sie in die 120 Zeichen der Mutantenzeile passt: `pytest-Bedienfehler (Rueckgabewert 4): <zeile>`. Die Zeile ist bei 4 die erste mit „error“ außer der Gebrauchszeile `ERROR: usage:` von argparse, bei 3 die letzte. Diese Tests machen den Mutanten wie bisher `UNKLAR`. Ein Sammelfehler bleibt `ROT`, auch wenn pytest dabei mit 4 endet: Ein Knoten in einer Datei, die sich nicht sammeln lässt, ergibt zusätzlich `found no collectors for …` (Treiber-Fall 11, am 2026-10-06 an einer ersten Fassung gesehen, die jeden Abbruch als `UNKLAR` zählte).
```
wird:
```markdown
- Die Kopfzeilen `pytest-konfiguration (Grundlinie, <python>): …` nennen je Interpreter, was für seinen Aufruf in der Grundlinie gilt.
- Gegen pytest selbst geprüft wird das mit `mutanten_lauf_probe_konfig.py` (Treiber-Fall 43): 26 Aufbauten, je `pytest_konfig_bestimmen` gegen `determine_setup` und ein echtes `pytest --co` ohne `-c` gegen einen Lauf mit den Argumenten des Läufers unter fremder Konfiguration. Die Probe sichert ihre Voraussetzung mit: Ohne `-c` endet pytest mit 0 (außer bei fehlenden Knoten), und wo der Aufbau ein `conftest.py` hat, ist eines geladen.
- Benannte Lücken: Eine `tox.ini` oder ein `setup.cfg`, die iniconfig nicht lesen kann, übergeht der Läufer, pytest bricht dort mit 4 ab; eine nicht lesbare `pyproject.toml` oder `pytest.toml` gilt dagegen als Treffer, damit pytest den Fehler selbst meldet. Ein `setup.cfg` mit `[pytest]`, bei pytest ein Fehler, gilt hier als keine Konfiguration. Ohne Konfiguration in der Wurzel ist `config.inipath` die leere Datei; ini-Werte vom Typ `paths`, die über `-o` oder `PYTEST_ADDOPTS` kommen, gelten dann relativ zum Plugin-Ordner. Liegt `rootdir` unter der Wurzel (Konfiguration oder `setup.py` in einem Unterordner der Tests), bildet pytest die Knotennamen relativ dazu, und `rot_bei` passt nicht mehr: Die Grundlinie meldet `FEHLT`, die Kopfzeile nennt die gefundene Datei; heute betrifft das kein Projekt im Vault. Zeilen trennt der Läufer wie iniconfig mit `splitlines`, also auch an Seitenvorschub und Unicode-Zeilentrennern.
**Abbruch von pytest (seit 2026-10-06):** Endet ein pytest-Aufruf mit Rückgabewert 3 (`INTERNAL_ERROR`) oder 4 (`USAGE_ERROR`, auch bei einem Testknoten, den es nicht gibt), hält der Läufer die letzten 20 nicht leeren Zeilen von stdout und stderr fest. In der Grundlinie wird daraus je Interpreter ein Befund `Grundlinie: pytest-Bedienfehler (Rueckgabewert 4) mit <python>; Ende der Ausgabe: …` (bei 3: `pytest-Abbruch mit internem Fehler`) statt einer `FEHLT`-Zeile je Test; der Bericht führt die Tests mit `FEHLT` und der Meldung „pytest abgebrochen, siehe Befunde“. Unter einem Mutanten tragen die Tests ohne Bericht und ohne Sammelfehler eine kurze Fassung als Meldung, die Ursache vorn, damit sie in die 120 Zeichen der Mutantenzeile passt: `pytest-Bedienfehler (Rueckgabewert 4): <zeile>`. Die Zeile ist bei 4 die erste mit „error“ außer der Gebrauchszeile `ERROR: usage:` von argparse, bei 3 die letzte mit `INTERNALERROR>` (danach steht noch die Summenzeile `no tests ran …`, Treiber-Fall 46), ohne Treffer die letzte überhaupt. Die lange Fassung steht im Bericht beim Test unter `ausgabe`. Diese Tests machen den Mutanten wie bisher `UNKLAR`. Ein Sammelfehler bleibt `ROT`, auch wenn pytest dabei mit 4 endet: Ein Knoten in einer Datei, die sich nicht sammeln lässt, ergibt zusätzlich `found no collectors for …` (Treiber-Fall 11, am 2026-10-06 an einer ersten Fassung gesehen, die jeden Abbruch als `UNKLAR` zählte).
```
### `mutanten_lauf_ENTWURF.md`, Stelle 2 von 3
Aus:
```markdown
`--bericht` schreibt dasselbe als JSON: `{"wurzel", "mutanten_datei", "befunde": [...], "grundlinie": {knoten: {ergebnis, meldung}}, "mutanten": [{"id", "datei", "status", "hinweis", "treffer", "tests": [{"eintrag", "knoten", "ergebnis", "meldung"}]}], "zusammenfassung": {...}}`. Der Bericht wird auch bei einem Abbruch geschrieben, mit allem, was bis dahin feststand.
```
wird:
```markdown
`--bericht` schreibt dasselbe als JSON: `{"wurzel", "mutanten_datei", "befunde": [...], "grundlinie": {knoten: {ergebnis, meldung}}, "mutanten": [{"id", "datei", "status", "hinweis", "treffer", "tests": [{"eintrag", "knoten", "ergebnis", "meldung", "ausgabe" (nur bei einem Abbruch von pytest)}]}], "zusammenfassung": {...}}`. Der Bericht wird auch bei einem Abbruch geschrieben, mit allem, was bis dahin feststand.
```
### `mutanten_lauf_ENTWURF.md`, Stelle 3 von 3
Aus:
```markdown
| 39 | Wurzel ohne eigene Konfiguration, darüber ein `conftest.py`, das beim Laden abbricht | Exit 0, zweimal `OK`; prüft `--confcutdir` |
| 40 | ein Mutant schreibt in die eigene `pyproject.toml` `addopts = "--gibt-es-nicht"` | Exit 1; die Mutantenzeile selbst trägt `UNKLAR`, `pytest-Bedienfehler (Rueckgabewert 4)` und `--gibt-es-nicht` |
| 41 | Wurzel über einen Symlink angegeben (`link/k` → `echt/k`), seit der Nachbesserung vom 2026-10-06 | Exit 0, zweimal `OK` |
| 42 | ein `conftest.py` der Wurzel wirft in `pytest_collection_modifyitems` | Exit 2, `pytest-Abbruch mit internem Fehler (Rueckgabewert 3)` und der Text der Ausnahme, keine Zeile `ist FEHLT` |
| 43 | `mutanten_lauf_probe_konfig.py`: 24 Aufbauten gegen `determine_setup` und gegen echte `pytest --co`-Läufe | Exit 0, `abweichungen: 0` |
| 44 | zwei Interpretergruppen (`python_je_datei`), nur in einer ein Knoten, den es nicht gibt; diese Gruppe einmal an erster, einmal an letzter Stelle | je Exit 2, genau ein Befund mit dem Interpreter dieser Gruppe, keiner für die andere, keine Zeile `ist FEHLT` |
| 45 | ein Knoten mit einem Dateinamen über 255 Zeichen | Exit 2 mit einem Befund `Grundlinie: pytest-…`, kein `unerwarteter Abbruch` |
```
wird:
```markdown
| 39 | Wurzel ohne eigene Konfiguration, darüber ein `conftest.py`, das beim Laden abbricht | Exit 0, zweimal `OK`; prüft `--confcutdir` |
| 40 | ein Mutant schreibt in die eigene `pyproject.toml` `addopts = "--gibt-es-nicht"` | Exit 1; die Mutantenzeile selbst trägt `UNKLAR`, `pytest-Bedienfehler (Rueckgabewert 4)` und `--gibt-es-nicht`; im Bericht steht beim Test `ausgabe` mit „Ende der Ausgabe:“ |
| 41 | Wurzel über einen Symlink angegeben (`link/k` → `echt/k`), seit der Nachbesserung vom 2026-10-06 | Exit 0, zweimal `OK` |
| 42 | ein `conftest.py` der Wurzel wirft in `pytest_collection_modifyitems` | Exit 2, `pytest-Abbruch mit internem Fehler (Rueckgabewert 3)` und der Text der Ausnahme, keine Zeile `ist FEHLT` |
| 43 | `mutanten_lauf_probe_konfig.py`: 26 Aufbauten gegen `determine_setup` und gegen echte `pytest --co`-Läufe, mit Selbstprüfung der Voraussetzung | Exit 0, `abweichungen: 0` |
| 44 | zwei Interpretergruppen (`python_je_datei`), nur in einer ein Knoten, den es nicht gibt; diese Gruppe einmal an erster, einmal an letzter Stelle | je Exit 2, genau ein Befund mit dem Interpreter dieser Gruppe, keiner für die andere, keine Zeile `ist FEHLT`; genau zwei Kopfzeilen, je eine je Interpreter |
| 45 | ein Knoten mit einem Dateinamen über 255 Zeichen | Exit 2 mit einem Befund `Grundlinie: pytest-…`, kein `unerwarteter Abbruch` |
| 46 | ein Mutant lässt ein `conftest.py` der Wurzel in `pytest_collection_modifyitems` werfen | Exit 1, die Mutantenzeile trägt `UNKLAR`, `pytest-Abbruch mit internem Fehler (Rueckgabewert 3): ` und den Text der Ausnahme; im Bericht `ausgabe` mit „Ende der Ausgabe:“ |
```
### `LIESMICH.md`, Stelle 1 von 2
Aus:
```markdown
8. **Mutanten fahren (seit 2026-09-27):** `mutanten_lauf.py --wurzel <wegwerfkopie> --mutanten <planordner>/mutanten.toml [--nur ID,…] [--nur-muster] [--bericht <datei.json>] [--zeitlimit <s>]`, 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 <mutanten_lauf.py> <arbeitsordner>` mit dem Python eines Venvs, in dem pytest liegt (24 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) und gibt die Ausgabe an die Linse `arithmetik`. Fehlt `unshare` ohne Netz, bricht das Skript vorher mit `MUTANTEN_LAUF_FEHLER` ab, statt ungesperrt 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_<n>`) 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.
```
wird:
```markdown
8. **Mutanten fahren (seit 2026-09-27):** `mutanten_lauf.py --wurzel <wegwerfkopie> --mutanten <planordner>/mutanten.toml [--nur ID,…] [--nur-muster] [--bericht <datei.json>] [--zeitlimit <s>]`, 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 <mutanten_lauf.py> <arbeitsordner>` 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) und gibt die Ausgabe an die Linse `arithmetik`. Fehlt `unshare` ohne Netz, bricht das Skript vorher mit `MUTANTEN_LAUF_FEHLER` ab, statt ungesperrt 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_<n>`) 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.
```
### `LIESMICH.md`, Stelle 2 von 2
Aus:
```markdown
- Gegenprobe an allen sechs `mutanten.toml` im Vault, alter gegen neuer Läufer auf Kopien aus `git archive HEAD` unter `/tmp`: Headscale A, Mattermost, After Install C1 und D-40 gleich (175, 130, 69 und 41 Mutanten `OK`); Headscale B und Trading Plattform WH enden bei beiden mit Exit 2, weil ihr Stand nicht zum Plan passt. Unter dem Windows-Temp endete C1 mit dem alten Läufer mit 101 × `FEHLT`, mit dem neuen mit 69 `OK`.
- Ein `pruefer` fand in der ersten Fassung den Symlink-Fall und die fehlenden Fälle; beides ist in der Nachbesserung behoben.
- Rohdaten: `AI/secondbrain/linux-haertung-after-install-memory/2026-10-06-c1-abschluss/roh/` (`02-*`, `03-gegenprobe/`, `07-*`).
```
wird:
```markdown
- Gegenprobe an allen sechs `mutanten.toml` im Vault, alter gegen neuer Läufer auf Kopien aus `git archive HEAD` unter `/tmp`: Headscale A, Mattermost, After Install C1 und D-40 gleich (175, 130, 69 und 41 Mutanten `OK`); Headscale B und Trading Plattform WH enden bei beiden mit Exit 2, weil ihr Stand nicht zum Plan passt. Unter dem Windows-Temp endete C1 mit dem alten Läufer mit 101 × `FEHLT`, mit dem neuen mit 69 `OK`.
- Ein `pruefer` fand in der ersten Fassung den Symlink-Fall und fehlende Fälle; die Nachbesserung behob beides. Ein zweiter `pruefer` fand danach keinen Verhaltensfehler mehr, nur eine Aufräumliste (Kopfzeile je Interpreter ohne eigenen Fall, Kurzfassung bei Rückgabewert 3, `;` und Seitenvorschub in `tox.ini`, Selbstprüfung der Probe, lange Fassung im Bericht, zwei Lücken benennen); sie ist in Nachbesserung 2 erledigt, Treiber 46 von 46.
- Rohdaten: `AI/secondbrain/linux-haertung-after-install-memory/2026-10-06-c1-abschluss/roh/` (`02-*`, `03-gegenprobe/`, `07-*`, `08-gegenprobe2/` mit dem nachgebesserten Läufer, `09-*` für Nachbesserung 2).
```
## Schritt 3 — Verifikation
```bash
# Kompilieren, ASCII, keine CR, Cache wieder weg
cd /d/Claude/.vault-tools/plan-kopie
python -m py_compile mutanten_lauf.py mutanten_lauf_treiber.py mutanten_lauf_probe_konfig.py && echo kompiliert
rm -rf __pycache__
LC_ALL=C grep -cP '[^\x00-\x7F]' mutanten_lauf.py mutanten_lauf_treiber.py mutanten_lauf_probe_konfig.py
for f in mutanten_lauf.py mutanten_lauf_treiber.py mutanten_lauf_probe_konfig.py mutanten_lauf_ENTWURF.md LIESMICH.md; do printf '%s %s\n' "$f" "$(tr -cd '\r' < "$f" | wc -c)"; done
```
Erwartet: `kompiliert`, überall `0`.
```powershell
# Treiber gegen den Laeufer nach Nachbesserung 2
wsl -d Debian -- timeout 1500 /home/claude/venvs/trading/bin/python /mnt/d/Claude/.vault-tools/plan-kopie/mutanten_lauf_treiber.py *> "C:\Users\PASCAL~1\AppData\Local\Temp\mlauf-rot3\ausgabe-neu.txt"
"exit=$LASTEXITCODE"
Select-String -Path "C:\Users\PASCAL~1\AppData\Local\Temp\mlauf-rot3\ausgabe-neu.txt" -Pattern "FEHLER:|gerechnete"
```
Erwartet: `exit=0` und als einziger Treffer eine Zeile, die auf `gerechnete Fehler: 0` endet. Danach den Rot-Ordner und ein von WSL angelegtes `__pycache__` unter `plan-kopie/` löschen.
## Bericht
Je Zieldatei die Zahl der angewendeten Paare, die Ausgaben aus Schritt 1 und 3 im Wortlaut, jede Abweichung und Annahme.
## Fertig heißt (Koordinator)
1. Die fünf Zieldateien byte-gleich mit der Kopie mit angewendetem Plan (`cmp`).
2. Treiber 46 von 46, selbst gefahren.
3. Rot-Nachweis wie unter „Gemessen“.
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,693 @@
---
type: plan
title: "Mutantenlauf auf der Kopie: Nachbesserung 1 (Fälle für die Fehlerzweige der lo-Prüfung)"
status: active
summary: "Delta zum Plan vom 2026-10-09: Treiber-Fälle W10 bis W13 (Probe scheitert an ip, lo bleibt aus, ip scheitert erst im Hauptaufruf, setpriv wirft nichts ab), Varianten v6 bis v9 als Regressionsnachweis, Kopfkommentar des Skripts berichtigt, Satz zur Reihenfolge vor KEINE_MUTANTEN_TOML in LIESMICH und Entwurf, Datensatz rb3d-kopie im Plan."
tags: [plan, vault-tools, pruefwerkzeuge]
created: 2026-10-09
updated: 2026-10-09
modell: haiku
modell_begruendung: "Jeder Block steht wörtlich da, Zieldateien, Verbote und Haltepunkte sind genannt; kein Codeblock trägt typografische Anführungszeichen, Striche oder Auslassungspunkte."
---
Übersicht: [LIESMICH der Planwerkzeuge](../../LIESMICH.md) · Vorgänger: [Plan lo im Netz-Namensraum](2026-10-09-mutanten-kopie-loopback.md)
# Mutantenlauf auf der Kopie: Nachbesserung 1
## Ziel
Der Treiber `mutanten_lauf_kopie_treiber.py` sieht danach auch die Fehlerzweige der lo-Prüfung: eine gescheiterte Probe (W10, W11), die Fähigkeitsprüfung der Probe (W13) und den Zweig nach `||` im Hauptaufruf (W12). Jeder dieser Fälle ist an einer kaputten Variante rot gesehen. Kopfkommentar, LIESMICH und Entwurf sagen genau, was die Vorprüfung prüft und dass sie vor `KEINE_MUTANTEN_TOML` steht. Der Plan nennt für den Lauf mit echten Daten den richtigen Datensatz.
## Nicht im Umfang
- Das Verhalten von `mutanten_lauf_kopie.sh` bleibt gleich. Im Skript ändert sich nur der Kopfkommentar; Schritt 2 prüft das.
- Die Reihenfolge der Vorprüfung vor `KEINE_MUTANTEN_TOML` bleibt (Befund 3), sie wird nur beschrieben.
- Kein neuer Lauf gegen das Skript aus HEAD, kein neuer Lauf der Varianten v1 bis v5, kein neuer Lauf mit echten Daten (siehe Getroffene Annahmen).
- `mutanten_lauf.py`, `mutanten_lauf_treiber.py`, `.claude/workflows/*` und alles unter `D:/Claude/.arbeitsbaeume/` bleiben unberührt.
## Warum so
Der `pruefer` fand am 2026-10-09 keinen blockierenden Befund. Treiber W1 bis W9 grün, die 73 Mutanten von RB3 durch das neue Skript alle `OK`, T1 und T12 im Namensraum grün. Rohdaten: `D:/Claude/AI/secondbrain/trading-plattform-memory/2026-10-09-mkopie-roh/pruefer/` (`angriffe.py`, `angriffe.out`, `varianten_pruefer.py`).
| Befund | Hier |
|---|---|
| 1 (wichtig): kein Fall für `exit 2` nach der Probe und keiner für den Zweig nach `\|\|` im Hauptaufruf; v6 lief ganz grün | W10 (A1), W11 (A3), W12 (A4), dazu W13 für die Fähigkeitsprüfung der Probe ohne Variante; Varianten v6 und v7 des Prüfers, dazu v8 und v9 |
| 2 (klein): Kopfkommentar Z. 42 bis 43 zu weit | Kopfkommentar trennt Probe und Hauptaufruf, nennt CapEff und CapAmb und nur die Probe |
| 3 (klein): Vorprüfung vor `KEINE_MUTANTEN_TOML` | bleibt; Satz in LIESMICH, Entwurf und Kopfkommentar |
| 4 (klein): Datensatz `rb3d-kopie` statt `rb3b-kopie` | drei Ersetzungen im Plan, Abschnitt „Lauf mit echten Daten“ |
### Bauweise der neuen Fälle
Aus `angriffe.py` des Prüfers übernommen: Der `PATH` des Skripts ist ein Ordner mit Verweisen auf alle Werkzeuge aus `WERKZEUGE`. Einzelne Werkzeuge sind dort durch kleine Skripte mit `#!/bin/sh` ersetzt. W4, W5 und W9 lassen ein Werkzeug weg, W10 bis W13 ersetzen eines.
| Fall | Ersatz | Erwartung | Gemessen oder gerechnet |
|---|---|---|---|
| W10 | `ip`: Meldung auf stderr, Exit 1 | Exit 2, `MUTANTEN_LAUF_FEHLER: lo im Netz-Namensraum nicht nutzbar`, Text der Attrappe in stdout, keine Modus-Zeile, keine `EXIT`-Zeile, keine Kopie | gemessen als A1 |
| W11 | `ip`: Exit 0 ohne Wirkung | dasselbe, mit `Errno 101` in stdout | gemessen als A3 |
| W12 | `ip`: zählt seine Aufrufe in einer Datei, ab dem zweiten Exit 1, sonst `exec` des echten `ip` | Exit 2, `modus: voll`, `MUTANTEN_LAUF_FEHLER: lo im Netz-Namensraum nicht hochgefahren`, `EXIT mutanten_lauf: 2`, kein `MW7`, Text der Attrappe in stderr, genau zwei Aufrufe, keine Kopie | gemessen als A4, ohne die Zählung |
| W13 | `setpriv`: überspringt alles bis `--` und startet den Rest per `exec`, ohne etwas abzuwerfen | wie W10, mit `Faehigkeiten nicht abgeworfen` in stdout | gerechnet |
W13 rechnet so: `unshare --keep-caps` hebt die Fähigkeiten in die ambiente Menge. Sonst käme `ip` nach dem `exec` der `sh` nicht an `CAP_NET_ADMIN`. Ohne `setpriv` behält `python3` sie deshalb, wie bei Variante v1 des Plans (W3 rot, gemessen).
### Varianten
| Variante | Änderung | rot erwartet | gemessen oder gerechnet |
|---|---|---|---|
| `v6_probe_ohne_exit` | Zeile `exit 2` nach der Meldung der Probe gestrichen (v6 des Prüfers) | W10, W11, W13 | W1 bis W9 grün gemessen (Prüfer), W10 rot gemessen (A6), W11 und W13 gerechnet |
| `v7_probe_ohne_setpriv` | Probe ohne `setpriv` (v7 des Prüfers) | W1 bis W9, W12 | gerechnet |
| `v8_haupt_ohne_oder` | Hauptaufruf ohne den Zweig nach `\|\|` | W12 | gerechnet |
| `v9_probe_ohne_faehigkeiten` | Probe ohne Fähigkeitsprüfung (`if False:`) | W13 | gerechnet |
Damit ist jeder neue Fall einmal rot: W10 und W11 an v6, W12 an v7 und v8, W13 an v6 und v9.
## Zieldateien
Pfade relativ zu `D:/Claude/.vault-tools/plan-kopie/`:
- `mutanten_lauf_kopie_treiber.py` (geändert)
- `mutanten_lauf_kopie.sh` (geändert)
- `LIESMICH.md` (geändert)
- `mutanten_lauf_ENTWURF.md` (geändert)
- `docs/plans/2026-10-09-mutanten-kopie-loopback.md` (geändert)
## Verbote
- Nur die fünf Zieldateien ändern. Kein `git add`, kein `git commit`.
- Hilfsdateien nur im Rot-Ordner `C:\Users\PASCAL~1\AppData\Local\Temp\mkopie-rot-n1` (Git-Bash: `/c/Users/PASCAL~1/AppData/Local/Temp/mkopie-rot-n1`, WSL: `/mnt/c/Users/PASCAL~1/AppData/Local/Temp/mkopie-rot-n1`).
- Jeden Block byte-genau übernehmen: keine Umformulierung, keine Ersetzung von Zeichen, keine zusätzlichen Leerzeilen. Aus-Block als `old_string`, wird-Block als `new_string` im Edit-Werkzeug; die Variantendatei mit dem Write-Werkzeug.
- Kein `sed`, kein Suchen-und-Ersetzen über ganze Dateien.
- WSL-Aufrufe nur aus der PowerShell.
- Kein Netz außer `127.0.0.1`.
- Den Treiber nicht fahren, während eine Plankritik läuft.
- Steht ein Aus-Block nicht genau einmal in seiner Datei oder weicht eine Pflicht-Ausgabe ab: anhalten und melden. Den Treiber nie anpassen, damit ein Fall grün wird.
- `python -m py_compile` legt `__pycache__` an; den Ordner danach löschen.
## Schritt 0 — Rot-Ordner und Ausgangsstand
In der Git-Bash:
```bash
# Rot-Ordner neu anlegen, Skript im jetzigen Stand als Vergleichsdatei hineinlegen
R=/c/Users/PASCAL~1/AppData/Local/Temp/mkopie-rot-n1
rm -rf "$R" && mkdir -p "$R"
cp /d/Claude/.vault-tools/plan-kopie/mutanten_lauf_kopie.sh "$R/skript_vor_n1.sh"
# Ausgangsstand: Plan umgesetzt, nicht committet
git -C /d/Claude status --porcelain -- .vault-tools
```
Erwartet: genau diese sechs Zeilen, in beliebiger Reihenfolge. Sonst anhalten und melden.
```text
M .vault-tools/plan-kopie/LIESMICH.md
M .vault-tools/plan-kopie/mutanten_lauf_ENTWURF.md
M .vault-tools/plan-kopie/mutanten_lauf_kopie.sh
?? .vault-tools/plan-kopie/docs/plans/2026-10-09-mutanten-kopie-loopback-nachbesserung-1.md
?? .vault-tools/plan-kopie/docs/plans/2026-10-09-mutanten-kopie-loopback.md
?? .vault-tools/plan-kopie/mutanten_lauf_kopie_treiber.py
```
## Schritt 1 — Tests zuerst: Treiber
### `mutanten_lauf_kopie_treiber.py`, Stelle 1 von 7
Aus:
```python
#!/usr/bin/env python3
"""Treiber fuer mutanten_lauf_kopie.sh: Faelle W1 bis W9 (lo im Netz-Namensraum).
```
wird:
```python
#!/usr/bin/env python3
"""Treiber fuer mutanten_lauf_kopie.sh: Faelle W1 bis W13 (lo im Netz-Namensraum).
```
### `mutanten_lauf_kopie_treiber.py`, Stelle 2 von 7
Aus:
```python
Nicht im Treiber: ob plankritik.js das Skript so aufruft wie hier
(MSYS_NO_PATHCONV, cygpath -m -l).
"""
```
wird:
```python
Faelle W10 bis W13 (Nachbesserung 1, Plan
docs/plans/2026-10-09-mutanten-kopie-loopback-nachbesserung-1.md) ersetzen im
Attrappen-Ordner einzelne Werkzeuge durch kleine sh-Skripte, nach den
Angriffen A1, A3 und A4 des Pruefers vom 2026-10-09: ip scheitert in der
Probe (W10), ip meldet Erfolg und lo bleibt aus (W11), ip scheitert erst im
Hauptaufruf (W12), setpriv wirft keine Faehigkeit ab (W13).
Nicht im Treiber: ob plankritik.js das Skript so aufruft wie hier
(MSYS_NO_PATHCONV, cygpath -m -l); ob die Probe bei Exit 0 mit falscher
Ausgabe scheitert.
"""
```
### `mutanten_lauf_kopie_treiber.py`, Stelle 3 von 7
Aus:
```python
import os
import shutil
```
wird:
```python
import os
import shlex
import shutil
```
### `mutanten_lauf_kopie_treiber.py`, Stelle 4 von 7
Aus:
```python
# Was mutanten_lauf_kopie.sh ueber PATH aufruft (Stand 2026-10-09); daraus
# bauen W4, W5 und W9 ihre Attrappen-Ordner.
```
wird:
```python
# Was mutanten_lauf_kopie.sh ueber PATH aufruft (Stand 2026-10-09); daraus
# bauen W4, W5, W9 und W10 bis W13 ihre Attrappen-Ordner.
```
### `mutanten_lauf_kopie_treiber.py`, Stelle 5 von 7
Aus:
```python
def fall_w9(skript):
fahre_ohne_werkzeug("W9", skript, "python3")
def prozesse_mit_marke(marke):
```
wird:
```python
def fall_w9(skript):
fahre_ohne_werkzeug("W9", skript, "python3")
def baue_attrappen_mit_ersatz(ordner, ersatz):
# Wie baue_attrappen, aber jedes Werkzeug aus ersatz ist ein kleines
# sh-Skript statt eines Verweises auf das echte Werkzeug (Bauweise aus den
# Angriffen des Pruefers vom 2026-10-09).
ordner.mkdir()
for name in WERKZEUGE:
pfad = ordner / name
if name in ersatz:
pfad.write_text("#!/bin/sh\n" + ersatz[name] + "\n", encoding="utf-8")
pfad.chmod(0o755)
else:
os.symlink(shutil.which(name), pfad)
def fahre_probe_scheitert(name, skript, ersatz, merkmal):
# Die Vorpruefung muss scheitern: Exit 2 mit der Meldung der Probe und dem
# Merkmal des Angriffs, ohne Modus-Zeile, ohne EXIT-Zeile, ohne Kopie.
fall = fall_einfach()
try:
ordner = fall.basis / "attrappen"
baue_attrappen_mit_ersatz(ordner, ersatz)
vorher = plankritik_ordner()
ergebnis = starte_skript(skript, fall, pfad=str(ordner))
neu = sorted(str(p) for p in plankritik_ordner() - vorher)
ok = (
ergebnis.returncode == 2
and "MUTANTEN_LAUF_FEHLER: lo im Netz-Namensraum nicht nutzbar" in ergebnis.stdout
and merkmal in ergebnis.stdout
and "modus:" not in ergebnis.stdout
and "EXIT mutanten_lauf" not in ergebnis.stdout
and not neu
)
melde(name, ok, "neue Kopien={0}, {1}".format(neu, kurz(ergebnis)))
fall.pruefe_kopie(name)
finally:
fall.aufraeumen()
def fall_w10(skript):
# ip scheitert schon in der Probe (Angriff A1 des Pruefers).
fahre_probe_scheitert("W10", skript, {"ip": "echo ip-attrappe scheitert >&2; exit 1"}, "ip-attrappe scheitert")
def fall_w11(skript):
# ip meldet Erfolg, lo bleibt aus (A3): Die Verbindung der Probe scheitert
# mit Errno 101.
fahre_probe_scheitert("W11", skript, {"ip": "exit 0"}, "Errno 101")
def fall_w12(skript):
# ip klappt in der Probe und scheitert erst im Hauptaufruf (A4); eine
# Zaehlerdatei haelt fest, der wievielte Aufruf es ist. Erwartet:
# Modus-Zeile, Meldung des Hauptaufrufs, EXIT-Zeile mit 2, kein Laeufer,
# genau zwei Aufrufe von ip, keine Kopie unter /tmp.
fall = fall_einfach()
try:
zaehler = fall.basis / "ip_zaehler"
z = shlex.quote(str(zaehler))
ip_echt = shlex.quote(shutil.which("ip"))
ersatz = {"ip": (
"n=0\n"
"if [ -f " + z + " ]; then read n < " + z + "; fi\n"
"n=$((n + 1))\n"
"echo \"$n\" > " + z + "\n"
"if [ \"$n\" -ge 2 ]; then echo \"ip-attrappe zweiter Aufruf\" >&2; exit 1; fi\n"
"exec " + ip_echt + " \"$@\""
)}
ordner = fall.basis / "attrappen"
baue_attrappen_mit_ersatz(ordner, ersatz)
vorher = plankritik_ordner()
ergebnis = starte_skript(skript, fall, pfad=str(ordner))
neu = sorted(str(p) for p in plankritik_ordner() - vorher)
try:
aufrufe = zaehler.read_text(encoding="ascii").strip()
except OSError:
aufrufe = "keine"
ok = (
ergebnis.returncode == 2
and "modus: voll" in ergebnis.stdout
and "MUTANTEN_LAUF_FEHLER: lo im Netz-Namensraum nicht hochgefahren" in ergebnis.stdout
and "EXIT mutanten_lauf: 2" in ergebnis.stdout
and "nicht nutzbar" not in ergebnis.stdout
and "MW7" not in ergebnis.stdout
and "ip-attrappe zweiter Aufruf" in ergebnis.stderr
and aufrufe == "2"
and not neu
)
melde("W12", ok, "ip-aufrufe={0}, neue Kopien={1}, {2}".format(aufrufe, neu, kurz(ergebnis)))
fall.pruefe_kopie("W12")
finally:
fall.aufraeumen()
SETPRIV_OHNE_WIRKUNG = (
"while [ \"$#\" -gt 0 ] && [ \"$1\" != \"--\" ]; do shift; done\n"
"shift\n"
"exec \"$@\""
)
def fall_w13(skript):
# setpriv wirft keine Faehigkeit ab und startet nur den Befehl hinter --:
# Die Faehigkeitspruefung der Probe muss anschlagen.
fahre_probe_scheitert("W13", skript, {"setpriv": SETPRIV_OHNE_WIRKUNG}, "Faehigkeiten nicht abgeworfen")
def prozesse_mit_marke(marke):
```
### `mutanten_lauf_kopie_treiber.py`, Stelle 6 von 7
Aus:
```python
FAELLE = [
("W7", fall_w7),
("W1", fall_w1),
("W2", fall_w2),
("W3", fall_w3),
("W4", fall_w4),
("W5", fall_w5),
("W6", fall_w6),
("W8", fall_w8),
("W9", fall_w9),
]
```
wird:
```python
FAELLE = [
("W7", fall_w7),
("W1", fall_w1),
("W2", fall_w2),
("W3", fall_w3),
("W4", fall_w4),
("W5", fall_w5),
("W6", fall_w6),
("W8", fall_w8),
("W9", fall_w9),
("W10", fall_w10),
("W11", fall_w11),
("W12", fall_w12),
("W13", fall_w13),
]
```
### `mutanten_lauf_kopie_treiber.py`, Stelle 7 von 7
Aus:
```python
zerleger = argparse.ArgumentParser(description="Treiber fuer mutanten_lauf_kopie.sh (Faelle W1 bis W9)")
```
wird:
```python
zerleger = argparse.ArgumentParser(description="Treiber fuer mutanten_lauf_kopie.sh (Faelle W1 bis W13)")
```
Dann in der Git-Bash prüfen:
```bash
# Treiber kompilieren, nur ASCII, keine CR, vier neue Faelle; __pycache__ danach weg
cd /d/Claude/.vault-tools/plan-kopie
python -m py_compile mutanten_lauf_kopie_treiber.py && echo kompiliert
rm -rf __pycache__
LC_ALL=C grep -cP '[^\x00-\x7F]' mutanten_lauf_kopie_treiber.py
tr -cd '\r' < mutanten_lauf_kopie_treiber.py | wc -c
grep -c '^def fall_w1[0-3](skript):$' mutanten_lauf_kopie_treiber.py
```
Erwartet: `kompiliert`, dann `0`, `0`, `4`.
### Kein Rot vor der Umsetzung
Diese Nachbesserung ändert das Verhalten des Skripts nicht. Die neuen Fälle sind deshalb schon gegen den jetzigen Stand grün; W10 bis W12 hat der Prüfer als A1, A3 und A4 gemessen. Ihr Rot zeigen die Varianten in Schritt 4.
## Schritt 2 — Umsetzung: Kopfkommentar des Skripts
### `./mutanten_lauf_kopie.sh`, Stelle 1 von 1
Aus:
```bash
# - Vorpruefung vor dem Kopieren: Fehlt ip, setpriv oder python3, kommt lo nicht hoch oder
# bleiben Faehigkeiten, endet das Skript mit MUTANTEN_LAUF_FEHLER und Exit 2. Ein Lauf ohne
# Netzsperre oder ohne lo ist nie die Ausweichloesung.
# - Treiber: mutanten_lauf_kopie_treiber.py, Faelle W1 bis W9.
set -u
```
wird:
```bash
# - Vorpruefung vor dem Kopieren: Fehlt ip, setpriv oder python3, kommt lo in einer Probe im
# eigenen Namensraum nicht hoch, gelingt dort keine Verbindung ueber 127.0.0.1 oder hat die
# Probe nach setpriv noch CapEff oder CapAmb ungleich 0, endet das Skript mit
# MUTANTEN_LAUF_FEHLER und Exit 2, ohne EXIT-Zeile. Die Faehigkeiten prueft das Skript nur an
# dieser Probe und nur CapEff und CapAmb, nicht am Laeufer selbst; alle vier Mengen am Laeufer
# prueft Treiber-Fall W3. Die Vorpruefung steht vor der Suche nach mutanten.toml: Scheitert
# sie, endet das Skript auch fuer einen Planordner ohne mutanten.toml mit Exit 2 statt mit
# KEINE_MUTANTEN_TOML.
# - Hauptaufruf: Kommt lo dort nicht hoch, endet das Skript nach der Modus-Zeile mit
# MUTANTEN_LAUF_FEHLER, Exit 2 und EXIT-Zeile, ohne den Laeufer zu starten. Ein Lauf ohne
# Netzsperre oder ohne lo ist nie die Ausweichloesung.
# - Treiber: mutanten_lauf_kopie_treiber.py, Faelle W1 bis W13 (W10 bis W13 seit Nachbesserung 1,
# docs/plans/2026-10-09-mutanten-kopie-loopback-nachbesserung-1.md).
set -u
```
Dann in der Git-Bash:
```bash
# nur ASCII, keine CR, und ausser Kommentarzeilen nichts geaendert
cd /d/Claude/.vault-tools/plan-kopie
R=/c/Users/PASCAL~1/AppData/Local/Temp/mkopie-rot-n1
LC_ALL=C grep -cP '[^\x00-\x7F]' mutanten_lauf_kopie.sh
tr -cd '\r' < mutanten_lauf_kopie.sh | wc -c
diff <(grep -v '^#' "$R/skript_vor_n1.sh") <(grep -v '^#' mutanten_lauf_kopie.sh) && echo nur-kommentar
```
Erwartet: `0`, `0`, `nur-kommentar`.
In der PowerShell:
```powershell
# Syntax des Skripts in WSL
wsl -d Debian -- bash -n /mnt/d/Claude/.vault-tools/plan-kopie/mutanten_lauf_kopie.sh
"exit=$LASTEXITCODE"
```
Erwartet: nur `exit=0`.
## Schritt 3 — Verifikation: Treiber gegen das Skript
```powershell
# Treiber in WSL gegen das Skript nach Nachbesserung 1
wsl -d Debian -- timeout 900 /home/claude/venvs/trading/bin/python /mnt/d/Claude/.vault-tools/plan-kopie/mutanten_lauf_kopie_treiber.py *> "C:\Users\PASCAL~1\AppData\Local\Temp\mkopie-rot-n1\ausgabe-neu.txt"
"exit=$LASTEXITCODE"
Select-String -Path "C:\Users\PASCAL~1\AppData\Local\Temp\mkopie-rot-n1\ausgabe-neu.txt" -Pattern "^W\d.*FEHLER:|^gerechnete|^TREIBER_FEHLER|^rundreise"
Select-String -Path "C:\Users\PASCAL~1\AppData\Local\Temp\mkopie-rot-n1\ausgabe-neu.txt" -Pattern "^W1[0-3] ok"
```
Erwartet:
- `exit=0`.
- Aus dem ersten `Select-String` genau zwei Treffer: einer mit `rundreise: /tmp/mkopie_treiber_rundreise_`, einer endet auf `gerechnete Fehler: 0`.
- Aus dem zweiten genau vier Treffer, sie enden auf `W10 ok`, `W11 ok`, `W12 ok`, `W13 ok`.
Sonst anhalten und die ganze Datei `ausgabe-neu.txt` melden. Gerechnet, nicht gemessen ist nur W13; steht dort `W13 FEHLER: neue Kopien=[], exit=0`, hat die Probe ohne `setpriv` keine Fähigkeiten gesehen. Das ist ein Befund für den Koordinator, kein Grund, den Treiber anzupassen.
## Schritt 4 — Regressionsnachweis: vier Varianten
### Varianten bauen
Leg mit dem Write-Werkzeug die Datei `C:\Users\PASCAL~1\AppData\Local\Temp\mkopie-rot-n1\varianten_n1.py` an.
Inhalt:
```python
# Baut vier kaputte Fassungen von mutanten_lauf_kopie.sh fuer Nachbesserung 1 (v6 bis v9).
# Aufruf: python varianten_n1.py <skript> <zielordner>
import sys
from pathlib import Path
quelle = Path(sys.argv[1])
ziel = Path(sys.argv[2])
text = quelle.read_bytes().decode("utf-8")
if "\r" in text:
raise SystemExit("Skript hat CR")
def ersetze(text, aus, wird, anzahl):
gefunden = text.count(aus)
if gefunden != anzahl:
raise SystemExit("{0!r}: {1} Treffer statt {2}".format(aus, gefunden, anzahl))
return text.replace(aus, wird)
varianten = {}
# v6: Probe meldet, das Skript laeuft weiter; Zeile exit 2 nach der Meldung gestrichen (erwartet rot: W10, W11, W13)
zeilen = text.split("\n")
treffer = [i for i, z in enumerate(zeilen)
if z.lstrip().startswith("echo \"MUTANTEN_LAUF_FEHLER: lo im Netz-Namensraum nicht nutzbar")]
if len(treffer) != 1 or zeilen[treffer[0] + 1].strip() != "exit 2":
raise SystemExit("Meldung der Probe mit exit 2 nicht eindeutig gefunden")
k = treffer[0]
varianten["v6_probe_ohne_exit"] = "\n".join(zeilen[:k + 1] + zeilen[k + 2:])
# v7: Probe ohne setpriv (erwartet rot: W1 bis W9, W12)
varianten["v7_probe_ohne_setpriv"] = ersetze(
text, "exec setpriv --inh-caps=-all --ambient-caps=-all -- python3 -c ", "exec python3 -c ", 1)
# v8: Hauptaufruf ohne den Zweig nach || (erwartet rot: W12)
varianten["v8_haupt_ohne_oder"] = ersetze(
text, " || { echo \"MUTANTEN_LAUF_FEHLER: lo im Netz-Namensraum nicht hochgefahren\"; exit 2; }", "", 1)
# v9: Probe ohne Faehigkeitspruefung (erwartet rot: W13)
varianten["v9_probe_ohne_faehigkeiten"] = ersetze(
text, "if int(felder[\"CapEff\"], 16) != 0 or int(felder[\"CapAmb\"], 16) != 0:", "if False:", 1)
for name, inhalt in varianten.items():
if inhalt == text:
raise SystemExit(name + " unveraendert")
(ziel / (name + ".sh")).write_bytes(inhalt.encode("utf-8"))
print(name)
```
In der Git-Bash:
```bash
# Varianten aus dem Skript nach Nachbesserung 1 bauen
R=/c/Users/PASCAL~1/AppData/Local/Temp/mkopie-rot-n1
python "$R/varianten_n1.py" /d/Claude/.vault-tools/plan-kopie/mutanten_lauf_kopie.sh "$R"
```
Erwartet genau diese vier Zeilen:
```text
v6_probe_ohne_exit
v7_probe_ohne_setpriv
v8_haupt_ohne_oder
v9_probe_ohne_faehigkeiten
```
Endet das Skript mit einer Meldung statt der vier Zeilen: anhalten und melden.
### Treiber gegen jede Variante
```powershell
# Treiber in WSL gegen jede kaputte Variante
foreach ($v in "v6_probe_ohne_exit","v7_probe_ohne_setpriv","v8_haupt_ohne_oder","v9_probe_ohne_faehigkeiten") {
wsl -d Debian -- timeout 900 /home/claude/venvs/trading/bin/python /mnt/d/Claude/.vault-tools/plan-kopie/mutanten_lauf_kopie_treiber.py --skript "/mnt/c/Users/PASCAL~1/AppData/Local/Temp/mkopie-rot-n1/$v.sh" *> "C:\Users\PASCAL~1\AppData\Local\Temp\mkopie-rot-n1\ausgabe-$v.txt"
"$v exit=$LASTEXITCODE"
Select-String -Path "C:\Users\PASCAL~1\AppData\Local\Temp\mkopie-rot-n1\ausgabe-$v.txt" -Pattern "^W\d.*FEHLER:|^gerechnete|^TREIBER_FEHLER"
}
```
Erwartet je Variante `exit=1`, und die `FEHLER`-Zeilen betreffen genau diese Fälle. Zeilen wie `W12 (kopie) FEHLER:` oder `W12 (aufraeumen) FEHLER:` zählen zu ihrem Fall:
| Variante | rote Fälle | gerechnet: Zeilen der neuen Fälle | was es belegt |
|---|---|---|---|
| `v6_probe_ohne_exit` | W10, W11, W13 | `W10 FEHLER: neue Kopien=[], exit=2`, `W11 FEHLER: neue Kopien=[], exit=0`, `W13 FEHLER: neue Kopien=[], exit=0` | W10, W11 und W13 sehen ein Skript, das nach gescheiterter Probe weiterläuft |
| `v7_probe_ohne_setpriv` | W1 bis W9, W12 | `W12 FEHLER: ip-aufrufe=1, neue Kopien=[], exit=2` | die Fähigkeitsprüfung der Probe schlägt an; mit Fähigkeiten läuft kein Mutant |
| `v8_haupt_ohne_oder` | W12 | `W12 FEHLER: ip-aufrufe=2, neue Kopien=[], exit=0` | W12 sieht den fehlenden Zweig nach `\|\|` |
| `v9_probe_ohne_faehigkeiten` | W13 | `W13 FEHLER: neue Kopien=[], exit=0` | W13 sieht eine Probe ohne Fähigkeitsprüfung |
- Pflicht ist die Menge der roten Fälle je Variante. Weicht sie ab: anhalten und die ganze Ausgabedatei der Variante melden. Nicht weiter zu Schritt 5.
- Gerechnet, nicht gemessen: die Zeilen in der dritten Spalte und die Zahl hinter `gerechnete Fehler:` (3, 10, 1, 1). Weicht nur ein solcher Wert ab, meldest du ihn und machst weiter.
## Schritt 5 — Doku und Plan
### `LIESMICH.md`, Stelle 1 von 1
Aus:
```markdown
- `mutanten_lauf_kopie_treiber.py` ruft das Skript wie die Plankritik mit Windows-Pfaden auf und fährt neun Fälle: Server auf `127.0.0.1` (W1), keine Route nach außen und `ENETUNREACH` (W2), uid wie außen und keine Fähigkeiten (W3), `ip`, `setpriv` oder `python3` fehlen (W4, W5, W9), Zeitgrenze mit `timeout` als Elternprozess des Läufers und ohne Prozessreste (W6), Grundlinie ohne Server in beiden Modi (W7), Dienst des Rechners unerreichbar (W8). Neuer Stand ohne Fehler.
- Gegen das Skript vor der Änderung rot: W1, W2, W4, W5, W9. Gegen kaputte Varianten genau an ihrem Fall: ohne `setpriv` W3, `setpriv` ohne `exec` W6, ohne Vorprüfung W4, W5 und W9, ohne `--keep-caps` alle (die Vorprüfung meldet `Operation not permitted`), ohne Namensraum W2 und W8.
- Nicht abgeworfen und nicht geprüft: die Begrenzungsmenge (`CapBnd`) und `no_new_privs`. `plankritik.js` führt eine Vorprüfung mit `MUTANTEN_LAUF_FEHLER` wie bisher bei fehlendem `unshare` als `ohne_ergebnis`, ohne den Text der Meldung.
```
wird:
```markdown
- `mutanten_lauf_kopie_treiber.py` ruft das Skript wie die Plankritik mit Windows-Pfaden auf und fährt die Fälle W1 bis W13: Server auf `127.0.0.1` (W1), keine Route nach außen und `ENETUNREACH` (W2), uid wie außen und keine Fähigkeiten (W3), `ip`, `setpriv` oder `python3` fehlen (W4, W5, W9), Zeitgrenze mit `timeout` als Elternprozess des Läufers und ohne Prozessreste (W6), Grundlinie ohne Server in beiden Modi (W7), Dienst des Rechners unerreichbar (W8). Seit Nachbesserung 1 auch: `ip` scheitert in der Probe (W10), `ip` meldet Erfolg und `lo` bleibt aus (W11), `ip` scheitert erst im Hauptaufruf nach der Modus-Zeile (W12), `setpriv` wirft keine Fähigkeit ab (W13). W10 bis W13 ersetzen dazu einzelne Werkzeuge durch kleine `sh`-Skripte. Neuer Stand ohne Fehler.
- Gegen das Skript vor der Änderung rot: W1, W2, W4, W5, W9. Gegen kaputte Varianten genau an ihrem Fall, gefahren mit W1 bis W9: ohne `setpriv` W3, `setpriv` ohne `exec` W6, ohne Vorprüfung W4, W5 und W9, ohne `--keep-caps` alle (die Vorprüfung meldet `Operation not permitted`), ohne Namensraum W2 und W8. Gefahren mit W1 bis W13 (Nachbesserung 1, `docs/plans/2026-10-09-mutanten-kopie-loopback-nachbesserung-1.md`): ohne `exit 2` nach der Probe W10, W11 und W13; Probe ohne `setpriv` W1 bis W9 und W12; Hauptaufruf ohne den Zweig nach `||` W12; Probe ohne Fähigkeitsprüfung W13.
- Nicht abgeworfen und nicht geprüft: die Begrenzungsmenge (`CapBnd`) und `no_new_privs`. `plankritik.js` führt eine Vorprüfung mit `MUTANTEN_LAUF_FEHLER` wie bisher bei fehlendem `unshare` als `ohne_ergebnis`, ohne den Text der Meldung.
- Die Fähigkeiten prüft das Skript nur an der Probe und nur `CapEff` und `CapAmb`, nicht am Läufer selbst. Alle vier Mengen am Läufer prüft Treiber-Fall W3.
- Die Vorprüfung steht vor der Suche nach `mutanten.toml`. Fehlt auf einem Rechner `ip`, `setpriv` oder `python3` oder kommt `lo` nicht hoch, endet das Skript deshalb auch für einen Planordner ohne `mutanten.toml` mit `MUTANTEN_LAUF_FEHLER` und Exit 2 statt mit `KEINE_MUTANTEN_TOML`. `plankritik.js` meldet dann `ohne_ergebnis` statt `keine_toml`, und die Linse erfährt nicht, dass die `mutanten.toml` fehlt. Das bleibt so.
- Lauf mit echten Daten: die 73 Mutanten des Pakets RB3 (Kopie `rb3d-kopie`) durch das neue Skript, alle `OK`, T1 und T12 im Namensraum.
- Ein `pruefer` fand keinen blockierenden Befund. Seine Lücken (kein Fall für den Fehlerzweig der Probe und den Zweig nach `||` im Hauptaufruf, Kopfkommentar zu weit gefasst, der Satz zur Reihenfolge oben) behebt Nachbesserung 1. Rohdaten: `AI/secondbrain/trading-plattform-memory/2026-10-09-mkopie-roh/`.
```
### `mutanten_lauf_ENTWURF.md`, Stelle 1 von 1
Aus:
```markdown
Treiber: `mutanten_lauf_kopie_treiber.py` (W1 bis W9), Plan `docs/plans/2026-10-09-mutanten-kopie-loopback.md`.
```
wird:
```markdown
Treiber: `mutanten_lauf_kopie_treiber.py` (W1 bis W13), Plan `docs/plans/2026-10-09-mutanten-kopie-loopback.md` und seine Nachbesserung 1. Die Fähigkeiten prüft der Wrapper nur an der Probe (`CapEff`, `CapAmb`), nicht am Läufer. Weil die Vorprüfung vor der Suche nach `mutanten.toml` steht, endet er ohne `ip`, `setpriv`, `python3` oder nutzbares `lo` auch ohne `mutanten.toml` mit Exit 2 statt mit `KEINE_MUTANTEN_TOML`.
```
### `docs/plans/2026-10-09-mutanten-kopie-loopback.md`, Stelle 1 von 3
Aus:
```markdown
- **Datensatz:** Paket RB3 der Trading Plattform, Kopie `D:/Claude/.arbeitsbaeume/rb3b-kopie`, Planordner `D:/Claude/.arbeitsbaeume/trading-plattform-rb3/docs/plans/2026-10-08-paket-rb3-totmann-ping` (dieselben wie in `rb3_mutanten.sh`). Fehlt die Kopie, vorher neu bauen.
```
wird:
```markdown
- **Datensatz:** Paket RB3 der Trading Plattform, Kopie `D:/Claude/.arbeitsbaeume/rb3d-kopie`, Planordner `D:/Claude/.arbeitsbaeume/trading-plattform-rb3/docs/plans/2026-10-08-paket-rb3-totmann-ping` (dieselben wie in `rb3d_mutanten.sh`). Die Mutanten der `mutanten.toml` passen nur zum Stand in `rb3d-kopie`, nicht zu `rb3b-kopie` aus `rb3_mutanten.sh` (berichtigt in Nachbesserung 1). Fehlt die Kopie, vorher neu bauen.
```
### `docs/plans/2026-10-09-mutanten-kopie-loopback.md`, Stelle 2 von 3
Aus:
```powershell
wsl -d Debian -- env BUDGET_S=3000 ZEITLIMIT_S=300 timeout 3300 bash /mnt/d/Claude/.vault-tools/plan-kopie/mutanten_lauf_kopie.sh "D:/Claude/.arbeitsbaeume/rb3b-kopie" "D:/Claude/.arbeitsbaeume/trading-plattform-rb3/docs/plans/2026-10-08-paket-rb3-totmann-ping" voll *> "C:\Users\PASCAL~1\AppData\Local\Temp\mkopie-rot\rb3.txt"
```
wird:
```powershell
wsl -d Debian -- env BUDGET_S=3000 ZEITLIMIT_S=300 timeout 3300 bash /mnt/d/Claude/.vault-tools/plan-kopie/mutanten_lauf_kopie.sh "D:/Claude/.arbeitsbaeume/rb3d-kopie" "D:/Claude/.arbeitsbaeume/trading-plattform-rb3/docs/plans/2026-10-08-paket-rb3-totmann-ping" voll *> "C:\Users\PASCAL~1\AppData\Local\Temp\mkopie-rot\rb3.txt"
```
### `docs/plans/2026-10-09-mutanten-kopie-loopback.md`, Stelle 3 von 3
Aus:
```markdown
Die übrigen Mutanten haben denselben Status wie in Teil A von `rb3_mutanten.sh`.
```
wird:
```markdown
Die übrigen Mutanten haben denselben Status wie in Teil A von `rb3d_mutanten.sh`.
```
## Schritt 6 — Abschlussprüfung
In der Git-Bash:
```bash
# keine CR in den fuenf Zieldateien, nichts ausserhalb der Zieldateien geaendert
cd /d/Claude/.vault-tools/plan-kopie
for f in mutanten_lauf_kopie_treiber.py mutanten_lauf_kopie.sh LIESMICH.md mutanten_lauf_ENTWURF.md docs/plans/2026-10-09-mutanten-kopie-loopback.md; do printf '%s %s\n' "$f" "$(tr -cd '\r' < "$f" | wc -c)"; done
ls -d __pycache__ 2>/dev/null || echo kein-pycache
git -C /d/Claude status --porcelain -- .vault-tools
```
Erwartet: je Datei die Zahl `0`, dann `kein-pycache`, dann dieselben sechs Zeilen wie in Schritt 0.
Den Rot-Ordner lässt du stehen; der Koordinator legt die Ausgabedateien ab und löscht ihn.
## Bericht
Melde:
- je Zieldatei die Zahl der angewendeten Aus/wird-Paare (Treiber 7, Skript 1, LIESMICH 1, Entwurf 1, Plan 3);
- die Ausgaben aus Schritt 0, 1, 2, 3 (alle `Select-String`-Treffer im Wortlaut), 4 (Variantenzeilen und alle `Select-String`-Treffer im Wortlaut) und 6;
- jede Abweichung vom Plan, jeden gerechneten Wert, der anders ausfiel, und jede getroffene Annahme. „Keine Abweichungen“ nur, wenn es keine gab.
## Fertig heißt (Koordinator)
1. Die fünf Zieldateien sind byte-gleich mit der Kopie, auf die diese Nachbesserung per `plan_materialisiere.py` angewendet wurde (`cmp`); sonst ist unter `.vault-tools/` nichts geändert.
2. Treiber in WSL gegen das Skript nach Nachbesserung 1, selbst gefahren: Exit 0, `gerechnete Fehler: 0`, `W10 ok` bis `W13 ok` stehen da.
3. Die vier Varianten selbst gesehen: rot genau an ihren Fällen laut Tabelle in Schritt 4. Die Ausgabedateien aus dem Rot-Ordner liegen als Rohdaten neben `pruefer/` unter `AI/secondbrain/trading-plattform-memory/2026-10-09-mkopie-roh/`.
4. Schritt 2 hat `nur-kommentar` gezeigt: Das Verhalten des Skripts ist unverändert, der Lauf mit echten Daten vom 2026-10-09 gilt weiter.
## Lauf mit echten Daten
Entfällt. Das Skript ändert sich nur im Kommentar, Schritt 2 belegt das mit `grep -v '^#'`. Es gilt der Lauf des Prüfers vom 2026-10-09: 73 Mutanten von RB3 aus `rb3d-kopie`, alle `OK`, T1 und T12 im Namensraum.
## Risiken
1. **W13 auf einem Rechner ohne ambiente Fähigkeiten.** Hebt `unshare --keep-caps` die Fähigkeiten nicht in die ambiente Menge, sieht die Probe ohne `setpriv` keine, und W13 ist schon gegen das neue Skript rot. Merkmal: `W13 FEHLER: neue Kopien=[], exit=0` in Schritt 3. Dann wäre auch v7 grün statt rot; Befund für den Koordinator.
2. **Zählerdatei in W12.** Die Attrappe schreibt im Namensraum in den Fallordner des Treibers. Gelingt das nicht, zählt `ip` nicht weiter. Merkmal: `ip-aufrufe=` ungleich `2` auf dem neuen Skript.
3. **Gleichzeitige Plankritik.** Legt eine Plankritik während des Treiberlaufs `/tmp/plankritik-mut-*` an, meldet der Treiber `(aufraeumen)` für den gerade laufenden Fall.
4. **Laufzeit.** Fünf Treiberläufe; v7 wartet in W6 bis zum Ende der Schleife (rund sechs Sekunden). Zusammen gerechnet unter einer Viertelstunde, gemessen nicht.
## Getroffene Annahmen
- W13 ließ sich ohne Variante bauen: eine `setpriv`-Attrappe, die nichts abwirft. v7 des Prüfers bleibt trotzdem im Nachweis, wie beauftragt. Weil v7 W13 nicht rot macht (die Probe scheitert dort ebenfalls), kommt v9 dazu: Probe ohne Fähigkeitsprüfung, rot nur an W13.
- Für den Zweig nach `||` im Hauptaufruf kommt Variante v8 dazu. Der Auftrag nannte nur v6 und v7; v6 lässt W12 grün, v7 macht W12 nur über die Probe rot.
- W10 bis W13 fahren keinen Kontrolllauf wie W4, W5 und W9. Jeder prüft stattdessen ein Merkmal, das nur seine Attrappe oder die Probe erzeugt (Text der Attrappe, `Errno 101`, Zählerstand, `Faehigkeiten nicht abgeworfen`). Dass ein Ordner nur aus Verweisen ein grünes Skript ergibt, zeigt der Kontrolllauf in W4.
- „Keine Reste“ in W12 heißt: kein neuer Ordner `/tmp/plankritik-mut-*` und die übergebene Kopie byte-gleich. Prozessreste prüft W6.
- Das echte `ip` holt W12 über `shutil.which("ip")` des Treibers statt fest über `/usr/bin/ip` wie der Prüfer.
- Kein neuer Lauf gegen das Skript aus HEAD und gegen v1 bis v5: Sie belegen W1 bis W9, und an diesen Fällen ändert sich nichts. LIESMICH sagt deshalb, mit welchen Fällen welche Varianten gefahren sind.
- Im Plan ändert sich neben dem Datensatz auch der Verweis auf das Skript mit Teil A (`rb3d_mutanten.sh` statt `rb3_mutanten.sh`), weil `rb3_mutanten.sh` `rb3b-kopie` benutzt. Die Verweise auf `rb3_mutanten.sh` im Anlass des Plans bleiben, sie beschreiben die Geschichte.
- Der Satz zu Befund 3 steht in LIESMICH, wie beauftragt, und zusätzlich knapp im Kopfkommentar und im Entwurf, weil beide die Vorprüfung beschreiben.
- `modell: haiku`: Alles steht wörtlich da, kein Codeblock trägt typografische Zeichen.
## Offene Punkte
- **Gerechnet, nicht gemessen:** W13 grün gegen das neue Skript; die roten Mengen von v7, v8 und v9; bei v6 W11 und W13 rot; alle Zeilen hinter `FEHLER:` und die Zahlen hinter `gerechnete Fehler:` in Schritt 4. Gemessen sind W10 bis W12 als A1, A3 und A4, v6 grün an W1 bis W9 und rot an W10 (A6).
- **Ausgabevergleich der Probe ohne Fall.** Endet die Probe mit 0, aber mit anderer Ausgabe als `lo-probe ok`, greift der zweite Teil der Bedingung. Dafür gibt es weder Fall noch Variante; im Docstring des Treibers als Lücke benannt.
- **`plankritik.js` verliert die Meldung** weiter (offener Punkt des Plans). Mit Befund 3 trifft das jetzt auch den Hinweis auf eine fehlende `mutanten.toml`.
- **Weitere Prüfrunde:** Die Nachbesserung ändert nur Treiber und Text. Ob ein `pruefer` sie noch einmal liest oder Schritt 3 und 4 genügen, entscheidest du.
File diff suppressed because it is too large. Load diff