.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]>
635 lines
34 KiB
Markdown
635 lines
34 KiB
Markdown
---
|
||
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“.
|